diff options
Diffstat (limited to 'drivers')
630 files changed, 24942 insertions, 7914 deletions
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index e88fe3632dd6..0972ec0e2eb8 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -418,7 +418,7 @@ static int video_set_report_key_events(const struct dmi_system_id *id) return 0; } -static struct dmi_system_id video_dmi_table[] = { +static const struct dmi_system_id video_dmi_table[] = { /* * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 */ diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 037fd537bbf6..995c4d8922b1 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -30,7 +30,7 @@ #include "internal.h" -static struct dmi_system_id acpi_rev_dmi_table[] __initdata; +static const struct dmi_system_id acpi_rev_dmi_table[] __initconst; /* * POLICY: If *anything* doesn't work, put it on the blacklist. @@ -89,7 +89,7 @@ static int __init dmi_enable_rev_override(const struct dmi_system_id *d) } #endif -static struct dmi_system_id acpi_rev_dmi_table[] __initdata = { +static const struct dmi_system_id acpi_rev_dmi_table[] __initconst = { #ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE /* * DELL XPS 13 (2015) switches sound between HDA and I2S diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 59f2f96fdb7e..4d0979e02a28 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -67,7 +67,7 @@ static int set_copy_dsdt(const struct dmi_system_id *id) } #endif -static struct dmi_system_id dsdt_dmi_table[] __initdata = { +static const struct dmi_system_id dsdt_dmi_table[] __initconst = { /* * Invoke DSDT corruption work-around on all Toshiba Satellite. * https://bugzilla.kernel.org/show_bug.cgi?id=14679 @@ -83,7 +83,7 @@ static struct dmi_system_id dsdt_dmi_table[] __initdata = { {} }; #else -static struct dmi_system_id dsdt_dmi_table[] __initdata = { +static const struct dmi_system_id dsdt_dmi_table[] __initconst = { {} }; #endif diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index fdfae6f3c0b1..236b14324780 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1809,7 +1809,7 @@ static int ec_honor_ecdt_gpe(const struct dmi_system_id *id) return 0; } -static struct dmi_system_id ec_dmi_table[] __initdata = { +static const struct dmi_system_id ec_dmi_table[] __initconst = { { ec_correct_ecdt, "MSI MS-171F", { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"), diff --git a/drivers/acpi/nfit/Kconfig b/drivers/acpi/nfit/Kconfig index 6d3351452ea2..929ba4da0b30 100644 --- a/drivers/acpi/nfit/Kconfig +++ b/drivers/acpi/nfit/Kconfig @@ -2,7 +2,7 @@ config ACPI_NFIT tristate "ACPI NVDIMM Firmware Interface Table (NFIT)" depends on PHYS_ADDR_T_64BIT depends on BLK_DEV - depends on ARCH_HAS_MMIO_FLUSH + depends on ARCH_HAS_PMEM_API select LIBNVDIMM help Infrastructure to probe ACPI 6 compliant platforms for diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 1893e416e7c0..9c2c49b6a240 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -228,6 +228,10 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, if (cmd == ND_CMD_CALL) { call_pkg = buf; func = call_pkg->nd_command; + + for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++) + if (call_pkg->nd_reserved2[i]) + return -EINVAL; } if (nvdimm) { @@ -1674,8 +1678,19 @@ static ssize_t range_index_show(struct device *dev, } static DEVICE_ATTR_RO(range_index); +static ssize_t ecc_unit_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nd_region *nd_region = to_nd_region(dev); + struct nfit_spa *nfit_spa = nd_region_provider_data(nd_region); + + return sprintf(buf, "%d\n", nfit_spa->clear_err_unit); +} +static DEVICE_ATTR_RO(ecc_unit_size); + static struct attribute *acpi_nfit_region_attributes[] = { &dev_attr_range_index.attr, + &dev_attr_ecc_unit_size.attr, NULL, }; @@ -1804,6 +1819,7 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc, struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); struct acpi_nfit_memory_map *memdev = memdev_from_spa(acpi_desc, spa->range_index, i); + struct acpi_nfit_control_region *dcr = nfit_mem->dcr; if (!memdev || !nfit_mem->dcr) { dev_err(dev, "%s: failed to find DCR\n", __func__); @@ -1811,13 +1827,13 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc, } map->region_offset = memdev->region_offset; - map->serial_number = nfit_mem->dcr->serial_number; + map->serial_number = dcr->serial_number; map2->region_offset = memdev->region_offset; - map2->serial_number = nfit_mem->dcr->serial_number; - map2->vendor_id = nfit_mem->dcr->vendor_id; - map2->manufacturing_date = nfit_mem->dcr->manufacturing_date; - map2->manufacturing_location = nfit_mem->dcr->manufacturing_location; + map2->serial_number = dcr->serial_number; + map2->vendor_id = dcr->vendor_id; + map2->manufacturing_date = dcr->manufacturing_date; + map2->manufacturing_location = dcr->manufacturing_location; } /* v1.1 namespaces */ @@ -1835,6 +1851,28 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc, cmp_map_compat, NULL); nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); + /* record the result of the sort for the mapping position */ + for (i = 0; i < nr; i++) { + struct nfit_set_info_map2 *map2 = &info2->mapping[i]; + int j; + + for (j = 0; j < nr; j++) { + struct nd_mapping_desc *mapping = &ndr_desc->mapping[j]; + struct nvdimm *nvdimm = mapping->nvdimm; + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct acpi_nfit_control_region *dcr = nfit_mem->dcr; + + if (map2->serial_number == dcr->serial_number && + map2->vendor_id == dcr->vendor_id && + map2->manufacturing_date == dcr->manufacturing_date && + map2->manufacturing_location + == dcr->manufacturing_location) { + mapping->position = i; + break; + } + } + } + ndr_desc->nd_set = nd_set; devm_kfree(dev, info); devm_kfree(dev, info2); @@ -1930,7 +1968,7 @@ static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk, memcpy_flushcache(mmio->addr.aperture + offset, iobuf + copied, c); else { if (nfit_blk->dimm_flags & NFIT_BLK_READ_FLUSH) - mmio_flush_range((void __force *) + arch_invalidate_pmem((void __force *) mmio->addr.aperture + offset, c); memcpy(iobuf + copied, mmio->addr.aperture + offset, c); diff --git a/drivers/acpi/osi.c b/drivers/acpi/osi.c index 19cdd8a783a9..76998a51bf99 100644 --- a/drivers/acpi/osi.c +++ b/drivers/acpi/osi.c @@ -312,7 +312,7 @@ static int __init dmi_disable_osi_win8(const struct dmi_system_id *d) * Note that _OSI("Linux")/_OSI("Darwin") determined here can be overridden * by acpi_osi=!Linux/acpi_osi=!Darwin command line options. */ -static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { +static const struct dmi_system_id acpi_osi_dmi_table[] __initconst = { { .callback = dmi_disable_osi_vista, .ident = "Fujitsu Siemens", diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index f62c68e24317..e90b61f7d2db 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -174,7 +174,7 @@ static int do_sta_before_sun(const struct dmi_system_id *d) return 0; } -static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = { +static const struct dmi_system_id acpi_pci_slot_dmi_table[] __initconst = { /* * Fujitsu Primequest machines will return 1023 to indicate an * error if the _SUN method is evaluated on SxFy objects that diff --git a/drivers/acpi/processor_pdc.c b/drivers/acpi/processor_pdc.c index 7cfbda4d7c51..74f738cb6073 100644 --- a/drivers/acpi/processor_pdc.c +++ b/drivers/acpi/processor_pdc.c @@ -173,7 +173,7 @@ static int __init set_no_mwait(const struct dmi_system_id *id) return 0; } -static struct dmi_system_id processor_idle_dmi_table[] __initdata = { +static const struct dmi_system_id processor_idle_dmi_table[] __initconst = { { set_no_mwait, "Extensa 5220", { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"), diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 9fdd014759f8..6804ddab3052 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -160,7 +160,7 @@ static int __init init_nvs_nosave(const struct dmi_system_id *d) return 0; } -static struct dmi_system_id acpisleep_dmi_table[] __initdata = { +static const struct dmi_system_id acpisleep_dmi_table[] __initconst = { { .callback = init_old_suspend_ordering, .ident = "Abit KN9 (nForce4 variant)", diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 1d0417b87cb7..551b71a24b85 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -1209,7 +1209,7 @@ static int thermal_psv(const struct dmi_system_id *d) { return 0; } -static struct dmi_system_id thermal_dmi_table[] __initdata = { +static const struct dmi_system_id thermal_dmi_table[] __initconst = { /* * Award BIOS on this AOpen makes thermal control almost worthless. * http://bugzilla.kernel.org/show_bug.cgi?id=8842 diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 2c3b359b3536..321cd7b4d817 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -256,9 +256,9 @@ static ssize_t print_cpus_offline(struct device *dev, buf[n++] = ','; if (nr_cpu_ids == total_cpus-1) - n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids); + n += snprintf(&buf[n], len - n, "%u", nr_cpu_ids); else - n += snprintf(&buf[n], len - n, "%d-%d", + n += snprintf(&buf[n], len - n, "%u-%d", nr_cpu_ids, total_cpus-1); } diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index 1c152aed6b82..a39b2166b145 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -37,7 +37,7 @@ static inline dma_addr_t dma_get_device_base(struct device *dev, return mem->device_base; } -static bool dma_init_coherent_memory( +static int dma_init_coherent_memory( phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags, struct dma_coherent_mem **mem) { @@ -45,25 +45,28 @@ static bool dma_init_coherent_memory( void __iomem *mem_base = NULL; int pages = size >> PAGE_SHIFT; int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); + int ret; - if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) - goto out; - if (!size) + if (!size) { + ret = -EINVAL; goto out; + } - if (flags & DMA_MEMORY_MAP) - mem_base = memremap(phys_addr, size, MEMREMAP_WC); - else - mem_base = ioremap(phys_addr, size); - if (!mem_base) + mem_base = memremap(phys_addr, size, MEMREMAP_WC); + if (!mem_base) { + ret = -EINVAL; goto out; - + } dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); - if (!dma_mem) + if (!dma_mem) { + ret = -ENOMEM; goto out; + } dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!dma_mem->bitmap) + if (!dma_mem->bitmap) { + ret = -ENOMEM; goto out; + } dma_mem->virt_base = mem_base; dma_mem->device_base = device_addr; @@ -73,17 +76,13 @@ static bool dma_init_coherent_memory( spin_lock_init(&dma_mem->spinlock); *mem = dma_mem; - return true; + return 0; out: kfree(dma_mem); - if (mem_base) { - if (flags & DMA_MEMORY_MAP) - memunmap(mem_base); - else - iounmap(mem_base); - } - return false; + if (mem_base) + memunmap(mem_base); + return ret; } static void dma_release_coherent_memory(struct dma_coherent_mem *mem) @@ -91,10 +90,7 @@ static void dma_release_coherent_memory(struct dma_coherent_mem *mem) if (!mem) return; - if (mem->flags & DMA_MEMORY_MAP) - memunmap(mem->virt_base); - else - iounmap(mem->virt_base); + memunmap(mem->virt_base); kfree(mem->bitmap); kfree(mem); } @@ -109,8 +105,6 @@ static int dma_assign_coherent_memory(struct device *dev, return -EBUSY; dev->dma_mem = mem; - /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ - return 0; } @@ -118,16 +112,16 @@ int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags) { struct dma_coherent_mem *mem; + int ret; - if (!dma_init_coherent_memory(phys_addr, device_addr, size, flags, - &mem)) - return 0; - - if (dma_assign_coherent_memory(dev, mem) == 0) - return flags & DMA_MEMORY_MAP ? DMA_MEMORY_MAP : DMA_MEMORY_IO; + ret = dma_init_coherent_memory(phys_addr, device_addr, size, flags, &mem); + if (ret) + return ret; - dma_release_coherent_memory(mem); - return 0; + ret = dma_assign_coherent_memory(dev, mem); + if (ret) + dma_release_coherent_memory(mem); + return ret; } EXPORT_SYMBOL(dma_declare_coherent_memory); @@ -171,7 +165,6 @@ static void *__dma_alloc_from_coherent(struct dma_coherent_mem *mem, int order = get_order(size); unsigned long flags; int pageno; - int dma_memory_map; void *ret; spin_lock_irqsave(&mem->spinlock, flags); @@ -188,15 +181,9 @@ static void *__dma_alloc_from_coherent(struct dma_coherent_mem *mem, */ *dma_handle = mem->device_base + (pageno << PAGE_SHIFT); ret = mem->virt_base + (pageno << PAGE_SHIFT); - dma_memory_map = (mem->flags & DMA_MEMORY_MAP); spin_unlock_irqrestore(&mem->spinlock, flags); - if (dma_memory_map) - memset(ret, 0, size); - else - memset_io(ret, 0, size); - + memset(ret, 0, size); return ret; - err: spin_unlock_irqrestore(&mem->spinlock, flags); return NULL; @@ -359,14 +346,18 @@ static struct reserved_mem *dma_reserved_default_memory __initdata; static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev) { struct dma_coherent_mem *mem = rmem->priv; + int ret; - if (!mem && - !dma_init_coherent_memory(rmem->base, rmem->base, rmem->size, - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE, - &mem)) { + if (!mem) + return -ENODEV; + + ret = dma_init_coherent_memory(rmem->base, rmem->base, rmem->size, + DMA_MEMORY_EXCLUSIVE, &mem); + + if (ret) { pr_err("Reserved memory: failed to init DMA memory pool at %pa, size %ld MiB\n", &rmem->base, (unsigned long)rmem->size / SZ_1M); - return -ENODEV; + return ret; } mem->use_dev_dma_pfn_offset = true; rmem->priv = mem; diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index b555ff9dd8fc..e584eddef0a7 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c @@ -176,13 +176,10 @@ int dmam_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, rc = dma_declare_coherent_memory(dev, phys_addr, device_addr, size, flags); - if (rc) { + if (!rc) devres_add(dev, res); - rc = 0; - } else { + else devres_free(res); - rc = -ENOMEM; - } return rc; } diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index a5fb884a136d..4b57cf5bc81d 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -258,38 +258,6 @@ static int fw_cache_piggyback_on_request(const char *name); * guarding for corner cases a global lock should be OK */ static DEFINE_MUTEX(fw_lock); -static bool __enable_firmware = false; - -static void enable_firmware(void) -{ - mutex_lock(&fw_lock); - __enable_firmware = true; - mutex_unlock(&fw_lock); -} - -static void disable_firmware(void) -{ - mutex_lock(&fw_lock); - __enable_firmware = false; - mutex_unlock(&fw_lock); -} - -/* - * When disabled only the built-in firmware and the firmware cache will be - * used to look for firmware. - */ -static bool firmware_enabled(void) -{ - bool enabled = false; - - mutex_lock(&fw_lock); - if (__enable_firmware) - enabled = true; - mutex_unlock(&fw_lock); - - return enabled; -} - static struct firmware_cache fw_cache; static struct firmware_buf *__allocate_fw_buf(const char *fw_name, @@ -1246,12 +1214,6 @@ _request_firmware(const struct firmware **firmware_p, const char *name, if (ret <= 0) /* error or already assigned */ goto out; - if (!firmware_enabled()) { - WARN(1, "firmware request while host is not available\n"); - ret = -EHOSTDOWN; - goto out; - } - ret = fw_get_filesystem_firmware(device, fw->priv); if (ret) { if (!(opt_flags & FW_OPT_NO_WARN)) @@ -1762,62 +1724,6 @@ static void device_uncache_fw_images_delay(unsigned long delay) msecs_to_jiffies(delay)); } -/** - * fw_pm_notify - notifier for suspend/resume - * @notify_block: unused - * @mode: mode we are switching to - * @unused: unused - * - * Used to modify the firmware_class state as we move in between states. - * The firmware_class implements a firmware cache to enable device driver - * to fetch firmware upon resume before the root filesystem is ready. We - * disable API calls which do not use the built-in firmware or the firmware - * cache when we know these calls will not work. - * - * The inner logic behind all this is a bit complex so it is worth summarizing - * the kernel's own suspend/resume process with context and focus on how this - * can impact the firmware API. - * - * First a review on how we go to suspend:: - * - * pm_suspend() --> enter_state() --> - * sys_sync() - * suspend_prepare() --> - * __pm_notifier_call_chain(PM_SUSPEND_PREPARE, ...); - * suspend_freeze_processes() --> - * freeze_processes() --> - * __usermodehelper_set_disable_depth(UMH_DISABLED); - * freeze all tasks ... - * freeze_kernel_threads() - * suspend_devices_and_enter() --> - * dpm_suspend_start() --> - * dpm_prepare() - * dpm_suspend() - * suspend_enter() --> - * platform_suspend_prepare() - * dpm_suspend_late() - * freeze_enter() - * syscore_suspend() - * - * When we resume we bail out of a loop from suspend_devices_and_enter() and - * unwind back out to the caller enter_state() where we were before as follows:: - * - * enter_state() --> - * suspend_devices_and_enter() --> (bail from loop) - * dpm_resume_end() --> - * dpm_resume() - * dpm_complete() - * suspend_finish() --> - * suspend_thaw_processes() --> - * thaw_processes() --> - * __usermodehelper_set_disable_depth(UMH_FREEZING); - * thaw_workqueues(); - * thaw all processes ... - * usermodehelper_enable(); - * pm_notifier_call_chain(PM_POST_SUSPEND); - * - * fw_pm_notify() works through pm_notifier_call_chain(). - */ static int fw_pm_notify(struct notifier_block *notify_block, unsigned long mode, void *unused) { @@ -1831,7 +1737,6 @@ static int fw_pm_notify(struct notifier_block *notify_block, */ kill_pending_fw_fallback_reqs(true); device_cache_fw_images(); - disable_firmware(); break; case PM_POST_SUSPEND: @@ -1844,7 +1749,6 @@ static int fw_pm_notify(struct notifier_block *notify_block, mutex_lock(&fw_lock); fw_cache.state = FW_LOADER_NO_CACHE; mutex_unlock(&fw_lock); - enable_firmware(); device_uncache_fw_images_delay(10 * MSEC_PER_SEC); break; @@ -1893,7 +1797,6 @@ static void __init fw_cache_init(void) static int fw_shutdown_notify(struct notifier_block *unused1, unsigned long unused2, void *unused3) { - disable_firmware(); /* * Kill all pending fallback requests to avoid both stalling shutdown, * and avoid a deadlock with the usermode_lock. @@ -1909,7 +1812,6 @@ static struct notifier_block fw_shutdown_nb = { static int __init firmware_class_init(void) { - enable_firmware(); fw_cache_init(); register_reboot_notifier(&fw_shutdown_nb); #ifdef CONFIG_FW_LOADER_USER_HELPER @@ -1921,7 +1823,6 @@ static int __init firmware_class_init(void) static void __exit firmware_class_exit(void) { - disable_firmware(); #ifdef CONFIG_PM_SLEEP unregister_syscore_ops(&fw_syscore_ops); unregister_pm_notifier(&fw_cache.pm_notify); diff --git a/drivers/base/node.c b/drivers/base/node.c index d8dc83017d8d..3855902f2c5b 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -160,12 +160,12 @@ static ssize_t node_read_numastat(struct device *dev, "interleave_hit %lu\n" "local_node %lu\n" "other_node %lu\n", - sum_zone_node_page_state(dev->id, NUMA_HIT), - sum_zone_node_page_state(dev->id, NUMA_MISS), - sum_zone_node_page_state(dev->id, NUMA_FOREIGN), - sum_zone_node_page_state(dev->id, NUMA_INTERLEAVE_HIT), - sum_zone_node_page_state(dev->id, NUMA_LOCAL), - sum_zone_node_page_state(dev->id, NUMA_OTHER)); + sum_zone_numa_state(dev->id, NUMA_HIT), + sum_zone_numa_state(dev->id, NUMA_MISS), + sum_zone_numa_state(dev->id, NUMA_FOREIGN), + sum_zone_numa_state(dev->id, NUMA_INTERLEAVE_HIT), + sum_zone_numa_state(dev->id, NUMA_LOCAL), + sum_zone_numa_state(dev->id, NUMA_OTHER)); } static DEVICE_ATTR(numastat, S_IRUGO, node_read_numastat, NULL); @@ -181,9 +181,17 @@ static ssize_t node_read_vmstat(struct device *dev, n += sprintf(buf+n, "%s %lu\n", vmstat_text[i], sum_zone_node_page_state(nid, i)); - for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) +#ifdef CONFIG_NUMA + for (i = 0; i < NR_VM_NUMA_STAT_ITEMS; i++) n += sprintf(buf+n, "%s %lu\n", vmstat_text[i + NR_VM_ZONE_STAT_ITEMS], + sum_zone_numa_state(nid, i)); +#endif + + for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) + n += sprintf(buf+n, "%s %lu\n", + vmstat_text[i + NR_VM_ZONE_STAT_ITEMS + + NR_VM_NUMA_STAT_ITEMS], node_page_state(pgdat, i)); return n; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 407cb172d6e3..85de67334695 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -213,10 +213,13 @@ static void __loop_update_dio(struct loop_device *lo, bool dio) */ blk_mq_freeze_queue(lo->lo_queue); lo->use_dio = use_dio; - if (use_dio) + if (use_dio) { + queue_flag_clear_unlocked(QUEUE_FLAG_NOMERGES, lo->lo_queue); lo->lo_flags |= LO_FLAGS_DIRECT_IO; - else + } else { + queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, lo->lo_queue); lo->lo_flags &= ~LO_FLAGS_DIRECT_IO; + } blk_mq_unfreeze_queue(lo->lo_queue); } @@ -460,12 +463,21 @@ static void lo_complete_rq(struct request *rq) blk_mq_end_request(rq, cmd->ret < 0 ? BLK_STS_IOERR : BLK_STS_OK); } +static void lo_rw_aio_do_completion(struct loop_cmd *cmd) +{ + if (!atomic_dec_and_test(&cmd->ref)) + return; + kfree(cmd->bvec); + cmd->bvec = NULL; + blk_mq_complete_request(cmd->rq); +} + static void lo_rw_aio_complete(struct kiocb *iocb, long ret, long ret2) { struct loop_cmd *cmd = container_of(iocb, struct loop_cmd, iocb); cmd->ret = ret; - blk_mq_complete_request(cmd->rq); + lo_rw_aio_do_completion(cmd); } static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, @@ -473,22 +485,51 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, { struct iov_iter iter; struct bio_vec *bvec; - struct bio *bio = cmd->rq->bio; + struct request *rq = cmd->rq; + struct bio *bio = rq->bio; struct file *file = lo->lo_backing_file; + unsigned int offset; + int segments = 0; int ret; - /* nomerge for loop request queue */ - WARN_ON(cmd->rq->bio != cmd->rq->biotail); + if (rq->bio != rq->biotail) { + struct req_iterator iter; + struct bio_vec tmp; + + __rq_for_each_bio(bio, rq) + segments += bio_segments(bio); + bvec = kmalloc(sizeof(struct bio_vec) * segments, GFP_NOIO); + if (!bvec) + return -EIO; + cmd->bvec = bvec; + + /* + * The bios of the request may be started from the middle of + * the 'bvec' because of bio splitting, so we can't directly + * copy bio->bi_iov_vec to new bvec. The rq_for_each_segment + * API will take care of all details for us. + */ + rq_for_each_segment(tmp, rq, iter) { + *bvec = tmp; + bvec++; + } + bvec = cmd->bvec; + offset = 0; + } else { + /* + * Same here, this bio may be started from the middle of the + * 'bvec' because of bio splitting, so offset from the bvec + * must be passed to iov iterator + */ + offset = bio->bi_iter.bi_bvec_done; + bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter); + segments = bio_segments(bio); + } + atomic_set(&cmd->ref, 2); - bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter); iov_iter_bvec(&iter, ITER_BVEC | rw, bvec, - bio_segments(bio), blk_rq_bytes(cmd->rq)); - /* - * This bio may be started from the middle of the 'bvec' - * because of bio splitting, so offset from the bvec must - * be passed to iov iterator - */ - iter.iov_offset = bio->bi_iter.bi_bvec_done; + segments, blk_rq_bytes(rq)); + iter.iov_offset = offset; cmd->iocb.ki_pos = pos; cmd->iocb.ki_filp = file; @@ -500,6 +541,8 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, else ret = call_read_iter(file, &cmd->iocb, &iter); + lo_rw_aio_do_completion(cmd); + if (ret != -EIOCBQUEUED) cmd->iocb.ki_complete(&cmd->iocb, ret, 0); return 0; @@ -546,74 +589,12 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq) } } -struct switch_request { - struct file *file; - struct completion wait; -}; - static inline void loop_update_dio(struct loop_device *lo) { __loop_update_dio(lo, io_is_direct(lo->lo_backing_file) | lo->use_dio); } -/* - * Do the actual switch; called from the BIO completion routine - */ -static void do_loop_switch(struct loop_device *lo, struct switch_request *p) -{ - struct file *file = p->file; - struct file *old_file = lo->lo_backing_file; - struct address_space *mapping; - - /* if no new file, only flush of queued bios requested */ - if (!file) - return; - - mapping = file->f_mapping; - mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); - lo->lo_backing_file = file; - lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ? - mapping->host->i_bdev->bd_block_size : PAGE_SIZE; - lo->old_gfp_mask = mapping_gfp_mask(mapping); - mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); - loop_update_dio(lo); -} - -/* - * loop_switch performs the hard work of switching a backing store. - * First it needs to flush existing IO, it does this by sending a magic - * BIO down the pipe. The completion of this BIO does the actual switch. - */ -static int loop_switch(struct loop_device *lo, struct file *file) -{ - struct switch_request w; - - w.file = file; - - /* freeze queue and wait for completion of scheduled requests */ - blk_mq_freeze_queue(lo->lo_queue); - - /* do the switch action */ - do_loop_switch(lo, &w); - - /* unfreeze */ - blk_mq_unfreeze_queue(lo->lo_queue); - - return 0; -} - -/* - * Helper to flush the IOs in loop, but keeping loop thread running - */ -static int loop_flush(struct loop_device *lo) -{ - /* loop not yet configured, no running thread, nothing to flush */ - if (lo->lo_state != Lo_bound) - return 0; - return loop_switch(lo, NULL); -} - static void loop_reread_partitions(struct loop_device *lo, struct block_device *bdev) { @@ -678,9 +659,14 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, goto out_putf; /* and ... switch */ - error = loop_switch(lo, file); - if (error) - goto out_putf; + blk_mq_freeze_queue(lo->lo_queue); + mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); + lo->lo_backing_file = file; + lo->old_gfp_mask = mapping_gfp_mask(file->f_mapping); + mapping_set_gfp_mask(file->f_mapping, + lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); + loop_update_dio(lo); + blk_mq_unfreeze_queue(lo->lo_queue); fput(old_file); if (lo->lo_flags & LO_FLAGS_PARTSCAN) @@ -867,7 +853,6 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, struct file *file, *f; struct inode *inode; struct address_space *mapping; - unsigned lo_blocksize; int lo_flags = 0; int error; loff_t size; @@ -911,9 +896,6 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, !file->f_op->write_iter) lo_flags |= LO_FLAGS_READ_ONLY; - lo_blocksize = S_ISBLK(inode->i_mode) ? - inode->i_bdev->bd_block_size : PAGE_SIZE; - error = -EFBIG; size = get_loop_size(lo, file); if ((loff_t)(sector_t)size != size) @@ -927,7 +909,6 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, set_device_ro(bdev, (lo_flags & LO_FLAGS_READ_ONLY) != 0); lo->use_dio = false; - lo->lo_blocksize = lo_blocksize; lo->lo_device = bdev; lo->lo_flags = lo_flags; lo->lo_backing_file = file; @@ -947,7 +928,8 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, /* let user-space know about the new size */ kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE); - set_blocksize(bdev, lo_blocksize); + set_blocksize(bdev, S_ISBLK(inode->i_mode) ? + block_size(inode->i_bdev) : PAGE_SIZE); lo->lo_state = Lo_bound; if (part_shift) @@ -1053,6 +1035,9 @@ static int loop_clr_fd(struct loop_device *lo) memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE); memset(lo->lo_crypt_name, 0, LO_NAME_SIZE); memset(lo->lo_file_name, 0, LO_NAME_SIZE); + blk_queue_logical_block_size(lo->lo_queue, 512); + blk_queue_physical_block_size(lo->lo_queue, 512); + blk_queue_io_min(lo->lo_queue, 512); if (bdev) { bdput(bdev); invalidate_bdev(bdev); @@ -1336,6 +1321,26 @@ static int loop_set_dio(struct loop_device *lo, unsigned long arg) return error; } +static int loop_set_block_size(struct loop_device *lo, unsigned long arg) +{ + if (lo->lo_state != Lo_bound) + return -ENXIO; + + if (arg < 512 || arg > PAGE_SIZE || !is_power_of_2(arg)) + return -EINVAL; + + blk_mq_freeze_queue(lo->lo_queue); + + blk_queue_logical_block_size(lo->lo_queue, arg); + blk_queue_physical_block_size(lo->lo_queue, arg); + blk_queue_io_min(lo->lo_queue, arg); + loop_update_dio(lo); + + blk_mq_unfreeze_queue(lo->lo_queue); + + return 0; +} + static int lo_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { @@ -1384,6 +1389,11 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) err = loop_set_dio(lo, arg); break; + case LOOP_SET_BLOCK_SIZE: + err = -EPERM; + if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) + err = loop_set_block_size(lo, arg); + break; default: err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL; } @@ -1583,12 +1593,13 @@ static void lo_release(struct gendisk *disk, fmode_t mode) err = loop_clr_fd(lo); if (!err) return; - } else { + } else if (lo->lo_state == Lo_bound) { /* * Otherwise keep thread (if running) and config, * but flush possible ongoing bios in thread. */ - loop_flush(lo); + blk_mq_freeze_queue(lo->lo_queue); + blk_mq_unfreeze_queue(lo->lo_queue); } mutex_unlock(&lo->lo_ctl_mutex); @@ -1770,9 +1781,13 @@ static int loop_add(struct loop_device **l, int i) } lo->lo_queue->queuedata = lo; + blk_queue_max_hw_sectors(lo->lo_queue, BLK_DEF_MAX_SECTORS); + /* - * It doesn't make sense to enable merge because the I/O - * submitted to backing file is handled page by page. + * By default, we do buffer IO, so it doesn't make sense to enable + * merge because the I/O submitted to backing file is handled page by + * page. For directio mode, merge does help to dispatch bigger request + * to underlayer disk. We will enable merge once directio is enabled. */ queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, lo->lo_queue); diff --git a/drivers/block/loop.h b/drivers/block/loop.h index fecd3f97ef8c..f68c1d50802f 100644 --- a/drivers/block/loop.h +++ b/drivers/block/loop.h @@ -48,7 +48,6 @@ struct loop_device { struct file * lo_backing_file; struct block_device *lo_device; - unsigned lo_blocksize; void *key_data; gfp_t old_gfp_mask; @@ -68,10 +67,13 @@ struct loop_device { struct loop_cmd { struct kthread_work work; struct request *rq; - struct list_head list; - bool use_aio; /* use AIO interface to handle I/O */ + union { + bool use_aio; /* use AIO interface to handle I/O */ + atomic_t ref; /* only for aio */ + }; long ret; struct kiocb iocb; + struct bio_vec *bvec; }; /* Support for loadable transfer modules */ diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index b008b6a98098..b640ad8a6d20 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -3435,7 +3435,7 @@ static void rbd_acquire_lock(struct work_struct *work) struct rbd_device *rbd_dev = container_of(to_delayed_work(work), struct rbd_device, lock_dwork); enum rbd_lock_state lock_state; - int ret; + int ret = 0; dout("%s rbd_dev %p\n", __func__, rbd_dev); again: diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 4063f3f59f4f..2981c27d3aae 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -175,20 +175,11 @@ static inline void update_used_max(struct zram *zram, } while (old_max != cur_max); } -static inline void zram_fill_page(char *ptr, unsigned long len, +static inline void zram_fill_page(void *ptr, unsigned long len, unsigned long value) { - int i; - unsigned long *page = (unsigned long *)ptr; - WARN_ON_ONCE(!IS_ALIGNED(len, sizeof(unsigned long))); - - if (likely(value == 0)) { - memset(ptr, 0, len); - } else { - for (i = 0; i < len / sizeof(*page); i++) - page[i] = value; - } + memset_l(ptr, value, len / sizeof(unsigned long)); } static bool page_same_filled(void *ptr, unsigned long *element) diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 2408ea38a39c..ae3d8f3444b9 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -132,7 +132,7 @@ config SIMPLE_PM_BUS config SUNXI_RSB tristate "Allwinner sunXi Reduced Serial Bus Driver" - default MACH_SUN8I || MACH_SUN9I + default MACH_SUN8I || MACH_SUN9I || ARM64 depends on ARCH_SUNXI select REGMAP help diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index c49da15d9790..3c29d36702a8 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c @@ -2124,8 +2124,8 @@ int notrace __cci_control_port_by_device(struct device_node *dn, bool enable) return -ENODEV; port = __cci_ace_get_port(dn, ACE_LITE_PORT); - if (WARN_ONCE(port < 0, "node %s ACE lite port look-up failure\n", - dn->full_name)) + if (WARN_ONCE(port < 0, "node %pOF ACE lite port look-up failure\n", + dn)) return -ENODEV; cci_port_control(port, enable); return 0; @@ -2200,14 +2200,14 @@ static int cci_probe_ports(struct device_node *np) if (of_property_read_string(cp, "interface-type", &match_str)) { - WARN(1, "node %s missing interface-type property\n", - cp->full_name); + WARN(1, "node %pOF missing interface-type property\n", + cp); continue; } is_ace = strcmp(match_str, "ace") == 0; if (!is_ace && strcmp(match_str, "ace-lite")) { - WARN(1, "node %s containing invalid interface-type property, skipping it\n", - cp->full_name); + WARN(1, "node %pOF containing invalid interface-type property, skipping it\n", + cp); continue; } diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index 4bd361d64270..3d56ebcda720 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -156,8 +156,8 @@ static int __init weim_parse_dt(struct platform_device *pdev, ret = weim_timing_setup(child, base, devtype); if (ret) - dev_warn(&pdev->dev, "%s set timing failed.\n", - child->full_name); + dev_warn(&pdev->dev, "%pOF set timing failed.\n", + child); else have_child = 1; } @@ -166,8 +166,8 @@ static int __init weim_parse_dt(struct platform_device *pdev, ret = of_platform_default_populate(pdev->dev.of_node, NULL, &pdev->dev); if (ret) - dev_err(&pdev->dev, "%s fail to create devices.\n", - pdev->dev.of_node->full_name); + dev_err(&pdev->dev, "%pOF fail to create devices.\n", + pdev->dev.of_node); return ret; } diff --git a/drivers/bus/omap-ocp2scp.c b/drivers/bus/omap-ocp2scp.c index bf500e0e7362..77791f3dcfc6 100644 --- a/drivers/bus/omap-ocp2scp.c +++ b/drivers/bus/omap-ocp2scp.c @@ -70,8 +70,10 @@ static int omap_ocp2scp_probe(struct platform_device *pdev) if (!of_device_is_compatible(np, "ti,am437x-ocp2scp")) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(regs)) - goto err0; + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto err1; + } pm_runtime_get_sync(&pdev->dev); reg = readl_relaxed(regs + OCP2SCP_TIMING); @@ -83,6 +85,9 @@ static int omap_ocp2scp_probe(struct platform_device *pdev) return 0; +err1: + pm_runtime_disable(&pdev->dev); + err0: device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices); diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index 795c9d9c96a6..328ca93781cf 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -556,20 +556,20 @@ static int of_rsb_register_devices(struct sunxi_rsb *rsb) /* Runtime addresses for all slaves should be set first */ for_each_available_child_of_node(np, child) { - dev_dbg(dev, "setting child %s runtime address\n", - child->full_name); + dev_dbg(dev, "setting child %pOF runtime address\n", + child); ret = of_property_read_u32(child, "reg", &hwaddr); if (ret) { - dev_err(dev, "%s: invalid 'reg' property: %d\n", - child->full_name, ret); + dev_err(dev, "%pOF: invalid 'reg' property: %d\n", + child, ret); continue; } rtaddr = sunxi_rsb_get_rtaddr(hwaddr); if (!rtaddr) { - dev_err(dev, "%s: unknown hardware device address\n", - child->full_name); + dev_err(dev, "%pOF: unknown hardware device address\n", + child); continue; } @@ -586,15 +586,15 @@ static int of_rsb_register_devices(struct sunxi_rsb *rsb) /* send command */ ret = _sunxi_rsb_run_xfer(rsb); if (ret) - dev_warn(dev, "%s: set runtime address failed: %d\n", - child->full_name, ret); + dev_warn(dev, "%pOF: set runtime address failed: %d\n", + child, ret); } /* Then we start adding devices and probing them */ for_each_available_child_of_node(np, child) { struct sunxi_rsb_device *rdev; - dev_dbg(dev, "adding child %s\n", child->full_name); + dev_dbg(dev, "adding child %pOF\n", child); ret = of_property_read_u32(child, "reg", &hwaddr); if (ret) @@ -606,8 +606,8 @@ static int of_rsb_register_devices(struct sunxi_rsb *rsb) rdev = sunxi_rsb_device_create(rsb, child, hwaddr, rtaddr); if (IS_ERR(rdev)) - dev_err(dev, "failed to add child device %s: %ld\n", - child->full_name, PTR_ERR(rdev)); + dev_err(dev, "failed to add child device %pOF: %ld\n", + child, PTR_ERR(rdev)); } return 0; diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index f4f866ee54bc..d3a979e25724 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1491,7 +1491,7 @@ static struct platform_driver sonypi_driver = { static struct platform_device *sonypi_platform_device; -static struct dmi_system_id __initdata sonypi_dmi_table[] = { +static const struct dmi_system_id sonypi_dmi_table[] __initconst = { { .ident = "Sony Vaio", .matches = { diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 23f33f95d4a6..d1aed2513bd9 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -451,9 +451,6 @@ static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size, * device is created by remoteproc, the DMA memory is * associated with the grandparent device: * vdev => rproc => platform-dev. - * The code here would have been less quirky if - * DMA_MEMORY_INCLUDES_CHILDREN had been supported - * in dma-coherent.c */ if (!vq->vdev->dev.parent || !vq->vdev->dev.parent->parent) goto free_buf; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 68ca2d9fcd73..1c4e1aa6767e 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -31,6 +31,13 @@ config COMMON_CLK_WM831X source "drivers/clk/versatile/Kconfig" +config CLK_HSDK + bool "PLL Driver for HSDK platform" + depends on OF || COMPILE_TEST + ---help--- + This driver supports the HSDK core, system, ddr, tunnel and hdmi PLLs + control. + config COMMON_CLK_MAX77686 tristate "Clock driver for Maxim 77620/77686/77802 MFD" depends on MFD_MAX77686 || MFD_MAX77620 || COMPILE_TEST @@ -39,10 +46,10 @@ config COMMON_CLK_MAX77686 clock. config COMMON_CLK_RK808 - tristate "Clock driver for RK808/RK818" + tristate "Clock driver for RK805/RK808/RK818" depends on MFD_RK808 ---help--- - This driver supports RK808 and RK818 crystal oscillator clock. These + This driver supports RK805, RK808 and RK818 crystal oscillator clock. These multi-function devices have two fixed-rate oscillators, clocked at 32KHz each. Clkout1 is always on, Clkout2 can off by control register. @@ -210,14 +217,14 @@ config COMMON_CLK_OXNAS Support for the OXNAS SoC Family clocks. config COMMON_CLK_VC5 - tristate "Clock driver for IDT VersaClock5 devices" + tristate "Clock driver for IDT VersaClock 5,6 devices" depends on I2C depends on OF select REGMAP_I2C help ---help--- - This driver supports the IDT VersaClock5 programmable clock - generator. + This driver supports the IDT VersaClock 5 and VersaClock 6 + programmable clock generators. source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cd376b3fb47a..c99f363826f0 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -27,8 +27,8 @@ obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o +obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o -obj-$(CONFIG_ARCH_MB86S7X) += clk-mb86s7x.o obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o @@ -44,6 +44,7 @@ obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o +obj-$(CONFIG_ARCH_STM32) += clk-stm32h7.o obj-$(CONFIG_ARCH_TANGO) += clk-tango4.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o obj-$(CONFIG_ARCH_U300) += clk-u300.o diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 13e67bd35cff..c68947b65a4c 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -6,6 +6,7 @@ obj-y += pmc.o sckc.o obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o obj-y += clk-system.o clk-peripheral.o clk-programmable.o +obj-$(CONFIG_HAVE_AT91_AUDIO_PLL) += clk-audio-pll.o obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c new file mode 100644 index 000000000000..da7bafcfbe70 --- /dev/null +++ b/drivers/clk/at91/clk-audio-pll.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2016 Atmel Corporation, + * Songjun Wu <songjun.wu@atmel.com>, + * Nicolas Ferre <nicolas.ferre@atmel.com> + * Copyright (C) 2017 Free Electrons, + * Quentin Schulz <quentin.schulz@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The Sama5d2 SoC has two audio PLLs (PMC and PAD) that shares the same parent + * (FRAC). FRAC can output between 620 and 700MHz and only multiply the rate of + * its own parent. PMC and PAD can then divide the FRAC rate to best match the + * asked rate. + * + * Traits of FRAC clock: + * enable - clk_enable writes nd, fracr parameters and enables PLL + * rate - rate is adjustable. + * clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22)) + * parent - fixed parent. No clk_set_parent support + * + * Traits of PMC clock: + * enable - clk_enable writes qdpmc, and enables PMC output + * rate - rate is adjustable. + * clk->rate = parent->rate / (qdpmc + 1) + * parent - fixed parent. No clk_set_parent support + * + * Traits of PAD clock: + * enable - clk_enable writes divisors and enables PAD output + * rate - rate is adjustable. + * clk->rate = parent->rate / (qdaudio * div)) + * parent - fixed parent. No clk_set_parent support + * + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clk/at91_pmc.h> +#include <linux/of.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define AUDIO_PLL_DIV_FRAC BIT(22) +#define AUDIO_PLL_ND_MAX (AT91_PMC_AUDIO_PLL_ND_MASK >> \ + AT91_PMC_AUDIO_PLL_ND_OFFSET) + +#define AUDIO_PLL_QDPAD(qd, div) ((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \ + AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \ + (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \ + AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK)) + +#define AUDIO_PLL_QDPMC_MAX (AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \ + AT91_PMC_AUDIO_PLL_QDPMC_OFFSET) + +#define AUDIO_PLL_FOUT_MIN 620000000UL +#define AUDIO_PLL_FOUT_MAX 700000000UL + +struct clk_audio_frac { + struct clk_hw hw; + struct regmap *regmap; + u32 fracr; + u8 nd; +}; + +struct clk_audio_pad { + struct clk_hw hw; + struct regmap *regmap; + u8 qdaudio; + u8 div; +}; + +struct clk_audio_pmc { + struct clk_hw hw; + struct regmap *regmap; + u8 qdpmc; +}; + +#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw) +#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw) +#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw) + +static int clk_audio_pll_frac_enable(struct clk_hw *hw) +{ + struct clk_audio_frac *frac = to_clk_audio_frac(hw); + + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_RESETN, 0); + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_RESETN, + AT91_PMC_AUDIO_PLL_RESETN); + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL1, + AT91_PMC_AUDIO_PLL_FRACR_MASK, frac->fracr); + + /* + * reset and enable have to be done in 2 separated writes + * for AT91_PMC_AUDIO_PLL0 + */ + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PLLEN | + AT91_PMC_AUDIO_PLL_ND_MASK, + AT91_PMC_AUDIO_PLL_PLLEN | + AT91_PMC_AUDIO_PLL_ND(frac->nd)); + + return 0; +} + +static int clk_audio_pll_pad_enable(struct clk_hw *hw) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + + regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1, + AT91_PMC_AUDIO_PLL_QDPAD_MASK, + AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div)); + regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN); + + return 0; +} + +static int clk_audio_pll_pmc_enable(struct clk_hw *hw) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + + regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PMCEN | + AT91_PMC_AUDIO_PLL_QDPMC_MASK, + AT91_PMC_AUDIO_PLL_PMCEN | + AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc)); + return 0; +} + +static void clk_audio_pll_frac_disable(struct clk_hw *hw) +{ + struct clk_audio_frac *frac = to_clk_audio_frac(hw); + + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PLLEN, 0); + /* do it in 2 separated writes */ + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_RESETN, 0); +} + +static void clk_audio_pll_pad_disable(struct clk_hw *hw) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + + regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PADEN, 0); +} + +static void clk_audio_pll_pmc_disable(struct clk_hw *hw) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + + regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PMCEN, 0); +} + +static unsigned long clk_audio_pll_fout(unsigned long parent_rate, + unsigned long nd, unsigned long fracr) +{ + unsigned long long fr = (unsigned long long)parent_rate * fracr; + + pr_debug("A PLL: %s, fr = %llu\n", __func__, fr); + + fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC); + + pr_debug("A PLL: %s, fr = %llu\n", __func__, fr); + + return parent_rate * (nd + 1) + fr; +} + +static unsigned long clk_audio_pll_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_audio_frac *frac = to_clk_audio_frac(hw); + unsigned long fout; + + fout = clk_audio_pll_fout(parent_rate, frac->nd, frac->fracr); + + pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__, + fout, frac->nd, (unsigned long)frac->fracr); + + return fout; +} + +static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + unsigned long apad_rate = 0; + + if (apad_ck->qdaudio && apad_ck->div) + apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div); + + pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n", + __func__, apad_rate, apad_ck->div, apad_ck->qdaudio); + + return apad_rate; +} + +static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + unsigned long apmc_rate = 0; + + apmc_rate = parent_rate / (apmc_ck->qdpmc + 1); + + pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__, + apmc_rate, apmc_ck->qdpmc); + + return apmc_rate; +} + +static int clk_audio_pll_frac_compute_frac(unsigned long rate, + unsigned long parent_rate, + unsigned long *nd, + unsigned long *fracr) +{ + unsigned long long tmp, rem; + + if (!rate) + return -EINVAL; + + tmp = rate; + rem = do_div(tmp, parent_rate); + if (!tmp || tmp >= AUDIO_PLL_ND_MAX) + return -EINVAL; + + *nd = tmp - 1; + + tmp = rem * AUDIO_PLL_DIV_FRAC; + tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate); + if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK) + return -EINVAL; + + /* we can cast here as we verified the bounds just above */ + *fracr = (unsigned long)tmp; + + return 0; +} + +static int clk_audio_pll_frac_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long fracr, nd; + int ret; + + pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, + req->rate, req->best_parent_rate); + + req->rate = clamp(req->rate, AUDIO_PLL_FOUT_MIN, AUDIO_PLL_FOUT_MAX); + + req->min_rate = max(req->min_rate, AUDIO_PLL_FOUT_MIN); + req->max_rate = min(req->max_rate, AUDIO_PLL_FOUT_MAX); + + ret = clk_audio_pll_frac_compute_frac(req->rate, req->best_parent_rate, + &nd, &fracr); + if (ret) + return ret; + + req->rate = clk_audio_pll_fout(req->best_parent_rate, nd, fracr); + + req->best_parent_hw = clk_hw_get_parent(hw); + + pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n", + __func__, req->rate, nd, fracr); + + return 0; +} + +static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_hw *pclk = clk_hw_get_parent(hw); + long best_rate = -EINVAL; + unsigned long best_parent_rate; + unsigned long tmp_qd; + u32 div; + long tmp_rate; + int tmp_diff; + int best_diff = -1; + + pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, *parent_rate); + + /* + * Rate divisor is actually made of two different divisors, multiplied + * between themselves before dividing the rate. + * tmp_qd goes from 1 to 31 and div is either 2 or 3. + * In order to avoid testing twice the rate divisor (e.g. divisor 12 can + * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop + * for a rate divisor when div is 2 and tmp_qd is a multiple of 3. + * We cannot inverse it (condition div is 3 and tmp_qd is even) or we + * would miss some rate divisor that aren't reachable with div being 2 + * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus + * tmp_qd is even so we skip it because we think div 2 could make this + * rate divisor which isn't possible since tmp_qd has to be <= 31). + */ + for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++) + for (div = 2; div <= 3; div++) { + if (div == 2 && tmp_qd % 3 == 0) + continue; + + best_parent_rate = clk_hw_round_rate(pclk, + rate * tmp_qd * div); + tmp_rate = best_parent_rate / (div * tmp_qd); + tmp_diff = abs(rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + *parent_rate = best_parent_rate; + best_rate = tmp_rate; + best_diff = tmp_diff; + } + } + + pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n", + __func__, best_rate, best_parent_rate); + + return best_rate; +} + +static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_hw *pclk = clk_hw_get_parent(hw); + long best_rate = -EINVAL; + unsigned long best_parent_rate = 0; + u32 tmp_qd = 0, div; + long tmp_rate; + int tmp_diff; + int best_diff = -1; + + pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, *parent_rate); + + for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) { + best_parent_rate = clk_round_rate(pclk->clk, rate * div); + tmp_rate = best_parent_rate / div; + tmp_diff = abs(rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + *parent_rate = best_parent_rate; + best_rate = tmp_rate; + best_diff = tmp_diff; + tmp_qd = div; + } + } + + pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n", + __func__, best_rate, *parent_rate, tmp_qd - 1); + + return best_rate; +} + +static int clk_audio_pll_frac_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_audio_frac *frac = to_clk_audio_frac(hw); + unsigned long fracr, nd; + int ret; + + pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate, + parent_rate); + + if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX) + return -EINVAL; + + ret = clk_audio_pll_frac_compute_frac(rate, parent_rate, &nd, &fracr); + if (ret) + return ret; + + frac->nd = nd; + frac->fracr = fracr; + + return 0; +} + +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + u8 tmp_div; + + pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, parent_rate); + + if (!rate) + return -EINVAL; + + tmp_div = parent_rate / rate; + if (tmp_div % 3 == 0) { + apad_ck->qdaudio = tmp_div / 3; + apad_ck->div = 3; + } else { + apad_ck->qdaudio = tmp_div / 2; + apad_ck->div = 2; + } + + return 0; +} + +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + + if (!rate) + return -EINVAL; + + pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, parent_rate); + + apmc_ck->qdpmc = parent_rate / rate - 1; + + return 0; +} + +static const struct clk_ops audio_pll_frac_ops = { + .enable = clk_audio_pll_frac_enable, + .disable = clk_audio_pll_frac_disable, + .recalc_rate = clk_audio_pll_frac_recalc_rate, + .determine_rate = clk_audio_pll_frac_determine_rate, + .set_rate = clk_audio_pll_frac_set_rate, +}; + +static const struct clk_ops audio_pll_pad_ops = { + .enable = clk_audio_pll_pad_enable, + .disable = clk_audio_pll_pad_disable, + .recalc_rate = clk_audio_pll_pad_recalc_rate, + .round_rate = clk_audio_pll_pad_round_rate, + .set_rate = clk_audio_pll_pad_set_rate, +}; + +static const struct clk_ops audio_pll_pmc_ops = { + .enable = clk_audio_pll_pmc_enable, + .disable = clk_audio_pll_pmc_disable, + .recalc_rate = clk_audio_pll_pmc_recalc_rate, + .round_rate = clk_audio_pll_pmc_round_rate, + .set_rate = clk_audio_pll_pmc_set_rate, +}; + +static int of_sama5d2_clk_audio_pll_setup(struct device_node *np, + struct clk_init_data *init, + struct clk_hw *hw, + struct regmap **clk_audio_regmap) +{ + struct regmap *regmap; + const char *parent_names[1]; + int ret; + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + init->name = np->name; + of_clk_parent_fill(np, parent_names, 1); + init->parent_names = parent_names; + init->num_parents = 1; + + hw->init = init; + *clk_audio_regmap = regmap; + + ret = clk_hw_register(NULL, hw); + if (ret) + return ret; + + return of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); +} + +static void __init of_sama5d2_clk_audio_pll_frac_setup(struct device_node *np) +{ + struct clk_audio_frac *frac_ck; + struct clk_init_data init = {}; + + frac_ck = kzalloc(sizeof(*frac_ck), GFP_KERNEL); + if (!frac_ck) + return; + + init.ops = &audio_pll_frac_ops; + init.flags = CLK_SET_RATE_GATE; + + if (of_sama5d2_clk_audio_pll_setup(np, &init, &frac_ck->hw, + &frac_ck->regmap)) + kfree(frac_ck); +} + +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np) +{ + struct clk_audio_pad *apad_ck; + struct clk_init_data init = {}; + + apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL); + if (!apad_ck) + return; + + init.ops = &audio_pll_pad_ops; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + + if (of_sama5d2_clk_audio_pll_setup(np, &init, &apad_ck->hw, + &apad_ck->regmap)) + kfree(apad_ck); +} + +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np) +{ + struct clk_audio_pad *apmc_ck; + struct clk_init_data init = {}; + + apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL); + if (!apmc_ck) + return; + + init.ops = &audio_pll_pmc_ops; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + + if (of_sama5d2_clk_audio_pll_setup(np, &init, &apmc_ck->hw, + &apmc_ck->regmap)) + kfree(apmc_ck); +} + +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_frac_setup, + "atmel,sama5d2-clk-audio-pll-frac", + of_sama5d2_clk_audio_pll_frac_setup); +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup, + "atmel,sama5d2-clk-audio-pll-pad", + of_sama5d2_clk_audio_pll_pad_setup); +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup, + "atmel,sama5d2-clk-audio-pll-pmc", + of_sama5d2_clk_audio_pll_pmc_setup); diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c index f0b7ae904ce2..33481368740e 100644 --- a/drivers/clk/at91/clk-generated.c +++ b/drivers/clk/at91/clk-generated.c @@ -26,6 +26,13 @@ #define GENERATED_SOURCE_MAX 6 #define GENERATED_MAX_DIV 255 +#define GCK_ID_SSC0 43 +#define GCK_ID_SSC1 44 +#define GCK_ID_I2S0 54 +#define GCK_ID_I2S1 55 +#define GCK_ID_CLASSD 59 +#define GCK_INDEX_DT_AUDIO_PLL 5 + struct clk_generated { struct clk_hw hw; struct regmap *regmap; @@ -34,6 +41,7 @@ struct clk_generated { u32 id; u32 gckdiv; u8 parent_id; + bool audio_pll_allowed; }; #define to_clk_generated(hw) \ @@ -99,21 +107,41 @@ clk_generated_recalc_rate(struct clk_hw *hw, return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1); } +static void clk_generated_best_diff(struct clk_rate_request *req, + struct clk_hw *parent, + unsigned long parent_rate, u32 div, + int *best_diff, long *best_rate) +{ + unsigned long tmp_rate; + int tmp_diff; + + if (!div) + tmp_rate = parent_rate; + else + tmp_rate = parent_rate / div; + tmp_diff = abs(req->rate - tmp_rate); + + if (*best_diff < 0 || *best_diff > tmp_diff) { + *best_rate = tmp_rate; + *best_diff = tmp_diff; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } +} + static int clk_generated_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct clk_generated *gck = to_clk_generated(hw); struct clk_hw *parent = NULL; + struct clk_rate_request req_parent = *req; long best_rate = -EINVAL; - unsigned long tmp_rate, min_rate; + unsigned long min_rate, parent_rate; int best_diff = -1; - int tmp_diff; int i; + u32 div; - for (i = 0; i < clk_hw_get_num_parents(hw); i++) { - u32 div; - unsigned long parent_rate; - + for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) { parent = clk_hw_get_parent_by_index(hw, i); if (!parent) continue; @@ -124,25 +152,43 @@ static int clk_generated_determine_rate(struct clk_hw *hw, (gck->range.max && min_rate > gck->range.max)) continue; - for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { - tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div); - tmp_diff = abs(req->rate - tmp_rate); + div = DIV_ROUND_CLOSEST(parent_rate, req->rate); - if (best_diff < 0 || best_diff > tmp_diff) { - best_rate = tmp_rate; - best_diff = tmp_diff; - req->best_parent_rate = parent_rate; - req->best_parent_hw = parent; - } + clk_generated_best_diff(req, parent, parent_rate, div, + &best_diff, &best_rate); - if (!best_diff || tmp_rate < req->rate) - break; - } + if (!best_diff) + break; + } + + /* + * The audio_pll rate can be modified, unlike the five others clocks + * that should never be altered. + * The audio_pll can technically be used by multiple consumers. However, + * with the rate locking, the first consumer to enable to clock will be + * the one definitely setting the rate of the clock. + * Since audio IPs are most likely to request the same rate, we enforce + * that the only clks able to modify gck rate are those of audio IPs. + */ + + if (!gck->audio_pll_allowed) + goto end; + + parent = clk_hw_get_parent_by_index(hw, GCK_INDEX_DT_AUDIO_PLL); + if (!parent) + goto end; + + for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { + req_parent.rate = req->rate * div; + __clk_determine_rate(parent, &req_parent); + clk_generated_best_diff(req, parent, req_parent.rate, div, + &best_diff, &best_rate); if (!best_diff) break; } +end: pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", __func__, best_rate, __clk_get_name((req->best_parent_hw)->clk), @@ -252,7 +298,8 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, init.ops = &generated_ops; init.parent_names = parent_names; init.num_parents = num_parents; - init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; gck->id = id; gck->hw.init = &init; @@ -284,6 +331,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np) struct device_node *gcknp; struct clk_range range = CLK_RANGE(0, 0); struct regmap *regmap; + struct clk_generated *gck; num_parents = of_clk_get_parent_count(np); if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX) @@ -315,6 +363,21 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np) hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name, parent_names, num_parents, id, &range); + + gck = to_clk_generated(hw); + + if (of_device_is_compatible(np, + "atmel,sama5d2-clk-generated")) { + if (gck->id == GCK_ID_SSC0 || gck->id == GCK_ID_SSC1 || + gck->id == GCK_ID_I2S0 || gck->id == GCK_ID_I2S1 || + gck->id == GCK_ID_CLASSD) + gck->audio_pll_allowed = true; + else + gck->audio_pll_allowed = false; + } else { + gck->audio_pll_allowed = false; + } + if (IS_ERR(hw)) continue; diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile index 01996b871b06..d747deafbf1e 100644 --- a/drivers/clk/axs10x/Makefile +++ b/drivers/clk/axs10x/Makefile @@ -1 +1,2 @@ obj-y += i2s_pll_clock.o +obj-y += pll_clock.o diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c new file mode 100644 index 000000000000..25d8c240ddfb --- /dev/null +++ b/drivers/clk/axs10x/pll_clock.c @@ -0,0 +1,346 @@ +/* + * Synopsys AXS10X SDP Generic PLL clock driver + * + * Copyright (C) 2017 Synopsys + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/slab.h> +#include <linux/of.h> + +/* PLL registers addresses */ +#define PLL_REG_IDIV 0x0 +#define PLL_REG_FBDIV 0x4 +#define PLL_REG_ODIV 0x8 + +/* + * Bit fields of the PLL IDIV/FBDIV/ODIV registers: + * ________________________________________________________________________ + * |31 15| 14 | 13 | 12 |11 6|5 0| + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--| + * |____________________|__________|________|______|____________|___________| + * + * Following macros determine the way of access to these registers + * They should be set up only using the macros. + * reg should be an u32 variable. + */ + +#define PLL_REG_GET_LOW(reg) \ + (((reg) & (0x3F << 0)) >> 0) +#define PLL_REG_GET_HIGH(reg) \ + (((reg) & (0x3F << 6)) >> 6) +#define PLL_REG_GET_EDGE(reg) \ + (((reg) & (BIT(12))) ? 1 : 0) +#define PLL_REG_GET_BYPASS(reg) \ + (((reg) & (BIT(13))) ? 1 : 0) +#define PLL_REG_GET_NOUPD(reg) \ + (((reg) & (BIT(14))) ? 1 : 0) +#define PLL_REG_GET_PAD(reg) \ + (((reg) & (0x1FFFF << 15)) >> 15) + +#define PLL_REG_SET_LOW(reg, value) \ + { reg |= (((value) & 0x3F) << 0); } +#define PLL_REG_SET_HIGH(reg, value) \ + { reg |= (((value) & 0x3F) << 6); } +#define PLL_REG_SET_EDGE(reg, value) \ + { reg |= (((value) & 0x01) << 12); } +#define PLL_REG_SET_BYPASS(reg, value) \ + { reg |= (((value) & 0x01) << 13); } +#define PLL_REG_SET_NOUPD(reg, value) \ + { reg |= (((value) & 0x01) << 14); } +#define PLL_REG_SET_PAD(reg, value) \ + { reg |= (((value) & 0x1FFFF) << 15); } + +#define PLL_LOCK BIT(0) +#define PLL_ERROR BIT(1) +#define PLL_MAX_LOCK_TIME 100 /* 100 us */ + +struct axs10x_pll_cfg { + u32 rate; + u32 idiv; + u32 fbdiv; + u32 odiv; +}; + +static const struct axs10x_pll_cfg arc_pll_cfg[] = { + { 33333333, 1, 1, 1 }, + { 50000000, 1, 30, 20 }, + { 75000000, 2, 45, 10 }, + { 90000000, 2, 54, 10 }, + { 100000000, 1, 30, 10 }, + { 125000000, 2, 45, 6 }, + {} +}; + +static const struct axs10x_pll_cfg pgu_pll_cfg[] = { + { 25200000, 1, 84, 90 }, + { 50000000, 1, 100, 54 }, + { 74250000, 1, 44, 16 }, + {} +}; + +struct axs10x_pll_clk { + struct clk_hw hw; + void __iomem *base; + void __iomem *lock; + const struct axs10x_pll_cfg *pll_cfg; + struct device *dev; +}; + +static inline void axs10x_pll_write(struct axs10x_pll_clk *clk, u32 reg, + u32 val) +{ + iowrite32(val, clk->base + reg); +} + +static inline u32 axs10x_pll_read(struct axs10x_pll_clk *clk, u32 reg) +{ + return ioread32(clk->base + reg); +} + +static inline struct axs10x_pll_clk *to_axs10x_pll_clk(struct clk_hw *hw) +{ + return container_of(hw, struct axs10x_pll_clk, hw); +} + +static inline u32 axs10x_div_get_value(u32 reg) +{ + if (PLL_REG_GET_BYPASS(reg)) + return 1; + + return PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg); +} + +static inline u32 axs10x_encode_div(unsigned int id, int upd) +{ + u32 div = 0; + + PLL_REG_SET_LOW(div, (id % 2 == 0) ? id >> 1 : (id >> 1) + 1); + PLL_REG_SET_HIGH(div, id >> 1); + PLL_REG_SET_EDGE(div, id % 2); + PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0); + PLL_REG_SET_NOUPD(div, upd == 0 ? 1 : 0); + + return div; +} + +static unsigned long axs10x_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u64 rate; + u32 idiv, fbdiv, odiv; + struct axs10x_pll_clk *clk = to_axs10x_pll_clk(hw); + + idiv = axs10x_div_get_value(axs10x_pll_read(clk, PLL_REG_IDIV)); + fbdiv = axs10x_div_get_value(axs10x_pll_read(clk, PLL_REG_FBDIV)); + odiv = axs10x_div_get_value(axs10x_pll_read(clk, PLL_REG_ODIV)); + + rate = (u64)parent_rate * fbdiv; + do_div(rate, idiv * odiv); + + return rate; +} + +static long axs10x_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i; + long best_rate; + struct axs10x_pll_clk *clk = to_axs10x_pll_clk(hw); + const struct axs10x_pll_cfg *pll_cfg = clk->pll_cfg; + + if (pll_cfg[0].rate == 0) + return -EINVAL; + + best_rate = pll_cfg[0].rate; + + for (i = 1; pll_cfg[i].rate != 0; i++) { + if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate)) + best_rate = pll_cfg[i].rate; + } + + return best_rate; +} + +static int axs10x_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int i; + struct axs10x_pll_clk *clk = to_axs10x_pll_clk(hw); + const struct axs10x_pll_cfg *pll_cfg = clk->pll_cfg; + + for (i = 0; pll_cfg[i].rate != 0; i++) { + if (pll_cfg[i].rate == rate) { + axs10x_pll_write(clk, PLL_REG_IDIV, + axs10x_encode_div(pll_cfg[i].idiv, 0)); + axs10x_pll_write(clk, PLL_REG_FBDIV, + axs10x_encode_div(pll_cfg[i].fbdiv, 0)); + axs10x_pll_write(clk, PLL_REG_ODIV, + axs10x_encode_div(pll_cfg[i].odiv, 1)); + + /* + * Wait until CGU relocks and check error status. + * If after timeout CGU is unlocked yet return error + */ + udelay(PLL_MAX_LOCK_TIME); + if (!(ioread32(clk->lock) & PLL_LOCK)) + return -ETIMEDOUT; + + if (ioread32(clk->lock) & PLL_ERROR) + return -EINVAL; + + return 0; + } + } + + dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate, + parent_rate); + return -EINVAL; +} + +static const struct clk_ops axs10x_pll_ops = { + .recalc_rate = axs10x_pll_recalc_rate, + .round_rate = axs10x_pll_round_rate, + .set_rate = axs10x_pll_set_rate, +}; + +static int axs10x_pll_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const char *parent_name; + struct axs10x_pll_clk *pll_clk; + struct resource *mem; + struct clk_init_data init = { }; + int ret; + + pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); + if (!pll_clk) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pll_clk->base = devm_ioremap_resource(dev, mem); + if (IS_ERR(pll_clk->base)) + return PTR_ERR(pll_clk->base); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pll_clk->lock = devm_ioremap_resource(dev, mem); + if (IS_ERR(pll_clk->lock)) + return PTR_ERR(pll_clk->lock); + + init.name = dev->of_node->name; + init.ops = &axs10x_pll_ops; + parent_name = of_clk_get_parent_name(dev->of_node, 0); + init.parent_names = &parent_name; + init.num_parents = 1; + pll_clk->hw.init = &init; + pll_clk->dev = dev; + pll_clk->pll_cfg = of_device_get_match_data(dev); + + if (!pll_clk->pll_cfg) { + dev_err(dev, "No OF match data provided\n"); + return -EINVAL; + } + + ret = devm_clk_hw_register(dev, &pll_clk->hw); + if (ret) { + dev_err(dev, "failed to register %s clock\n", init.name); + return ret; + } + + return of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get, + &pll_clk->hw); +} + +static int axs10x_pll_clk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + return 0; +} + +static void __init of_axs10x_pll_clk_setup(struct device_node *node) +{ + const char *parent_name; + struct axs10x_pll_clk *pll_clk; + struct clk_init_data init = { }; + int ret; + + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (!pll_clk) + return; + + pll_clk->base = of_iomap(node, 0); + if (!pll_clk->base) { + pr_err("failed to map pll div registers\n"); + goto err_free_pll_clk; + } + + pll_clk->lock = of_iomap(node, 1); + if (!pll_clk->lock) { + pr_err("failed to map pll lock register\n"); + goto err_unmap_base; + } + + init.name = node->name; + init.ops = &axs10x_pll_ops; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = &parent_name; + init.num_parents = parent_name ? 1 : 0; + pll_clk->hw.init = &init; + pll_clk->pll_cfg = arc_pll_cfg; + + ret = clk_hw_register(NULL, &pll_clk->hw); + if (ret) { + pr_err("failed to register %s clock\n", node->name); + goto err_unmap_lock; + } + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &pll_clk->hw); + if (ret) { + pr_err("failed to add hw provider for %s clock\n", node->name); + goto err_unregister_clk; + } + + return; + +err_unregister_clk: + clk_hw_unregister(&pll_clk->hw); +err_unmap_lock: + iounmap(pll_clk->lock); +err_unmap_base: + iounmap(pll_clk->base); +err_free_pll_clk: + kfree(pll_clk); +} +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", + of_axs10x_pll_clk_setup); + +static const struct of_device_id axs10x_pll_clk_id[] = { + { .compatible = "snps,axs10x-pgu-pll-clock", .data = &pgu_pll_cfg}, + { } +}; +MODULE_DEVICE_TABLE(of, axs10x_pll_clk_id); + +static struct platform_driver axs10x_pll_clk_driver = { + .driver = { + .name = "axs10x-pll-clock", + .of_match_table = axs10x_pll_clk_id, + }, + .probe = axs10x_pll_clk_probe, + .remove = axs10x_pll_clk_remove, +}; +builtin_platform_driver(axs10x_pll_clk_driver); + +MODULE_AUTHOR("Vlad Zakharov <vzakhar@synopsys.com>"); +MODULE_DESCRIPTION("Synopsys AXS10X SDP Generic PLL Clock Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/berlin/bg2.c b/drivers/clk/berlin/bg2.c index 1d99292e2039..e7331ace0337 100644 --- a/drivers/clk/berlin/bg2.c +++ b/drivers/clk/berlin/bg2.c @@ -679,8 +679,7 @@ static void __init berlin2_clock_setup(struct device_node *np) if (!IS_ERR(hws[n])) continue; - pr_err("%s: Unable to register leaf clock %d\n", - np->full_name, n); + pr_err("%pOF: Unable to register leaf clock %d\n", np, n); goto bg2_fail; } diff --git a/drivers/clk/berlin/bg2q.c b/drivers/clk/berlin/bg2q.c index 3b784b593afd..67c270b143f7 100644 --- a/drivers/clk/berlin/bg2q.c +++ b/drivers/clk/berlin/bg2q.c @@ -304,14 +304,14 @@ static void __init berlin2q_clock_setup(struct device_node *np) gbase = of_iomap(parent_np, 0); if (!gbase) { - pr_err("%s: Unable to map global base\n", np->full_name); + pr_err("%pOF: Unable to map global base\n", np); return; } /* BG2Q CPU PLL is not part of global registers */ cpupll_base = of_iomap(parent_np, 1); if (!cpupll_base) { - pr_err("%s: Unable to map cpupll base\n", np->full_name); + pr_err("%pOF: Unable to map cpupll base\n", np); iounmap(gbase); return; } @@ -376,8 +376,7 @@ static void __init berlin2q_clock_setup(struct device_node *np) if (!IS_ERR(hws[n])) continue; - pr_err("%s: Unable to register leaf clock %d\n", - np->full_name, n); + pr_err("%pOF: Unable to register leaf clock %d\n", np, n); goto bg2q_fail; } diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c index ea8568536193..bf0582cbbf38 100644 --- a/drivers/clk/clk-asm9260.c +++ b/drivers/clk/clk-asm9260.c @@ -338,8 +338,8 @@ static void __init asm9260_acc_init(struct device_node *np) if (!IS_ERR(hws[n])) continue; - pr_err("%s: Unable to register leaf clock %d\n", - np->full_name, n); + pr_err("%pOF: Unable to register leaf clock %d\n", + np, n); goto fail; } diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c index 7ec36722f8ab..49819b546134 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -23,8 +23,8 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier) num_parents = of_count_phandle_with_args(node, "assigned-clock-parents", "#clock-cells"); if (num_parents == -EINVAL) - pr_err("clk: invalid value of clock-parents property at %s\n", - node->full_name); + pr_err("clk: invalid value of clock-parents property at %pOF\n", + node); for (index = 0; index < num_parents; index++) { rc = of_parse_phandle_with_args(node, "assigned-clock-parents", @@ -41,8 +41,8 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier) pclk = of_clk_get_from_provider(&clkspec); if (IS_ERR(pclk)) { if (PTR_ERR(pclk) != -EPROBE_DEFER) - pr_warn("clk: couldn't get parent clock %d for %s\n", - index, node->full_name); + pr_warn("clk: couldn't get parent clock %d for %pOF\n", + index, node); return PTR_ERR(pclk); } @@ -57,8 +57,8 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier) clk = of_clk_get_from_provider(&clkspec); if (IS_ERR(clk)) { if (PTR_ERR(clk) != -EPROBE_DEFER) - pr_warn("clk: couldn't get assigned clock %d for %s\n", - index, node->full_name); + pr_warn("clk: couldn't get assigned clock %d for %pOF\n", + index, node); rc = PTR_ERR(clk); goto err; } @@ -102,8 +102,8 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) clk = of_clk_get_from_provider(&clkspec); if (IS_ERR(clk)) { if (PTR_ERR(clk) != -EPROBE_DEFER) - pr_warn("clk: couldn't get clock %d for %s\n", - index, node->full_name); + pr_warn("clk: couldn't get clock %d for %pOF\n", + index, node); return PTR_ERR(clk); } diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c index c54baede4d68..e8ea81c30f0c 100644 --- a/drivers/clk/clk-cs2000-cp.c +++ b/drivers/clk/clk-cs2000-cp.c @@ -343,6 +343,15 @@ static int cs2000_set_rate(struct clk_hw *hw, return __cs2000_set_rate(priv, ch, rate, parent_rate); } +static int cs2000_set_saved_rate(struct cs2000_priv *priv) +{ + int ch = 0; /* it uses ch0 only at this point */ + + return __cs2000_set_rate(priv, ch, + priv->saved_rate, + priv->saved_parent_rate); +} + static int cs2000_enable(struct clk_hw *hw) { struct cs2000_priv *priv = hw_to_priv(hw); @@ -535,11 +544,8 @@ probe_err: static int cs2000_resume(struct device *dev) { struct cs2000_priv *priv = dev_get_drvdata(dev); - int ch = 0; /* it uses ch0 only at this point */ - return __cs2000_set_rate(priv, ch, - priv->saved_rate, - priv->saved_parent_rate); + return cs2000_set_saved_rate(priv); } static const struct dev_pm_ops cs2000_pm_ops = { diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 9bb472cccca6..4ed516cb7276 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -385,12 +385,14 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_divider *divider = to_clk_divider(hw); - unsigned int value; + int value; unsigned long flags = 0; u32 val; value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags); + if (value < 0) + return value; if (divider->lock) spin_lock_irqsave(divider->lock, flags); @@ -403,7 +405,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, val = clk_readl(divider->reg); val &= ~(div_mask(divider->width) << divider->shift); } - val |= value << divider->shift; + val |= (u32)value << divider->shift; clk_writel(val, divider->reg); if (divider->lock) diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index aab904618eb6..fdf625fb10fa 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -49,16 +49,12 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, return ret; } -static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static void clk_fd_general_approximation(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate, + unsigned long *m, unsigned long *n) { struct clk_fractional_divider *fd = to_clk_fd(hw); unsigned long scale; - unsigned long m, n; - u64 ret; - - if (!rate || rate >= *parent_rate) - return *parent_rate; /* * Get rate closer to *parent_rate to guarantee there is no overflow @@ -71,7 +67,23 @@ static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, rational_best_approximation(rate, *parent_rate, GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), - &m, &n); + m, n); +} + +static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_fractional_divider *fd = to_clk_fd(hw); + unsigned long m, n; + u64 ret; + + if (!rate || rate >= *parent_rate) + return *parent_rate; + + if (fd->approximation) + fd->approximation(hw, rate, parent_rate, &m, &n); + else + clk_fd_general_approximation(hw, rate, parent_rate, &m, &n); ret = (u64)*parent_rate * m; do_div(ret, n); diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 4e0c054a787c..dd82485e09a1 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -86,7 +86,7 @@ static void clk_gate_disable(struct clk_hw *hw) clk_gate_endisable(hw, 0); } -static int clk_gate_is_enabled(struct clk_hw *hw) +int clk_gate_is_enabled(struct clk_hw *hw) { u32 reg; struct clk_gate *gate = to_clk_gate(hw); @@ -101,6 +101,7 @@ static int clk_gate_is_enabled(struct clk_hw *hw) return reg ? 1 : 0; } +EXPORT_SYMBOL_GPL(clk_gate_is_enabled); const struct clk_ops clk_gate_ops = { .enable = clk_gate_enable, diff --git a/drivers/clk/clk-gemini.c b/drivers/clk/clk-gemini.c index b4cf2f699a21..f940e5af845b 100644 --- a/drivers/clk/clk-gemini.c +++ b/drivers/clk/clk-gemini.c @@ -37,7 +37,6 @@ static DEFINE_SPINLOCK(gemini_clk_lock); #define GEMINI_GLOBAL_MISC_CONTROL 0x30 #define PCI_CLK_66MHZ BIT(18) -#define PCI_CLK_OE BIT(17) #define GEMINI_GLOBAL_CLOCK_CONTROL 0x34 #define PCI_CLKRUN_EN BIT(16) @@ -159,9 +158,6 @@ static int gemini_pci_enable(struct clk_hw *hw) regmap_update_bits(pciclk->map, GEMINI_GLOBAL_CLOCK_CONTROL, 0, PCI_CLKRUN_EN); - regmap_update_bits(pciclk->map, - GEMINI_GLOBAL_MISC_CONTROL, - 0, PCI_CLK_OE); return 0; } @@ -169,9 +165,6 @@ static void gemini_pci_disable(struct clk_hw *hw) { struct clk_gemini_pci *pciclk = to_pciclk(hw); - regmap_update_bits(pciclk->map, - GEMINI_GLOBAL_MISC_CONTROL, - PCI_CLK_OE, 0); regmap_update_bits(pciclk->map, GEMINI_GLOBAL_CLOCK_CONTROL, PCI_CLKRUN_EN, 0); } diff --git a/drivers/clk/clk-hsdk-pll.c b/drivers/clk/clk-hsdk-pll.c new file mode 100644 index 000000000000..bbf237173b37 --- /dev/null +++ b/drivers/clk/clk-hsdk-pll.c @@ -0,0 +1,431 @@ +/* + * Synopsys HSDK SDP Generic PLL clock driver + * + * Copyright (C) 2017 Synopsys + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define CGU_PLL_CTRL 0x000 /* ARC PLL control register */ +#define CGU_PLL_STATUS 0x004 /* ARC PLL status register */ +#define CGU_PLL_FMEAS 0x008 /* ARC PLL frequency measurement register */ +#define CGU_PLL_MON 0x00C /* ARC PLL monitor register */ + +#define CGU_PLL_CTRL_ODIV_SHIFT 2 +#define CGU_PLL_CTRL_IDIV_SHIFT 4 +#define CGU_PLL_CTRL_FBDIV_SHIFT 9 +#define CGU_PLL_CTRL_BAND_SHIFT 20 + +#define CGU_PLL_CTRL_ODIV_MASK GENMASK(3, CGU_PLL_CTRL_ODIV_SHIFT) +#define CGU_PLL_CTRL_IDIV_MASK GENMASK(8, CGU_PLL_CTRL_IDIV_SHIFT) +#define CGU_PLL_CTRL_FBDIV_MASK GENMASK(15, CGU_PLL_CTRL_FBDIV_SHIFT) + +#define CGU_PLL_CTRL_PD BIT(0) +#define CGU_PLL_CTRL_BYPASS BIT(1) + +#define CGU_PLL_STATUS_LOCK BIT(0) +#define CGU_PLL_STATUS_ERR BIT(1) + +#define HSDK_PLL_MAX_LOCK_TIME 100 /* 100 us */ + +#define CGU_PLL_SOURCE_MAX 1 + +#define CORE_IF_CLK_THRESHOLD_HZ 500000000 +#define CREG_CORE_IF_CLK_DIV_1 0x0 +#define CREG_CORE_IF_CLK_DIV_2 0x1 + +struct hsdk_pll_cfg { + u32 rate; + u32 idiv; + u32 fbdiv; + u32 odiv; + u32 band; +}; + +static const struct hsdk_pll_cfg asdt_pll_cfg[] = { + { 100000000, 0, 11, 3, 0 }, + { 133000000, 0, 15, 3, 0 }, + { 200000000, 1, 47, 3, 0 }, + { 233000000, 1, 27, 2, 0 }, + { 300000000, 1, 35, 2, 0 }, + { 333000000, 1, 39, 2, 0 }, + { 400000000, 1, 47, 2, 0 }, + { 500000000, 0, 14, 1, 0 }, + { 600000000, 0, 17, 1, 0 }, + { 700000000, 0, 20, 1, 0 }, + { 800000000, 0, 23, 1, 0 }, + { 900000000, 1, 26, 0, 0 }, + { 1000000000, 1, 29, 0, 0 }, + { 1100000000, 1, 32, 0, 0 }, + { 1200000000, 1, 35, 0, 0 }, + { 1300000000, 1, 38, 0, 0 }, + { 1400000000, 1, 41, 0, 0 }, + { 1500000000, 1, 44, 0, 0 }, + { 1600000000, 1, 47, 0, 0 }, + {} +}; + +static const struct hsdk_pll_cfg hdmi_pll_cfg[] = { + { 297000000, 0, 21, 2, 0 }, + { 540000000, 0, 19, 1, 0 }, + { 594000000, 0, 21, 1, 0 }, + {} +}; + +struct hsdk_pll_clk { + struct clk_hw hw; + void __iomem *regs; + void __iomem *spec_regs; + const struct hsdk_pll_devdata *pll_devdata; + struct device *dev; +}; + +struct hsdk_pll_devdata { + const struct hsdk_pll_cfg *pll_cfg; + int (*update_rate)(struct hsdk_pll_clk *clk, unsigned long rate, + const struct hsdk_pll_cfg *cfg); +}; + +static int hsdk_pll_core_update_rate(struct hsdk_pll_clk *, unsigned long, + const struct hsdk_pll_cfg *); +static int hsdk_pll_comm_update_rate(struct hsdk_pll_clk *, unsigned long, + const struct hsdk_pll_cfg *); + +static const struct hsdk_pll_devdata core_pll_devdata = { + .pll_cfg = asdt_pll_cfg, + .update_rate = hsdk_pll_core_update_rate, +}; + +static const struct hsdk_pll_devdata sdt_pll_devdata = { + .pll_cfg = asdt_pll_cfg, + .update_rate = hsdk_pll_comm_update_rate, +}; + +static const struct hsdk_pll_devdata hdmi_pll_devdata = { + .pll_cfg = hdmi_pll_cfg, + .update_rate = hsdk_pll_comm_update_rate, +}; + +static inline void hsdk_pll_write(struct hsdk_pll_clk *clk, u32 reg, u32 val) +{ + iowrite32(val, clk->regs + reg); +} + +static inline u32 hsdk_pll_read(struct hsdk_pll_clk *clk, u32 reg) +{ + return ioread32(clk->regs + reg); +} + +static inline void hsdk_pll_set_cfg(struct hsdk_pll_clk *clk, + const struct hsdk_pll_cfg *cfg) +{ + u32 val = 0; + + /* Powerdown and Bypass bits should be cleared */ + val |= cfg->idiv << CGU_PLL_CTRL_IDIV_SHIFT; + val |= cfg->fbdiv << CGU_PLL_CTRL_FBDIV_SHIFT; + val |= cfg->odiv << CGU_PLL_CTRL_ODIV_SHIFT; + val |= cfg->band << CGU_PLL_CTRL_BAND_SHIFT; + + dev_dbg(clk->dev, "write configurarion: %#x\n", val); + + hsdk_pll_write(clk, CGU_PLL_CTRL, val); +} + +static inline bool hsdk_pll_is_locked(struct hsdk_pll_clk *clk) +{ + return !!(hsdk_pll_read(clk, CGU_PLL_STATUS) & CGU_PLL_STATUS_LOCK); +} + +static inline bool hsdk_pll_is_err(struct hsdk_pll_clk *clk) +{ + return !!(hsdk_pll_read(clk, CGU_PLL_STATUS) & CGU_PLL_STATUS_ERR); +} + +static inline struct hsdk_pll_clk *to_hsdk_pll_clk(struct clk_hw *hw) +{ + return container_of(hw, struct hsdk_pll_clk, hw); +} + +static unsigned long hsdk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 val; + u64 rate; + u32 idiv, fbdiv, odiv; + struct hsdk_pll_clk *clk = to_hsdk_pll_clk(hw); + + val = hsdk_pll_read(clk, CGU_PLL_CTRL); + + dev_dbg(clk->dev, "current configurarion: %#x\n", val); + + /* Check if PLL is disabled */ + if (val & CGU_PLL_CTRL_PD) + return 0; + + /* Check if PLL is bypassed */ + if (val & CGU_PLL_CTRL_BYPASS) + return parent_rate; + + /* input divider = reg.idiv + 1 */ + idiv = 1 + ((val & CGU_PLL_CTRL_IDIV_MASK) >> CGU_PLL_CTRL_IDIV_SHIFT); + /* fb divider = 2*(reg.fbdiv + 1) */ + fbdiv = 2 * (1 + ((val & CGU_PLL_CTRL_FBDIV_MASK) >> CGU_PLL_CTRL_FBDIV_SHIFT)); + /* output divider = 2^(reg.odiv) */ + odiv = 1 << ((val & CGU_PLL_CTRL_ODIV_MASK) >> CGU_PLL_CTRL_ODIV_SHIFT); + + rate = (u64)parent_rate * fbdiv; + do_div(rate, idiv * odiv); + + return rate; +} + +static long hsdk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i; + unsigned long best_rate; + struct hsdk_pll_clk *clk = to_hsdk_pll_clk(hw); + const struct hsdk_pll_cfg *pll_cfg = clk->pll_devdata->pll_cfg; + + if (pll_cfg[0].rate == 0) + return -EINVAL; + + best_rate = pll_cfg[0].rate; + + for (i = 1; pll_cfg[i].rate != 0; i++) { + if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate)) + best_rate = pll_cfg[i].rate; + } + + dev_dbg(clk->dev, "chosen best rate: %lu\n", best_rate); + + return best_rate; +} + +static int hsdk_pll_comm_update_rate(struct hsdk_pll_clk *clk, + unsigned long rate, + const struct hsdk_pll_cfg *cfg) +{ + hsdk_pll_set_cfg(clk, cfg); + + /* + * Wait until CGU relocks and check error status. + * If after timeout CGU is unlocked yet return error. + */ + udelay(HSDK_PLL_MAX_LOCK_TIME); + if (!hsdk_pll_is_locked(clk)) + return -ETIMEDOUT; + + if (hsdk_pll_is_err(clk)) + return -EINVAL; + + return 0; +} + +static int hsdk_pll_core_update_rate(struct hsdk_pll_clk *clk, + unsigned long rate, + const struct hsdk_pll_cfg *cfg) +{ + /* + * When core clock exceeds 500MHz, the divider for the interface + * clock must be programmed to div-by-2. + */ + if (rate > CORE_IF_CLK_THRESHOLD_HZ) + iowrite32(CREG_CORE_IF_CLK_DIV_2, clk->spec_regs); + + hsdk_pll_set_cfg(clk, cfg); + + /* + * Wait until CGU relocks and check error status. + * If after timeout CGU is unlocked yet return error. + */ + udelay(HSDK_PLL_MAX_LOCK_TIME); + if (!hsdk_pll_is_locked(clk)) + return -ETIMEDOUT; + + if (hsdk_pll_is_err(clk)) + return -EINVAL; + + /* + * Program divider to div-by-1 if we succesfuly set core clock below + * 500MHz threshold. + */ + if (rate <= CORE_IF_CLK_THRESHOLD_HZ) + iowrite32(CREG_CORE_IF_CLK_DIV_1, clk->spec_regs); + + return 0; +} + +static int hsdk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int i; + struct hsdk_pll_clk *clk = to_hsdk_pll_clk(hw); + const struct hsdk_pll_cfg *pll_cfg = clk->pll_devdata->pll_cfg; + + for (i = 0; pll_cfg[i].rate != 0; i++) { + if (pll_cfg[i].rate == rate) { + return clk->pll_devdata->update_rate(clk, rate, + &pll_cfg[i]); + } + } + + dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate, + parent_rate); + + return -EINVAL; +} + +static const struct clk_ops hsdk_pll_ops = { + .recalc_rate = hsdk_pll_recalc_rate, + .round_rate = hsdk_pll_round_rate, + .set_rate = hsdk_pll_set_rate, +}; + +static int hsdk_pll_clk_probe(struct platform_device *pdev) +{ + int ret; + struct resource *mem; + const char *parent_name; + unsigned int num_parents; + struct hsdk_pll_clk *pll_clk; + struct clk_init_data init = { }; + struct device *dev = &pdev->dev; + + pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); + if (!pll_clk) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pll_clk->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(pll_clk->regs)) + return PTR_ERR(pll_clk->regs); + + init.name = dev->of_node->name; + init.ops = &hsdk_pll_ops; + parent_name = of_clk_get_parent_name(dev->of_node, 0); + init.parent_names = &parent_name; + num_parents = of_clk_get_parent_count(dev->of_node); + if (num_parents == 0 || num_parents > CGU_PLL_SOURCE_MAX) { + dev_err(dev, "wrong clock parents number: %u\n", num_parents); + return -EINVAL; + } + init.num_parents = num_parents; + + pll_clk->hw.init = &init; + pll_clk->dev = dev; + pll_clk->pll_devdata = of_device_get_match_data(dev); + + if (!pll_clk->pll_devdata) { + dev_err(dev, "No OF match data provided\n"); + return -EINVAL; + } + + ret = devm_clk_hw_register(dev, &pll_clk->hw); + if (ret) { + dev_err(dev, "failed to register %s clock\n", init.name); + return ret; + } + + return of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get, + &pll_clk->hw); +} + +static int hsdk_pll_clk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + return 0; +} + +static void __init of_hsdk_pll_clk_setup(struct device_node *node) +{ + int ret; + const char *parent_name; + unsigned int num_parents; + struct hsdk_pll_clk *pll_clk; + struct clk_init_data init = { }; + + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (!pll_clk) + return; + + pll_clk->regs = of_iomap(node, 0); + if (!pll_clk->regs) { + pr_err("failed to map pll registers\n"); + goto err_free_pll_clk; + } + + pll_clk->spec_regs = of_iomap(node, 1); + if (!pll_clk->spec_regs) { + pr_err("failed to map pll registers\n"); + goto err_unmap_comm_regs; + } + + init.name = node->name; + init.ops = &hsdk_pll_ops; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = &parent_name; + num_parents = of_clk_get_parent_count(node); + if (num_parents > CGU_PLL_SOURCE_MAX) { + pr_err("too much clock parents: %u\n", num_parents); + goto err_unmap_spec_regs; + } + init.num_parents = num_parents; + + pll_clk->hw.init = &init; + pll_clk->pll_devdata = &core_pll_devdata; + + ret = clk_hw_register(NULL, &pll_clk->hw); + if (ret) { + pr_err("failed to register %s clock\n", node->name); + goto err_unmap_spec_regs; + } + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &pll_clk->hw); + if (ret) { + pr_err("failed to add hw provider for %s clock\n", node->name); + goto err_unmap_spec_regs; + } + + return; + +err_unmap_spec_regs: + iounmap(pll_clk->spec_regs); +err_unmap_comm_regs: + iounmap(pll_clk->regs); +err_free_pll_clk: + kfree(pll_clk); +} + +/* Core PLL needed early for ARC cpus timers */ +CLK_OF_DECLARE(hsdk_pll_clock, "snps,hsdk-core-pll-clock", +of_hsdk_pll_clk_setup); + +static const struct of_device_id hsdk_pll_clk_id[] = { + { .compatible = "snps,hsdk-gp-pll-clock", .data = &sdt_pll_devdata}, + { .compatible = "snps,hsdk-hdmi-pll-clock", .data = &hdmi_pll_devdata}, + { } +}; + +static struct platform_driver hsdk_pll_clk_driver = { + .driver = { + .name = "hsdk-gp-pll-clock", + .of_match_table = hsdk_pll_clk_id, + }, + .probe = hsdk_pll_clk_probe, + .remove = hsdk_pll_clk_remove, +}; +builtin_platform_driver(hsdk_pll_clk_driver); diff --git a/drivers/clk/clk-mb86s7x.c b/drivers/clk/clk-mb86s7x.c deleted file mode 100644 index 2a83a3ff1d09..000000000000 --- a/drivers/clk/clk-mb86s7x.c +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright (C) 2013-2015 FUJITSU SEMICONDUCTOR LIMITED - * Copyright (C) 2015 Linaro Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/clkdev.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/cpu.h> -#include <linux/clk-provider.h> -#include <linux/spinlock.h> -#include <linux/module.h> -#include <linux/topology.h> -#include <linux/mailbox_client.h> -#include <linux/platform_device.h> - -#include <soc/mb86s7x/scb_mhu.h> - -#define to_crg_clk(p) container_of(p, struct crg_clk, hw) -#define to_clc_clk(p) container_of(p, struct cl_clk, hw) - -struct mb86s7x_peri_clk { - u32 payload_size; - u32 cntrlr; - u32 domain; - u32 port; - u32 en; - u64 frequency; -} __packed __aligned(4); - -struct hack_rate { - unsigned clk_id; - unsigned long rate; - int gated; -}; - -struct crg_clk { - struct clk_hw hw; - u8 cntrlr, domain, port; -}; - -static int crg_gate_control(struct clk_hw *hw, int en) -{ - struct crg_clk *crgclk = to_crg_clk(hw); - struct mb86s7x_peri_clk cmd; - int ret; - - cmd.payload_size = sizeof(cmd); - cmd.cntrlr = crgclk->cntrlr; - cmd.domain = crgclk->domain; - cmd.port = crgclk->port; - cmd.en = en; - - /* Port is UngatedCLK */ - if (cmd.port == 8) - return en ? 0 : -EINVAL; - - pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u En-%u}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port, cmd.en); - - ret = mb86s7x_send_packet(CMD_PERI_CLOCK_GATE_SET_REQ, - &cmd, sizeof(cmd)); - if (ret < 0) { - pr_err("%s:%d failed!\n", __func__, __LINE__); - return ret; - } - - pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u En-%u}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port, cmd.en); - - /* If the request was rejected */ - if (cmd.en != en) - ret = -EINVAL; - else - ret = 0; - - return ret; -} - -static int crg_port_prepare(struct clk_hw *hw) -{ - return crg_gate_control(hw, 1); -} - -static void crg_port_unprepare(struct clk_hw *hw) -{ - crg_gate_control(hw, 0); -} - -static int -crg_rate_control(struct clk_hw *hw, int set, unsigned long *rate) -{ - struct crg_clk *crgclk = to_crg_clk(hw); - struct mb86s7x_peri_clk cmd; - int code, ret; - - cmd.payload_size = sizeof(cmd); - cmd.cntrlr = crgclk->cntrlr; - cmd.domain = crgclk->domain; - cmd.port = crgclk->port; - cmd.frequency = *rate; - - if (set) { - code = CMD_PERI_CLOCK_RATE_SET_REQ; - pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u Rate-SET %lluHz}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port, cmd.frequency); - } else { - code = CMD_PERI_CLOCK_RATE_GET_REQ; - pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u Rate-GET}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port); - } - - ret = mb86s7x_send_packet(code, &cmd, sizeof(cmd)); - if (ret < 0) { - pr_err("%s:%d failed!\n", __func__, __LINE__); - return ret; - } - - if (set) - pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u Rate-SET %lluHz}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port, cmd.frequency); - else - pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u Rate-GOT %lluHz}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port, cmd.frequency); - - *rate = cmd.frequency; - return 0; -} - -static unsigned long -crg_port_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) -{ - unsigned long rate; - - crg_rate_control(hw, 0, &rate); - - return rate; -} - -static long -crg_port_round_rate(struct clk_hw *hw, - unsigned long rate, unsigned long *pr) -{ - return rate; -} - -static int -crg_port_set_rate(struct clk_hw *hw, - unsigned long rate, unsigned long parent_rate) -{ - return crg_rate_control(hw, 1, &rate); -} - -const struct clk_ops crg_port_ops = { - .prepare = crg_port_prepare, - .unprepare = crg_port_unprepare, - .recalc_rate = crg_port_recalc_rate, - .round_rate = crg_port_round_rate, - .set_rate = crg_port_set_rate, -}; - -struct mb86s70_crg11 { - struct mutex lock; /* protects CLK populating and searching */ -}; - -static struct clk *crg11_get(struct of_phandle_args *clkspec, void *data) -{ - struct mb86s70_crg11 *crg11 = data; - struct clk_init_data init; - u32 cntrlr, domain, port; - struct crg_clk *crgclk; - struct clk *clk; - char clkp[20]; - - if (clkspec->args_count != 3) - return ERR_PTR(-EINVAL); - - cntrlr = clkspec->args[0]; - domain = clkspec->args[1]; - port = clkspec->args[2]; - - if (port > 7) - snprintf(clkp, 20, "UngatedCLK%d_%X", cntrlr, domain); - else - snprintf(clkp, 20, "CLK%d_%X_%d", cntrlr, domain, port); - - mutex_lock(&crg11->lock); - - clk = __clk_lookup(clkp); - if (clk) { - mutex_unlock(&crg11->lock); - return clk; - } - - crgclk = kzalloc(sizeof(*crgclk), GFP_KERNEL); - if (!crgclk) { - mutex_unlock(&crg11->lock); - return ERR_PTR(-ENOMEM); - } - - init.name = clkp; - init.num_parents = 0; - init.ops = &crg_port_ops; - init.flags = 0; - crgclk->hw.init = &init; - crgclk->cntrlr = cntrlr; - crgclk->domain = domain; - crgclk->port = port; - clk = clk_register(NULL, &crgclk->hw); - if (IS_ERR(clk)) - pr_err("%s:%d Error!\n", __func__, __LINE__); - else - pr_debug("Registered %s\n", clkp); - - clk_register_clkdev(clk, clkp, NULL); - mutex_unlock(&crg11->lock); - return clk; -} - -static void __init crg_port_init(struct device_node *node) -{ - struct mb86s70_crg11 *crg11; - - crg11 = kzalloc(sizeof(*crg11), GFP_KERNEL); - if (!crg11) - return; - - mutex_init(&crg11->lock); - - of_clk_add_provider(node, crg11_get, crg11); -} -CLK_OF_DECLARE(crg11_gate, "fujitsu,mb86s70-crg11", crg_port_init); - -struct cl_clk { - struct clk_hw hw; - int cluster; -}; - -struct mb86s7x_cpu_freq { - u32 payload_size; - u32 cluster_class; - u32 cluster_id; - u32 cpu_id; - u64 frequency; -}; - -static void mhu_cluster_rate(struct clk_hw *hw, unsigned long *rate, int get) -{ - struct cl_clk *clc = to_clc_clk(hw); - struct mb86s7x_cpu_freq cmd; - int code, ret; - - cmd.payload_size = sizeof(cmd); - cmd.cluster_class = 0; - cmd.cluster_id = clc->cluster; - cmd.cpu_id = 0; - cmd.frequency = *rate; - - if (get) - code = CMD_CPU_CLOCK_RATE_GET_REQ; - else - code = CMD_CPU_CLOCK_RATE_SET_REQ; - - pr_debug("%s:%d CMD Cl_Class-%u CL_ID-%u CPU_ID-%u Freq-%llu}\n", - __func__, __LINE__, cmd.cluster_class, - cmd.cluster_id, cmd.cpu_id, cmd.frequency); - - ret = mb86s7x_send_packet(code, &cmd, sizeof(cmd)); - if (ret < 0) { - pr_err("%s:%d failed!\n", __func__, __LINE__); - return; - } - - pr_debug("%s:%d REP Cl_Class-%u CL_ID-%u CPU_ID-%u Freq-%llu}\n", - __func__, __LINE__, cmd.cluster_class, - cmd.cluster_id, cmd.cpu_id, cmd.frequency); - - *rate = cmd.frequency; -} - -static unsigned long -clc_recalc_rate(struct clk_hw *hw, unsigned long unused) -{ - unsigned long rate; - - mhu_cluster_rate(hw, &rate, 1); - return rate; -} - -static long -clc_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *unused) -{ - return rate; -} - -static int -clc_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long unused) -{ - unsigned long res = rate; - - mhu_cluster_rate(hw, &res, 0); - - return (res == rate) ? 0 : -EINVAL; -} - -static struct clk_ops clk_clc_ops = { - .recalc_rate = clc_recalc_rate, - .round_rate = clc_round_rate, - .set_rate = clc_set_rate, -}; - -static struct clk_hw *mb86s7x_clclk_register(struct device *cpu_dev) -{ - struct clk_init_data init; - struct cl_clk *clc; - int ret; - - clc = kzalloc(sizeof(*clc), GFP_KERNEL); - if (!clc) - return ERR_PTR(-ENOMEM); - - clc->hw.init = &init; - clc->cluster = topology_physical_package_id(cpu_dev->id); - - init.name = dev_name(cpu_dev); - init.ops = &clk_clc_ops; - init.flags = CLK_GET_RATE_NOCACHE; - init.num_parents = 0; - - ret = devm_clk_hw_register(cpu_dev, &clc->hw); - if (ret) - return ERR_PTR(ret); - return &clc->hw; -} - -static int mb86s7x_clclk_of_init(void) -{ - int cpu, ret = -ENODEV; - struct device_node *np; - struct clk_hw *hw; - - np = of_find_compatible_node(NULL, NULL, "fujitsu,mb86s70-scb-1.0"); - if (!np || !of_device_is_available(np)) - goto exit; - - for_each_possible_cpu(cpu) { - struct device *cpu_dev = get_cpu_device(cpu); - - if (!cpu_dev) { - pr_err("failed to get cpu%d device\n", cpu); - continue; - } - - hw = mb86s7x_clclk_register(cpu_dev); - if (IS_ERR(hw)) { - pr_err("failed to register cpu%d clock\n", cpu); - continue; - } - if (clk_hw_register_clkdev(hw, NULL, dev_name(cpu_dev))) { - pr_err("failed to register cpu%d clock lookup\n", cpu); - continue; - } - pr_debug("registered clk for %s\n", dev_name(cpu_dev)); - } - ret = 0; - - platform_device_register_simple("arm-bL-cpufreq-dt", -1, NULL, 0); -exit: - of_node_put(np); - return ret; -} -module_init(mb86s7x_clclk_of_init); diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c index b86dac851116..58428d0043fd 100644 --- a/drivers/clk/clk-moxart.c +++ b/drivers/clk/clk-moxart.c @@ -18,7 +18,7 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) { - static void __iomem *base; + void __iomem *base; struct clk_hw *hw; struct clk *ref_clk; unsigned int mul; @@ -30,7 +30,7 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) base = of_iomap(node, 0); if (!base) { - pr_err("%s: of_iomap failed\n", node->full_name); + pr_err("%pOF: of_iomap failed\n", node); return; } @@ -39,13 +39,13 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) ref_clk = of_clk_get(node, 0); if (IS_ERR(ref_clk)) { - pr_err("%s: of_clk_get failed\n", node->full_name); + pr_err("%pOF: of_clk_get failed\n", node); return; } hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, mul, 1); if (IS_ERR(hw)) { - pr_err("%s: failed to register clock\n", node->full_name); + pr_err("%pOF: failed to register clock\n", node); return; } @@ -57,7 +57,7 @@ CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock", static void __init moxart_of_apb_clk_init(struct device_node *node) { - static void __iomem *base; + void __iomem *base; struct clk_hw *hw; struct clk *pll_clk; unsigned int div, val; @@ -70,7 +70,7 @@ static void __init moxart_of_apb_clk_init(struct device_node *node) base = of_iomap(node, 0); if (!base) { - pr_err("%s: of_iomap failed\n", node->full_name); + pr_err("%pOF: of_iomap failed\n", node); return; } @@ -83,13 +83,13 @@ static void __init moxart_of_apb_clk_init(struct device_node *node) pll_clk = of_clk_get(node, 0); if (IS_ERR(pll_clk)) { - pr_err("%s: of_clk_get failed\n", node->full_name); + pr_err("%pOF: of_clk_get failed\n", node); return; } hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, 1, div); if (IS_ERR(hw)) { - pr_err("%s: failed to register clock\n", node->full_name); + pr_err("%pOF: failed to register clock\n", node); return; } diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index f3931e38fac0..b0ea753b8709 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -12,6 +12,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/fsl/guts.h> #include <linux/io.h> #include <linux/kernel.h> @@ -537,6 +538,17 @@ static const struct clockgen_chipinfo chipinfo[] = { .flags = CG_PLL_8BIT, }, { + .compat = "fsl,ls1088a-clockgen", + .cmux_groups = { + &clockgen2_cmux_cga12 + }, + .cmux_to_group = { + 0, 0, -1 + }, + .pll_mask = 0x07, + .flags = CG_VER3 | CG_LITTLE_ENDIAN, + }, + { .compat = "fsl,ls1012a-clockgen", .cmux_groups = { &ls1012a_cmux @@ -1113,6 +1125,7 @@ static void __init create_one_pll(struct clockgen *cg, int idx) for (i = 0; i < ARRAY_SIZE(pll->div); i++) { struct clk *clk; + int ret; snprintf(pll->div[i].name, sizeof(pll->div[i].name), "cg-pll%d-div%d", idx, i + 1); @@ -1126,6 +1139,11 @@ static void __init create_one_pll(struct clockgen *cg, int idx) } pll->div[i].clk = clk; + ret = clk_register_clkdev(clk, pll->div[i].name, NULL); + if (ret != 0) + pr_err("%s: %s: register to lookup table failed %ld\n", + __func__, pll->div[i].name, PTR_ERR(clk)); + } } @@ -1348,8 +1366,7 @@ static void __init clockgen_init(struct device_node *np) } if (i == ARRAY_SIZE(chipinfo)) { - pr_err("%s: unknown clockgen node %s\n", __func__, - np->full_name); + pr_err("%s: unknown clockgen node %pOF\n", __func__, np); goto err; } clockgen.info = chipinfo[i]; @@ -1362,8 +1379,8 @@ static void __init clockgen_init(struct device_node *np) if (guts) { clockgen.guts = of_iomap(guts, 0); if (!clockgen.guts) { - pr_err("%s: Couldn't map %s regs\n", __func__, - guts->full_name); + pr_err("%s: Couldn't map %pOF regs\n", __func__, + guts); } } @@ -1398,6 +1415,7 @@ CLK_OF_DECLARE(qoriq_clockgen_ls1012a, "fsl,ls1012a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1021a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls1043a, "fsl,ls1043a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls1046a, "fsl,ls1046a-clockgen", clockgen_init); +CLK_OF_DECLARE(qoriq_clockgen_ls1088a, "fsl,ls1088a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls2080a, "fsl,ls2080a-clockgen", clockgen_init); /* Legacy nodes */ diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 2492442eea77..20d90769cced 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -519,6 +519,11 @@ static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate, SI5351_CLK_INTEGER_MODE, (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0); + /* Do a pll soft reset on the affected pll */ + si5351_reg_write(hwdata->drvdata, SI5351_PLL_RESET, + hwdata->num == 0 ? SI5351_PLL_RESET_A : + SI5351_PLL_RESET_B); + dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n", __func__, clk_hw_get_name(hw), @@ -1091,13 +1096,6 @@ static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate, si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, SI5351_CLK_POWERDOWN, 0); - /* - * Do a pll soft reset on both plls, needed in some cases to get - * all outputs running. - */ - si5351_reg_write(hwdata->drvdata, SI5351_PLL_RESET, - SI5351_PLL_RESET_A | SI5351_PLL_RESET_B); - dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n", __func__, clk_hw_get_name(hw), (1 << rdiv), diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c index 68e2a4e499f1..96c6b6bc8f0e 100644 --- a/drivers/clk/clk-stm32f4.c +++ b/drivers/clk/clk-stm32f4.c @@ -1541,8 +1541,8 @@ static void __init stm32f4_rcc_init(struct device_node *np) base + gd->offset, gd->bit_idx, 0, &stm32f4_clk_lock); if (IS_ERR(clks[idx])) { - pr_err("%s: Unable to register leaf clock %s\n", - np->full_name, gd->name); + pr_err("%pOF: Unable to register leaf clock %s\n", + np, gd->name); goto fail; } } diff --git a/drivers/clk/clk-stm32h7.c b/drivers/clk/clk-stm32h7.c new file mode 100644 index 000000000000..a94c3f56c590 --- /dev/null +++ b/drivers/clk/clk-stm32h7.c @@ -0,0 +1,1410 @@ +/* + * Copyright (C) Gabriel Fernandez 2017 + * Author: Gabriel Fernandez <gabriel.fernandez@st.com> + * + * License terms: GPL V2.0. + * + * 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/io.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/stm32h7-clks.h> + +/* Reset Clock Control Registers */ +#define RCC_CR 0x00 +#define RCC_CFGR 0x10 +#define RCC_D1CFGR 0x18 +#define RCC_D2CFGR 0x1C +#define RCC_D3CFGR 0x20 +#define RCC_PLLCKSELR 0x28 +#define RCC_PLLCFGR 0x2C +#define RCC_PLL1DIVR 0x30 +#define RCC_PLL1FRACR 0x34 +#define RCC_PLL2DIVR 0x38 +#define RCC_PLL2FRACR 0x3C +#define RCC_PLL3DIVR 0x40 +#define RCC_PLL3FRACR 0x44 +#define RCC_D1CCIPR 0x4C +#define RCC_D2CCIP1R 0x50 +#define RCC_D2CCIP2R 0x54 +#define RCC_D3CCIPR 0x58 +#define RCC_BDCR 0x70 +#define RCC_CSR 0x74 +#define RCC_AHB3ENR 0xD4 +#define RCC_AHB1ENR 0xD8 +#define RCC_AHB2ENR 0xDC +#define RCC_AHB4ENR 0xE0 +#define RCC_APB3ENR 0xE4 +#define RCC_APB1LENR 0xE8 +#define RCC_APB1HENR 0xEC +#define RCC_APB2ENR 0xF0 +#define RCC_APB4ENR 0xF4 + +static DEFINE_SPINLOCK(stm32rcc_lock); + +static void __iomem *base; +static struct clk_hw **hws; + +/* System clock parent */ +static const char * const sys_src[] = { + "hsi_ck", "csi_ck", "hse_ck", "pll1_p" }; + +static const char * const tracein_src[] = { + "hsi_ck", "csi_ck", "hse_ck", "pll1_r" }; + +static const char * const per_src[] = { + "hsi_ker", "csi_ker", "hse_ck", "disabled" }; + +static const char * const pll_src[] = { + "hsi_ck", "csi_ck", "hse_ck", "no clock" }; + +static const char * const sdmmc_src[] = { "pll1_q", "pll2_r" }; + +static const char * const dsi_src[] = { "ck_dsi_phy", "pll2_q" }; + +static const char * const qspi_src[] = { + "hclk", "pll1_q", "pll2_r", "per_ck" }; + +static const char * const fmc_src[] = { + "hclk", "pll1_q", "pll2_r", "per_ck" }; + +/* Kernel clock parent */ +static const char * const swp_src[] = { "pclk1", "hsi_ker" }; + +static const char * const fdcan_src[] = { "hse_ck", "pll1_q", "pll2_q" }; + +static const char * const dfsdm1_src[] = { "pclk2", "sys_ck" }; + +static const char * const spdifrx_src[] = { + "pll1_q", "pll2_r", "pll3_r", "hsi_ker" }; + +static const char *spi_src1[5] = { + "pll1_q", "pll2_p", "pll3_p", NULL, "per_ck" }; + +static const char * const spi_src2[] = { + "pclk2", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "hse_ck" }; + +static const char * const spi_src3[] = { + "pclk4", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "hse_ck" }; + +static const char * const lptim_src1[] = { + "pclk1", "pll2_p", "pll3_r", "lse_ck", "lsi_ck", "per_ck" }; + +static const char * const lptim_src2[] = { + "pclk4", "pll2_p", "pll3_r", "lse_ck", "lsi_ck", "per_ck" }; + +static const char * const cec_src[] = {"lse_ck", "lsi_ck", "csi_ker_div122" }; + +static const char * const usbotg_src[] = {"pll1_q", "pll3_q", "rc48_ck" }; + +/* i2c 1,2,3 src */ +static const char * const i2c_src1[] = { + "pclk1", "pll3_r", "hsi_ker", "csi_ker" }; + +static const char * const i2c_src2[] = { + "pclk4", "pll3_r", "hsi_ker", "csi_ker" }; + +static const char * const rng_src[] = { + "rc48_ck", "pll1_q", "lse_ck", "lsi_ck" }; + +/* usart 1,6 src */ +static const char * const usart_src1[] = { + "pclk2", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "lse_ck" }; + +/* usart 2,3,4,5,7,8 src */ +static const char * const usart_src2[] = { + "pclk1", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "lse_ck" }; + +static const char *sai_src[5] = { + "pll1_q", "pll2_p", "pll3_p", NULL, "per_ck" }; + +static const char * const adc_src[] = { "pll2_p", "pll3_r", "per_ck" }; + +/* lptim 2,3,4,5 src */ +static const char * const lpuart1_src[] = { + "pclk3", "pll2_q", "pll3_q", "csi_ker", "lse_ck" }; + +static const char * const hrtim_src[] = { "tim2_ker", "d1cpre" }; + +/* RTC clock parent */ +static const char * const rtc_src[] = { "off", "lse_ck", "lsi_ck", "hse_1M" }; + +/* Micro-controller output clock parent */ +static const char * const mco_src1[] = { + "hsi_ck", "lse_ck", "hse_ck", "pll1_q", "rc48_ck" }; + +static const char * const mco_src2[] = { + "sys_ck", "pll2_p", "hse_ck", "pll1_p", "csi_ck", "lsi_ck" }; + +/* LCD clock */ +static const char * const ltdc_src[] = {"pll3_r"}; + +/* Gate clock with ready bit and backup domain management */ +struct stm32_ready_gate { + struct clk_gate gate; + u8 bit_rdy; +}; + +#define to_ready_gate_clk(_rgate) container_of(_rgate, struct stm32_ready_gate,\ + gate) + +#define RGATE_TIMEOUT 10000 + +static int ready_gate_clk_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32_ready_gate *rgate = to_ready_gate_clk(gate); + int bit_status; + unsigned int timeout = RGATE_TIMEOUT; + + if (clk_gate_ops.is_enabled(hw)) + return 0; + + clk_gate_ops.enable(hw); + + /* We can't use readl_poll_timeout() because we can blocked if + * someone enables this clock before clocksource changes. + * Only jiffies counter is available. Jiffies are incremented by + * interruptions and enable op does not allow to be interrupted. + */ + do { + bit_status = !(readl(gate->reg) & BIT(rgate->bit_rdy)); + + if (bit_status) + udelay(100); + + } while (bit_status && --timeout); + + return bit_status; +} + +static void ready_gate_clk_disable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32_ready_gate *rgate = to_ready_gate_clk(gate); + int bit_status; + unsigned int timeout = RGATE_TIMEOUT; + + if (!clk_gate_ops.is_enabled(hw)) + return; + + clk_gate_ops.disable(hw); + + do { + bit_status = !!(readl(gate->reg) & BIT(rgate->bit_rdy)); + + if (bit_status) + udelay(100); + + } while (bit_status && --timeout); +} + +static const struct clk_ops ready_gate_clk_ops = { + .enable = ready_gate_clk_enable, + .disable = ready_gate_clk_disable, + .is_enabled = clk_gate_is_enabled, +}; + +static struct clk_hw *clk_register_ready_gate(struct device *dev, + const char *name, const char *parent_name, + void __iomem *reg, u8 bit_idx, u8 bit_rdy, + unsigned long flags, spinlock_t *lock) +{ + struct stm32_ready_gate *rgate; + struct clk_init_data init = { NULL }; + struct clk_hw *hw; + int ret; + + rgate = kzalloc(sizeof(*rgate), GFP_KERNEL); + if (!rgate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &ready_gate_clk_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + rgate->bit_rdy = bit_rdy; + rgate->gate.lock = lock; + rgate->gate.reg = reg; + rgate->gate.bit_idx = bit_idx; + rgate->gate.hw.init = &init; + + hw = &rgate->gate.hw; + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(rgate); + hw = ERR_PTR(ret); + } + + return hw; +} + +struct gate_cfg { + u32 offset; + u8 bit_idx; +}; + +struct muxdiv_cfg { + u32 offset; + u8 shift; + u8 width; +}; + +struct composite_clk_cfg { + struct gate_cfg *gate; + struct muxdiv_cfg *mux; + struct muxdiv_cfg *div; + const char *name; + const char * const *parent_name; + int num_parents; + u32 flags; +}; + +struct composite_clk_gcfg_t { + u8 flags; + const struct clk_ops *ops; +}; + +/* + * General config definition of a composite clock (only clock diviser for rate) + */ +struct composite_clk_gcfg { + struct composite_clk_gcfg_t *mux; + struct composite_clk_gcfg_t *div; + struct composite_clk_gcfg_t *gate; +}; + +#define M_CFG_MUX(_mux_ops, _mux_flags)\ + .mux = &(struct composite_clk_gcfg_t) { _mux_flags, _mux_ops} + +#define M_CFG_DIV(_rate_ops, _rate_flags)\ + .div = &(struct composite_clk_gcfg_t) {_rate_flags, _rate_ops} + +#define M_CFG_GATE(_gate_ops, _gate_flags)\ + .gate = &(struct composite_clk_gcfg_t) { _gate_flags, _gate_ops} + +static struct clk_mux *_get_cmux(void __iomem *reg, u8 shift, u8 width, + u32 flags, spinlock_t *lock) +{ + struct clk_mux *mux; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = reg; + mux->shift = shift; + mux->mask = (1 << width) - 1; + mux->flags = flags; + mux->lock = lock; + + return mux; +} + +static struct clk_divider *_get_cdiv(void __iomem *reg, u8 shift, u8 width, + u32 flags, spinlock_t *lock) +{ + struct clk_divider *div; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + + if (!div) + return ERR_PTR(-ENOMEM); + + div->reg = reg; + div->shift = shift; + div->width = width; + div->flags = flags; + div->lock = lock; + + return div; +} + +static struct clk_gate *_get_cgate(void __iomem *reg, u8 bit_idx, u32 flags, + spinlock_t *lock) +{ + struct clk_gate *gate; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->reg = reg; + gate->bit_idx = bit_idx; + gate->flags = flags; + gate->lock = lock; + + return gate; +} + +struct composite_cfg { + struct clk_hw *mux_hw; + struct clk_hw *div_hw; + struct clk_hw *gate_hw; + + const struct clk_ops *mux_ops; + const struct clk_ops *div_ops; + const struct clk_ops *gate_ops; +}; + +static void get_cfg_composite_div(const struct composite_clk_gcfg *gcfg, + const struct composite_clk_cfg *cfg, + struct composite_cfg *composite, spinlock_t *lock) +{ + struct clk_mux *mux = NULL; + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + const struct clk_ops *mux_ops, *div_ops, *gate_ops; + struct clk_hw *mux_hw; + struct clk_hw *div_hw; + struct clk_hw *gate_hw; + + mux_ops = div_ops = gate_ops = NULL; + mux_hw = div_hw = gate_hw = NULL; + + if (gcfg->mux && gcfg->mux) { + mux = _get_cmux(base + cfg->mux->offset, + cfg->mux->shift, + cfg->mux->width, + gcfg->mux->flags, lock); + + if (!IS_ERR(mux)) { + mux_hw = &mux->hw; + mux_ops = gcfg->mux->ops ? + gcfg->mux->ops : &clk_mux_ops; + } + } + + if (gcfg->div && cfg->div) { + div = _get_cdiv(base + cfg->div->offset, + cfg->div->shift, + cfg->div->width, + gcfg->div->flags, lock); + + if (!IS_ERR(div)) { + div_hw = &div->hw; + div_ops = gcfg->div->ops ? + gcfg->div->ops : &clk_divider_ops; + } + } + + if (gcfg->gate && gcfg->gate) { + gate = _get_cgate(base + cfg->gate->offset, + cfg->gate->bit_idx, + gcfg->gate->flags, lock); + + if (!IS_ERR(gate)) { + gate_hw = &gate->hw; + gate_ops = gcfg->gate->ops ? + gcfg->gate->ops : &clk_gate_ops; + } + } + + composite->mux_hw = mux_hw; + composite->mux_ops = mux_ops; + + composite->div_hw = div_hw; + composite->div_ops = div_ops; + + composite->gate_hw = gate_hw; + composite->gate_ops = gate_ops; +} + +/* Kernel Timer */ +struct timer_ker { + u8 dppre_shift; + struct clk_hw hw; + spinlock_t *lock; +}; + +#define to_timer_ker(_hw) container_of(_hw, struct timer_ker, hw) + +static unsigned long timer_ker_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct timer_ker *clk_elem = to_timer_ker(hw); + u32 timpre; + u32 dppre_shift = clk_elem->dppre_shift; + u32 prescaler; + u32 mul; + + timpre = (readl(base + RCC_CFGR) >> 15) & 0x01; + + prescaler = (readl(base + RCC_D2CFGR) >> dppre_shift) & 0x03; + + mul = 2; + + if (prescaler < 4) + mul = 1; + + else if (timpre && prescaler > 4) + mul = 4; + + return parent_rate * mul; +} + +static const struct clk_ops timer_ker_ops = { + .recalc_rate = timer_ker_recalc_rate, +}; + +static struct clk_hw *clk_register_stm32_timer_ker(struct device *dev, + const char *name, const char *parent_name, + unsigned long flags, + u8 dppre_shift, + spinlock_t *lock) +{ + struct timer_ker *element; + struct clk_init_data init; + struct clk_hw *hw; + int err; + + element = kzalloc(sizeof(*element), GFP_KERNEL); + if (!element) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &timer_ker_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + element->hw.init = &init; + element->lock = lock; + element->dppre_shift = dppre_shift; + + hw = &element->hw; + err = clk_hw_register(dev, hw); + + if (err) { + kfree(element); + return ERR_PTR(err); + } + + return hw; +} + +static const struct clk_div_table d1cpre_div_table[] = { + { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1}, + { 4, 1 }, { 5, 1 }, { 6, 1 }, { 7, 1}, + { 8, 2 }, { 9, 4 }, { 10, 8 }, { 11, 16 }, + { 12, 64 }, { 13, 128 }, { 14, 256 }, + { 15, 512 }, + { 0 }, +}; + +static const struct clk_div_table ppre_div_table[] = { + { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1}, + { 4, 2 }, { 5, 4 }, { 6, 8 }, { 7, 16 }, + { 0 }, +}; + +static void register_core_and_bus_clocks(void) +{ + /* CORE AND BUS */ + hws[SYS_D1CPRE] = clk_hw_register_divider_table(NULL, "d1cpre", + "sys_ck", CLK_IGNORE_UNUSED, base + RCC_D1CFGR, 8, 4, 0, + d1cpre_div_table, &stm32rcc_lock); + + hws[HCLK] = clk_hw_register_divider_table(NULL, "hclk", "d1cpre", + CLK_IGNORE_UNUSED, base + RCC_D1CFGR, 0, 4, 0, + d1cpre_div_table, &stm32rcc_lock); + + /* D1 DOMAIN */ + /* * CPU Systick */ + hws[CPU_SYSTICK] = clk_hw_register_fixed_factor(NULL, "systick", + "d1cpre", 0, 1, 8); + + /* * APB3 peripheral */ + hws[PCLK3] = clk_hw_register_divider_table(NULL, "pclk3", "hclk", 0, + base + RCC_D1CFGR, 4, 3, 0, + ppre_div_table, &stm32rcc_lock); + + /* D2 DOMAIN */ + /* * APB1 peripheral */ + hws[PCLK1] = clk_hw_register_divider_table(NULL, "pclk1", "hclk", 0, + base + RCC_D2CFGR, 4, 3, 0, + ppre_div_table, &stm32rcc_lock); + + /* Timers prescaler clocks */ + clk_register_stm32_timer_ker(NULL, "tim1_ker", "pclk1", 0, + 4, &stm32rcc_lock); + + /* * APB2 peripheral */ + hws[PCLK2] = clk_hw_register_divider_table(NULL, "pclk2", "hclk", 0, + base + RCC_D2CFGR, 8, 3, 0, ppre_div_table, + &stm32rcc_lock); + + clk_register_stm32_timer_ker(NULL, "tim2_ker", "pclk2", 0, 8, + &stm32rcc_lock); + + /* D3 DOMAIN */ + /* * APB4 peripheral */ + hws[PCLK4] = clk_hw_register_divider_table(NULL, "pclk4", "hclk", 0, + base + RCC_D3CFGR, 4, 3, 0, + ppre_div_table, &stm32rcc_lock); +} + +/* MUX clock configuration */ +struct stm32_mux_clk { + const char *name; + const char * const *parents; + u8 num_parents; + u32 offset; + u8 shift; + u8 width; + u32 flags; +}; + +#define M_MCLOCF(_name, _parents, _mux_offset, _mux_shift, _mux_width, _flags)\ +{\ + .name = _name,\ + .parents = _parents,\ + .num_parents = ARRAY_SIZE(_parents),\ + .offset = _mux_offset,\ + .shift = _mux_shift,\ + .width = _mux_width,\ + .flags = _flags,\ +} + +#define M_MCLOC(_name, _parents, _mux_offset, _mux_shift, _mux_width)\ + M_MCLOCF(_name, _parents, _mux_offset, _mux_shift, _mux_width, 0)\ + +static const struct stm32_mux_clk stm32_mclk[] __initconst = { + M_MCLOC("per_ck", per_src, RCC_D1CCIPR, 28, 3), + M_MCLOC("pllsrc", pll_src, RCC_PLLCKSELR, 0, 3), + M_MCLOC("sys_ck", sys_src, RCC_CFGR, 0, 3), + M_MCLOC("tracein_ck", tracein_src, RCC_CFGR, 0, 3), +}; + +/* Oscillary clock configuration */ +struct stm32_osc_clk { + const char *name; + const char *parent; + u32 gate_offset; + u8 bit_idx; + u8 bit_rdy; + u32 flags; +}; + +#define OSC_CLKF(_name, _parent, _gate_offset, _bit_idx, _bit_rdy, _flags)\ +{\ + .name = _name,\ + .parent = _parent,\ + .gate_offset = _gate_offset,\ + .bit_idx = _bit_idx,\ + .bit_rdy = _bit_rdy,\ + .flags = _flags,\ +} + +#define OSC_CLK(_name, _parent, _gate_offset, _bit_idx, _bit_rdy)\ + OSC_CLKF(_name, _parent, _gate_offset, _bit_idx, _bit_rdy, 0) + +static const struct stm32_osc_clk stm32_oclk[] __initconst = { + OSC_CLKF("hsi_ck", "hsidiv", RCC_CR, 0, 2, CLK_IGNORE_UNUSED), + OSC_CLKF("hsi_ker", "hsidiv", RCC_CR, 1, 2, CLK_IGNORE_UNUSED), + OSC_CLKF("csi_ck", "clk-csi", RCC_CR, 7, 8, CLK_IGNORE_UNUSED), + OSC_CLKF("csi_ker", "clk-csi", RCC_CR, 9, 8, CLK_IGNORE_UNUSED), + OSC_CLKF("rc48_ck", "clk-rc48", RCC_CR, 12, 13, CLK_IGNORE_UNUSED), + OSC_CLKF("lsi_ck", "clk-lsi", RCC_CSR, 0, 1, CLK_IGNORE_UNUSED), +}; + +/* PLL configuration */ +struct st32h7_pll_cfg { + u8 bit_idx; + u32 offset_divr; + u8 bit_frac_en; + u32 offset_frac; + u8 divm; +}; + +struct stm32_pll_data { + const char *name; + const char *parent_name; + unsigned long flags; + const struct st32h7_pll_cfg *cfg; +}; + +static const struct st32h7_pll_cfg stm32h7_pll1 = { + .bit_idx = 24, + .offset_divr = RCC_PLL1DIVR, + .bit_frac_en = 0, + .offset_frac = RCC_PLL1FRACR, + .divm = 4, +}; + +static const struct st32h7_pll_cfg stm32h7_pll2 = { + .bit_idx = 26, + .offset_divr = RCC_PLL2DIVR, + .bit_frac_en = 4, + .offset_frac = RCC_PLL2FRACR, + .divm = 12, +}; + +static const struct st32h7_pll_cfg stm32h7_pll3 = { + .bit_idx = 28, + .offset_divr = RCC_PLL3DIVR, + .bit_frac_en = 8, + .offset_frac = RCC_PLL3FRACR, + .divm = 20, +}; + +static const struct stm32_pll_data stm32_pll[] = { + { "vco1", "pllsrc", CLK_IGNORE_UNUSED, &stm32h7_pll1 }, + { "vco2", "pllsrc", 0, &stm32h7_pll2 }, + { "vco3", "pllsrc", 0, &stm32h7_pll3 }, +}; + +struct stm32_fractional_divider { + void __iomem *mreg; + u8 mshift; + u8 mwidth; + u32 mmask; + + void __iomem *nreg; + u8 nshift; + u8 nwidth; + + void __iomem *freg_status; + u8 freg_bit; + void __iomem *freg_value; + u8 fshift; + u8 fwidth; + + u8 flags; + struct clk_hw hw; + spinlock_t *lock; +}; + +struct stm32_pll_obj { + spinlock_t *lock; + struct stm32_fractional_divider div; + struct stm32_ready_gate rgate; + struct clk_hw hw; +}; + +#define to_pll(_hw) container_of(_hw, struct stm32_pll_obj, hw) + +static int pll_is_enabled(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; + + __clk_hw_set_clk(_hw, hw); + + return ready_gate_clk_ops.is_enabled(_hw); +} + +static int pll_enable(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; + + __clk_hw_set_clk(_hw, hw); + + return ready_gate_clk_ops.enable(_hw); +} + +static void pll_disable(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; + + __clk_hw_set_clk(_hw, hw); + + ready_gate_clk_ops.disable(_hw); +} + +static int pll_frac_is_enabled(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct stm32_fractional_divider *fd = &clk_elem->div; + + return (readl(fd->freg_status) >> fd->freg_bit) & 0x01; +} + +static unsigned long pll_read_frac(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct stm32_fractional_divider *fd = &clk_elem->div; + + return (readl(fd->freg_value) >> fd->fshift) & + GENMASK(fd->fwidth - 1, 0); +} + +static unsigned long pll_fd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct stm32_fractional_divider *fd = &clk_elem->div; + unsigned long m, n; + u32 val, mask; + u64 rate, rate1 = 0; + + val = readl(fd->mreg); + mask = GENMASK(fd->mwidth - 1, 0) << fd->mshift; + m = (val & mask) >> fd->mshift; + + val = readl(fd->nreg); + mask = GENMASK(fd->nwidth - 1, 0) << fd->nshift; + n = ((val & mask) >> fd->nshift) + 1; + + if (!n || !m) + return parent_rate; + + rate = (u64)parent_rate * n; + do_div(rate, m); + + if (pll_frac_is_enabled(hw)) { + val = pll_read_frac(hw); + rate1 = (u64)parent_rate * (u64)val; + do_div(rate1, (m * 8191)); + } + + return rate + rate1; +} + +static const struct clk_ops pll_ops = { + .enable = pll_enable, + .disable = pll_disable, + .is_enabled = pll_is_enabled, + .recalc_rate = pll_fd_recalc_rate, +}; + +static struct clk_hw *clk_register_stm32_pll(struct device *dev, + const char *name, + const char *parent, + unsigned long flags, + const struct st32h7_pll_cfg *cfg, + spinlock_t *lock) +{ + struct stm32_pll_obj *pll; + struct clk_init_data init = { NULL }; + struct clk_hw *hw; + int ret; + struct stm32_fractional_divider *div = NULL; + struct stm32_ready_gate *rgate; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &pll_ops; + init.flags = flags; + init.parent_names = &parent; + init.num_parents = 1; + pll->hw.init = &init; + + hw = &pll->hw; + rgate = &pll->rgate; + + rgate->bit_rdy = cfg->bit_idx + 1; + rgate->gate.lock = lock; + rgate->gate.reg = base + RCC_CR; + rgate->gate.bit_idx = cfg->bit_idx; + + div = &pll->div; + div->flags = 0; + div->mreg = base + RCC_PLLCKSELR; + div->mshift = cfg->divm; + div->mwidth = 6; + div->nreg = base + cfg->offset_divr; + div->nshift = 0; + div->nwidth = 9; + + div->freg_status = base + RCC_PLLCFGR; + div->freg_bit = cfg->bit_frac_en; + div->freg_value = base + cfg->offset_frac; + div->fshift = 3; + div->fwidth = 13; + + div->lock = lock; + + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(pll); + hw = ERR_PTR(ret); + } + + return hw; +} + +/* ODF CLOCKS */ +static unsigned long odf_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return clk_divider_ops.recalc_rate(hw, parent_rate); +} + +static long odf_divider_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return clk_divider_ops.round_rate(hw, rate, prate); +} + +static int odf_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_hw *hwp; + int pll_status; + int ret; + + hwp = clk_hw_get_parent(hw); + + pll_status = pll_is_enabled(hwp); + + if (pll_status) + pll_disable(hwp); + + ret = clk_divider_ops.set_rate(hw, rate, parent_rate); + + if (pll_status) + pll_enable(hwp); + + return ret; +} + +static const struct clk_ops odf_divider_ops = { + .recalc_rate = odf_divider_recalc_rate, + .round_rate = odf_divider_round_rate, + .set_rate = odf_divider_set_rate, +}; + +static int odf_gate_enable(struct clk_hw *hw) +{ + struct clk_hw *hwp; + int pll_status; + int ret; + + if (clk_gate_ops.is_enabled(hw)) + return 0; + + hwp = clk_hw_get_parent(hw); + + pll_status = pll_is_enabled(hwp); + + if (pll_status) + pll_disable(hwp); + + ret = clk_gate_ops.enable(hw); + + if (pll_status) + pll_enable(hwp); + + return ret; +} + +static void odf_gate_disable(struct clk_hw *hw) +{ + struct clk_hw *hwp; + int pll_status; + + if (!clk_gate_ops.is_enabled(hw)) + return; + + hwp = clk_hw_get_parent(hw); + + pll_status = pll_is_enabled(hwp); + + if (pll_status) + pll_disable(hwp); + + clk_gate_ops.disable(hw); + + if (pll_status) + pll_enable(hwp); +} + +static const struct clk_ops odf_gate_ops = { + .enable = odf_gate_enable, + .disable = odf_gate_disable, + .is_enabled = clk_gate_is_enabled, +}; + +static struct composite_clk_gcfg odf_clk_gcfg = { + M_CFG_DIV(&odf_divider_ops, 0), + M_CFG_GATE(&odf_gate_ops, 0), +}; + +#define M_ODF_F(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ + _rate_shift, _rate_width, _flags)\ +{\ + .mux = NULL,\ + .div = &(struct muxdiv_cfg) {_rate_offset, _rate_shift, _rate_width},\ + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx },\ + .name = _name,\ + .parent_name = &(const char *) {_parent},\ + .num_parents = 1,\ + .flags = _flags,\ +} + +#define M_ODF(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ + _rate_shift, _rate_width)\ +M_ODF_F(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ + _rate_shift, _rate_width, 0)\ + +static const struct composite_clk_cfg stm32_odf[3][3] = { + { + M_ODF_F("pll1_p", "vco1", RCC_PLLCFGR, 16, RCC_PLL1DIVR, 9, 7, + CLK_IGNORE_UNUSED), + M_ODF_F("pll1_q", "vco1", RCC_PLLCFGR, 17, RCC_PLL1DIVR, 16, 7, + CLK_IGNORE_UNUSED), + M_ODF_F("pll1_r", "vco1", RCC_PLLCFGR, 18, RCC_PLL1DIVR, 24, 7, + CLK_IGNORE_UNUSED), + }, + + { + M_ODF("pll2_p", "vco2", RCC_PLLCFGR, 19, RCC_PLL2DIVR, 9, 7), + M_ODF("pll2_q", "vco2", RCC_PLLCFGR, 20, RCC_PLL2DIVR, 16, 7), + M_ODF("pll2_r", "vco2", RCC_PLLCFGR, 21, RCC_PLL2DIVR, 24, 7), + }, + { + M_ODF("pll3_p", "vco3", RCC_PLLCFGR, 22, RCC_PLL3DIVR, 9, 7), + M_ODF("pll3_q", "vco3", RCC_PLLCFGR, 23, RCC_PLL3DIVR, 16, 7), + M_ODF("pll3_r", "vco3", RCC_PLLCFGR, 24, RCC_PLL3DIVR, 24, 7), + } +}; + +/* PERIF CLOCKS */ +struct pclk_t { + u32 gate_offset; + u8 bit_idx; + const char *name; + const char *parent; + u32 flags; +}; + +#define PER_CLKF(_gate_offset, _bit_idx, _name, _parent, _flags)\ +{\ + .gate_offset = _gate_offset,\ + .bit_idx = _bit_idx,\ + .name = _name,\ + .parent = _parent,\ + .flags = _flags,\ +} + +#define PER_CLK(_gate_offset, _bit_idx, _name, _parent)\ + PER_CLKF(_gate_offset, _bit_idx, _name, _parent, 0) + +static const struct pclk_t pclk[] = { + PER_CLK(RCC_AHB3ENR, 31, "d1sram1", "hclk"), + PER_CLK(RCC_AHB3ENR, 30, "itcm", "hclk"), + PER_CLK(RCC_AHB3ENR, 29, "dtcm2", "hclk"), + PER_CLK(RCC_AHB3ENR, 28, "dtcm1", "hclk"), + PER_CLK(RCC_AHB3ENR, 8, "flitf", "hclk"), + PER_CLK(RCC_AHB3ENR, 5, "jpgdec", "hclk"), + PER_CLK(RCC_AHB3ENR, 4, "dma2d", "hclk"), + PER_CLK(RCC_AHB3ENR, 0, "mdma", "hclk"), + PER_CLK(RCC_AHB1ENR, 28, "usb2ulpi", "hclk"), + PER_CLK(RCC_AHB1ENR, 26, "usb1ulpi", "hclk"), + PER_CLK(RCC_AHB1ENR, 17, "eth1rx", "hclk"), + PER_CLK(RCC_AHB1ENR, 16, "eth1tx", "hclk"), + PER_CLK(RCC_AHB1ENR, 15, "eth1mac", "hclk"), + PER_CLK(RCC_AHB1ENR, 14, "art", "hclk"), + PER_CLK(RCC_AHB1ENR, 1, "dma2", "hclk"), + PER_CLK(RCC_AHB1ENR, 0, "dma1", "hclk"), + PER_CLK(RCC_AHB2ENR, 31, "d2sram3", "hclk"), + PER_CLK(RCC_AHB2ENR, 30, "d2sram2", "hclk"), + PER_CLK(RCC_AHB2ENR, 29, "d2sram1", "hclk"), + PER_CLK(RCC_AHB2ENR, 5, "hash", "hclk"), + PER_CLK(RCC_AHB2ENR, 4, "crypt", "hclk"), + PER_CLK(RCC_AHB2ENR, 0, "camitf", "hclk"), + PER_CLK(RCC_AHB4ENR, 28, "bkpram", "hclk"), + PER_CLK(RCC_AHB4ENR, 25, "hsem", "hclk"), + PER_CLK(RCC_AHB4ENR, 21, "bdma", "hclk"), + PER_CLK(RCC_AHB4ENR, 19, "crc", "hclk"), + PER_CLK(RCC_AHB4ENR, 10, "gpiok", "hclk"), + PER_CLK(RCC_AHB4ENR, 9, "gpioj", "hclk"), + PER_CLK(RCC_AHB4ENR, 8, "gpioi", "hclk"), + PER_CLK(RCC_AHB4ENR, 7, "gpioh", "hclk"), + PER_CLK(RCC_AHB4ENR, 6, "gpiog", "hclk"), + PER_CLK(RCC_AHB4ENR, 5, "gpiof", "hclk"), + PER_CLK(RCC_AHB4ENR, 4, "gpioe", "hclk"), + PER_CLK(RCC_AHB4ENR, 3, "gpiod", "hclk"), + PER_CLK(RCC_AHB4ENR, 2, "gpioc", "hclk"), + PER_CLK(RCC_AHB4ENR, 1, "gpiob", "hclk"), + PER_CLK(RCC_AHB4ENR, 0, "gpioa", "hclk"), + PER_CLK(RCC_APB3ENR, 6, "wwdg1", "pclk3"), + PER_CLK(RCC_APB1LENR, 29, "dac12", "pclk1"), + PER_CLK(RCC_APB1LENR, 11, "wwdg2", "pclk1"), + PER_CLK(RCC_APB1LENR, 8, "tim14", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 7, "tim13", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 6, "tim12", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 5, "tim7", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 4, "tim6", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 3, "tim5", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 2, "tim4", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 1, "tim3", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 0, "tim2", "tim1_ker"), + PER_CLK(RCC_APB1HENR, 5, "mdios", "pclk1"), + PER_CLK(RCC_APB1HENR, 4, "opamp", "pclk1"), + PER_CLK(RCC_APB1HENR, 1, "crs", "pclk1"), + PER_CLK(RCC_APB2ENR, 18, "tim17", "tim2_ker"), + PER_CLK(RCC_APB2ENR, 17, "tim16", "tim2_ker"), + PER_CLK(RCC_APB2ENR, 16, "tim15", "tim2_ker"), + PER_CLK(RCC_APB2ENR, 1, "tim8", "tim2_ker"), + PER_CLK(RCC_APB2ENR, 0, "tim1", "tim2_ker"), + PER_CLK(RCC_APB4ENR, 26, "tmpsens", "pclk4"), + PER_CLK(RCC_APB4ENR, 16, "rtcapb", "pclk4"), + PER_CLK(RCC_APB4ENR, 15, "vref", "pclk4"), + PER_CLK(RCC_APB4ENR, 14, "comp12", "pclk4"), + PER_CLK(RCC_APB4ENR, 1, "syscfg", "pclk4"), +}; + +/* KERNEL CLOCKS */ +#define KER_CLKF(_gate_offset, _bit_idx,\ + _mux_offset, _mux_shift, _mux_width,\ + _name, _parent_name,\ + _flags) \ +{ \ + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx},\ + .mux = &(struct muxdiv_cfg) {_mux_offset, _mux_shift, _mux_width },\ + .name = _name, \ + .parent_name = _parent_name, \ + .num_parents = ARRAY_SIZE(_parent_name),\ + .flags = _flags,\ +} + +#define KER_CLK(_gate_offset, _bit_idx, _mux_offset, _mux_shift, _mux_width,\ + _name, _parent_name) \ +KER_CLKF(_gate_offset, _bit_idx, _mux_offset, _mux_shift, _mux_width,\ + _name, _parent_name, 0)\ + +#define KER_CLKF_NOMUX(_gate_offset, _bit_idx,\ + _name, _parent_name,\ + _flags) \ +{ \ + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx},\ + .mux = NULL,\ + .name = _name, \ + .parent_name = _parent_name, \ + .num_parents = 1,\ + .flags = _flags,\ +} + +static const struct composite_clk_cfg kclk[] = { + KER_CLK(RCC_AHB3ENR, 16, RCC_D1CCIPR, 16, 1, "sdmmc1", sdmmc_src), + KER_CLKF(RCC_AHB3ENR, 14, RCC_D1CCIPR, 4, 2, "quadspi", qspi_src, + CLK_IGNORE_UNUSED), + KER_CLKF(RCC_AHB3ENR, 12, RCC_D1CCIPR, 0, 2, "fmc", fmc_src, + CLK_IGNORE_UNUSED), + KER_CLK(RCC_AHB1ENR, 27, RCC_D2CCIP2R, 20, 2, "usb2otg", usbotg_src), + KER_CLK(RCC_AHB1ENR, 25, RCC_D2CCIP2R, 20, 2, "usb1otg", usbotg_src), + KER_CLK(RCC_AHB1ENR, 5, RCC_D3CCIPR, 16, 2, "adc12", adc_src), + KER_CLK(RCC_AHB2ENR, 9, RCC_D1CCIPR, 16, 1, "sdmmc2", sdmmc_src), + KER_CLK(RCC_AHB2ENR, 6, RCC_D2CCIP2R, 8, 2, "rng", rng_src), + KER_CLK(RCC_AHB4ENR, 24, RCC_D3CCIPR, 16, 2, "adc3", adc_src), + KER_CLKF(RCC_APB3ENR, 4, RCC_D1CCIPR, 8, 1, "dsi", dsi_src, + CLK_SET_RATE_PARENT), + KER_CLKF_NOMUX(RCC_APB3ENR, 3, "ltdc", ltdc_src, CLK_SET_RATE_PARENT), + KER_CLK(RCC_APB1LENR, 31, RCC_D2CCIP2R, 0, 3, "usart8", usart_src2), + KER_CLK(RCC_APB1LENR, 30, RCC_D2CCIP2R, 0, 3, "usart7", usart_src2), + KER_CLK(RCC_APB1LENR, 27, RCC_D2CCIP2R, 22, 2, "hdmicec", cec_src), + KER_CLK(RCC_APB1LENR, 23, RCC_D2CCIP2R, 12, 2, "i2c3", i2c_src1), + KER_CLK(RCC_APB1LENR, 22, RCC_D2CCIP2R, 12, 2, "i2c2", i2c_src1), + KER_CLK(RCC_APB1LENR, 21, RCC_D2CCIP2R, 12, 2, "i2c1", i2c_src1), + KER_CLK(RCC_APB1LENR, 20, RCC_D2CCIP2R, 0, 3, "uart5", usart_src2), + KER_CLK(RCC_APB1LENR, 19, RCC_D2CCIP2R, 0, 3, "uart4", usart_src2), + KER_CLK(RCC_APB1LENR, 18, RCC_D2CCIP2R, 0, 3, "usart3", usart_src2), + KER_CLK(RCC_APB1LENR, 17, RCC_D2CCIP2R, 0, 3, "usart2", usart_src2), + KER_CLK(RCC_APB1LENR, 16, RCC_D2CCIP1R, 20, 2, "spdifrx", spdifrx_src), + KER_CLK(RCC_APB1LENR, 15, RCC_D2CCIP1R, 16, 3, "spi3", spi_src1), + KER_CLK(RCC_APB1LENR, 14, RCC_D2CCIP1R, 16, 3, "spi2", spi_src1), + KER_CLK(RCC_APB1LENR, 9, RCC_D2CCIP2R, 28, 3, "lptim1", lptim_src1), + KER_CLK(RCC_APB1HENR, 8, RCC_D2CCIP1R, 28, 2, "fdcan", fdcan_src), + KER_CLK(RCC_APB1HENR, 2, RCC_D2CCIP1R, 31, 1, "swp", swp_src), + KER_CLK(RCC_APB2ENR, 29, RCC_CFGR, 14, 1, "hrtim", hrtim_src), + KER_CLK(RCC_APB2ENR, 28, RCC_D2CCIP1R, 24, 1, "dfsdm1", dfsdm1_src), + KER_CLKF(RCC_APB2ENR, 24, RCC_D2CCIP1R, 6, 3, "sai3", sai_src, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), + KER_CLKF(RCC_APB2ENR, 23, RCC_D2CCIP1R, 6, 3, "sai2", sai_src, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), + KER_CLKF(RCC_APB2ENR, 22, RCC_D2CCIP1R, 0, 3, "sai1", sai_src, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), + KER_CLK(RCC_APB2ENR, 20, RCC_D2CCIP1R, 16, 3, "spi5", spi_src2), + KER_CLK(RCC_APB2ENR, 13, RCC_D2CCIP1R, 16, 3, "spi4", spi_src2), + KER_CLK(RCC_APB2ENR, 12, RCC_D2CCIP1R, 16, 3, "spi1", spi_src1), + KER_CLK(RCC_APB2ENR, 5, RCC_D2CCIP2R, 3, 3, "usart6", usart_src1), + KER_CLK(RCC_APB2ENR, 4, RCC_D2CCIP2R, 3, 3, "usart1", usart_src1), + KER_CLK(RCC_APB4ENR, 21, RCC_D3CCIPR, 24, 3, "sai4b", sai_src), + KER_CLK(RCC_APB4ENR, 21, RCC_D3CCIPR, 21, 3, "sai4a", sai_src), + KER_CLK(RCC_APB4ENR, 12, RCC_D3CCIPR, 13, 3, "lptim5", lptim_src2), + KER_CLK(RCC_APB4ENR, 11, RCC_D3CCIPR, 13, 3, "lptim4", lptim_src2), + KER_CLK(RCC_APB4ENR, 10, RCC_D3CCIPR, 13, 3, "lptim3", lptim_src2), + KER_CLK(RCC_APB4ENR, 9, RCC_D3CCIPR, 10, 3, "lptim2", lptim_src2), + KER_CLK(RCC_APB4ENR, 7, RCC_D3CCIPR, 8, 2, "i2c4", i2c_src2), + KER_CLK(RCC_APB4ENR, 5, RCC_D3CCIPR, 28, 3, "spi6", spi_src3), + KER_CLK(RCC_APB4ENR, 3, RCC_D3CCIPR, 0, 3, "lpuart1", lpuart1_src), +}; + +static struct composite_clk_gcfg kernel_clk_cfg = { + M_CFG_MUX(NULL, 0), + M_CFG_GATE(NULL, 0), +}; + +/* RTC clock */ +/* + * RTC & LSE registers are protected against parasitic write access. + * PWR_CR_DBP bit must be set to enable write access to RTC registers. + */ +/* STM32_PWR_CR */ +#define PWR_CR 0x00 +/* STM32_PWR_CR bit field */ +#define PWR_CR_DBP BIT(8) + +static struct composite_clk_gcfg rtc_clk_cfg = { + M_CFG_MUX(NULL, 0), + M_CFG_GATE(NULL, 0), +}; + +static const struct composite_clk_cfg rtc_clk = + KER_CLK(RCC_BDCR, 15, RCC_BDCR, 8, 2, "rtc_ck", rtc_src); + +/* Micro-controller output clock */ +static struct composite_clk_gcfg mco_clk_cfg = { + M_CFG_MUX(NULL, 0), + M_CFG_DIV(NULL, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO), +}; + +#define M_MCO_F(_name, _parents, _mux_offset, _mux_shift, _mux_width,\ + _rate_offset, _rate_shift, _rate_width,\ + _flags)\ +{\ + .mux = &(struct muxdiv_cfg) {_mux_offset, _mux_shift, _mux_width },\ + .div = &(struct muxdiv_cfg) {_rate_offset, _rate_shift, _rate_width},\ + .gate = NULL,\ + .name = _name,\ + .parent_name = _parents,\ + .num_parents = ARRAY_SIZE(_parents),\ + .flags = _flags,\ +} + +static const struct composite_clk_cfg mco_clk[] = { + M_MCO_F("mco1", mco_src1, RCC_CFGR, 22, 4, RCC_CFGR, 18, 4, 0), + M_MCO_F("mco2", mco_src2, RCC_CFGR, 29, 3, RCC_CFGR, 25, 4, 0), +}; + +static void __init stm32h7_rcc_init(struct device_node *np) +{ + struct clk_hw_onecell_data *clk_data; + struct composite_cfg c_cfg; + int n; + const char *hse_clk, *lse_clk, *i2s_clk; + struct regmap *pdrm; + + clk_data = kzalloc(sizeof(*clk_data) + + sizeof(*clk_data->hws) * STM32H7_MAX_CLKS, + GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = STM32H7_MAX_CLKS; + + hws = clk_data->hws; + + for (n = 0; n < STM32H7_MAX_CLKS; n++) + hws[n] = ERR_PTR(-ENOENT); + + /* get RCC base @ from DT */ + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: unable to map resource", np->name); + goto err_free_clks; + } + + pdrm = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(pdrm)) + pr_warn("%s: Unable to get syscfg\n", __func__); + else + /* In any case disable backup domain write protection + * and will never be enabled. + * Needed by LSE & RTC clocks. + */ + regmap_update_bits(pdrm, PWR_CR, PWR_CR_DBP, PWR_CR_DBP); + + /* Put parent names from DT */ + hse_clk = of_clk_get_parent_name(np, 0); + lse_clk = of_clk_get_parent_name(np, 1); + i2s_clk = of_clk_get_parent_name(np, 2); + + sai_src[3] = i2s_clk; + spi_src1[3] = i2s_clk; + + /* Register Internal oscillators */ + clk_hw_register_fixed_rate(NULL, "clk-hsi", NULL, 0, 64000000); + clk_hw_register_fixed_rate(NULL, "clk-csi", NULL, 0, 4000000); + clk_hw_register_fixed_rate(NULL, "clk-lsi", NULL, 0, 32000); + clk_hw_register_fixed_rate(NULL, "clk-rc48", NULL, 0, 48000); + + /* This clock is coming from outside. Frequencies unknown */ + hws[CK_DSI_PHY] = clk_hw_register_fixed_rate(NULL, "ck_dsi_phy", NULL, + 0, 0); + + hws[HSI_DIV] = clk_hw_register_divider(NULL, "hsidiv", "clk-hsi", 0, + base + RCC_CR, 3, 2, CLK_DIVIDER_POWER_OF_TWO, + &stm32rcc_lock); + + hws[HSE_1M] = clk_hw_register_divider(NULL, "hse_1M", "hse_ck", 0, + base + RCC_CFGR, 8, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, + &stm32rcc_lock); + + /* Mux system clocks */ + for (n = 0; n < ARRAY_SIZE(stm32_mclk); n++) + hws[MCLK_BANK + n] = clk_hw_register_mux(NULL, + stm32_mclk[n].name, + stm32_mclk[n].parents, + stm32_mclk[n].num_parents, + stm32_mclk[n].flags, + stm32_mclk[n].offset + base, + stm32_mclk[n].shift, + stm32_mclk[n].width, + 0, + &stm32rcc_lock); + + register_core_and_bus_clocks(); + + /* Oscillary clocks */ + for (n = 0; n < ARRAY_SIZE(stm32_oclk); n++) + hws[OSC_BANK + n] = clk_register_ready_gate(NULL, + stm32_oclk[n].name, + stm32_oclk[n].parent, + stm32_oclk[n].gate_offset + base, + stm32_oclk[n].bit_idx, + stm32_oclk[n].bit_rdy, + stm32_oclk[n].flags, + &stm32rcc_lock); + + hws[HSE_CK] = clk_register_ready_gate(NULL, + "hse_ck", + hse_clk, + RCC_CR + base, + 16, 17, + 0, + &stm32rcc_lock); + + hws[LSE_CK] = clk_register_ready_gate(NULL, + "lse_ck", + lse_clk, + RCC_BDCR + base, + 0, 1, + 0, + &stm32rcc_lock); + + hws[CSI_KER_DIV122 + n] = clk_hw_register_fixed_factor(NULL, + "csi_ker_div122", "csi_ker", 0, 1, 122); + + /* PLLs */ + for (n = 0; n < ARRAY_SIZE(stm32_pll); n++) { + int odf; + + /* Register the VCO */ + clk_register_stm32_pll(NULL, stm32_pll[n].name, + stm32_pll[n].parent_name, stm32_pll[n].flags, + stm32_pll[n].cfg, + &stm32rcc_lock); + + /* Register the 3 output dividers */ + for (odf = 0; odf < 3; odf++) { + int idx = n * 3 + odf; + + get_cfg_composite_div(&odf_clk_gcfg, &stm32_odf[n][odf], + &c_cfg, &stm32rcc_lock); + + hws[ODF_BANK + idx] = clk_hw_register_composite(NULL, + stm32_odf[n][odf].name, + stm32_odf[n][odf].parent_name, + stm32_odf[n][odf].num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + stm32_odf[n][odf].flags); + } + } + + /* Peripheral clocks */ + for (n = 0; n < ARRAY_SIZE(pclk); n++) + hws[PERIF_BANK + n] = clk_hw_register_gate(NULL, pclk[n].name, + pclk[n].parent, + pclk[n].flags, base + pclk[n].gate_offset, + pclk[n].bit_idx, pclk[n].flags, &stm32rcc_lock); + + /* Kernel clocks */ + for (n = 0; n < ARRAY_SIZE(kclk); n++) { + get_cfg_composite_div(&kernel_clk_cfg, &kclk[n], &c_cfg, + &stm32rcc_lock); + + hws[KERN_BANK + n] = clk_hw_register_composite(NULL, + kclk[n].name, + kclk[n].parent_name, + kclk[n].num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + kclk[n].flags); + } + + /* RTC clock (default state is off) */ + clk_hw_register_fixed_rate(NULL, "off", NULL, 0, 0); + + get_cfg_composite_div(&rtc_clk_cfg, &rtc_clk, &c_cfg, &stm32rcc_lock); + + hws[RTC_CK] = clk_hw_register_composite(NULL, + rtc_clk.name, + rtc_clk.parent_name, + rtc_clk.num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + rtc_clk.flags); + + /* Micro-controller clocks */ + for (n = 0; n < ARRAY_SIZE(mco_clk); n++) { + get_cfg_composite_div(&mco_clk_cfg, &mco_clk[n], &c_cfg, + &stm32rcc_lock); + + hws[MCO_BANK + n] = clk_hw_register_composite(NULL, + mco_clk[n].name, + mco_clk[n].parent_name, + mco_clk[n].num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + mco_clk[n].flags); + } + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); + + return; + +err_free_clks: + kfree(clk_data); +} + +/* The RCC node is a clock and reset controller, and these + * functionalities are supported by different drivers that + * matches the same compatible strings. + */ +CLK_OF_DECLARE_DRIVER(stm32h7_rcc, "st,stm32h743-rcc", stm32h7_rcc_init); diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index ea7d552a2f2b..decffb3826ec 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -57,6 +57,7 @@ #define VC5_PRIM_SRC_SHDN 0x10 #define VC5_PRIM_SRC_SHDN_EN_XTAL BIT(7) #define VC5_PRIM_SRC_SHDN_EN_CLKIN BIT(6) +#define VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ BIT(3) #define VC5_PRIM_SRC_SHDN_SP BIT(1) #define VC5_PRIM_SRC_SHDN_EN_GBL_SHDN BIT(0) @@ -122,12 +123,16 @@ /* flags to describe chip features */ /* chip has built-in oscilator */ #define VC5_HAS_INTERNAL_XTAL BIT(0) +/* chip has PFD requency doubler */ +#define VC5_HAS_PFD_FREQ_DBL BIT(1) /* Supported IDT VC5 models. */ enum vc5_model { IDT_VC5_5P49V5923, + IDT_VC5_5P49V5925, IDT_VC5_5P49V5933, IDT_VC5_5P49V5935, + IDT_VC6_5P49V6901, }; /* Structure to describe features of a particular VC5 model */ @@ -157,6 +162,8 @@ struct vc5_driver_data { struct clk *pin_clkin; unsigned char clk_mux_ins; struct clk_hw clk_mux; + struct clk_hw clk_mul; + struct clk_hw clk_pfd; struct vc5_hw_data clk_pll; struct vc5_hw_data clk_fod[VC5_MAX_FOD_NUM]; struct vc5_hw_data clk_out[VC5_MAX_CLK_OUT_NUM]; @@ -166,6 +173,14 @@ static const char * const vc5_mux_names[] = { "mux" }; +static const char * const vc5_dbl_names[] = { + "dbl" +}; + +static const char * const vc5_pfd_names[] = { + "pfd" +}; + static const char * const vc5_pll_names[] = { "pll" }; @@ -254,11 +269,64 @@ static int vc5_mux_set_parent(struct clk_hw *hw, u8 index) return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, mask, src); } -static unsigned long vc5_mux_recalc_rate(struct clk_hw *hw, +static const struct clk_ops vc5_mux_ops = { + .set_parent = vc5_mux_set_parent, + .get_parent = vc5_mux_get_parent, +}; + +static unsigned long vc5_dbl_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct vc5_driver_data *vc5 = - container_of(hw, struct vc5_driver_data, clk_mux); + container_of(hw, struct vc5_driver_data, clk_mul); + unsigned int premul; + + regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &premul); + if (premul & VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ) + parent_rate *= 2; + + return parent_rate; +} + +static long vc5_dbl_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + if ((*parent_rate == rate) || ((*parent_rate * 2) == rate)) + return rate; + else + return -EINVAL; +} + +static int vc5_dbl_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct vc5_driver_data *vc5 = + container_of(hw, struct vc5_driver_data, clk_mul); + u32 mask; + + if ((parent_rate * 2) == rate) + mask = VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ; + else + mask = 0; + + regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, + VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ, + mask); + + return 0; +} + +static const struct clk_ops vc5_dbl_ops = { + .recalc_rate = vc5_dbl_recalc_rate, + .round_rate = vc5_dbl_round_rate, + .set_rate = vc5_dbl_set_rate, +}; + +static unsigned long vc5_pfd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct vc5_driver_data *vc5 = + container_of(hw, struct vc5_driver_data, clk_pfd); unsigned int prediv, div; regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv); @@ -276,7 +344,7 @@ static unsigned long vc5_mux_recalc_rate(struct clk_hw *hw, return parent_rate / VC5_REF_DIVIDER_REF_DIV(div); } -static long vc5_mux_round_rate(struct clk_hw *hw, unsigned long rate, +static long vc5_pfd_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { unsigned long idiv; @@ -296,11 +364,11 @@ static long vc5_mux_round_rate(struct clk_hw *hw, unsigned long rate, return *parent_rate / idiv; } -static int vc5_mux_set_rate(struct clk_hw *hw, unsigned long rate, +static int vc5_pfd_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct vc5_driver_data *vc5 = - container_of(hw, struct vc5_driver_data, clk_mux); + container_of(hw, struct vc5_driver_data, clk_pfd); unsigned long idiv; u8 div; @@ -328,12 +396,10 @@ static int vc5_mux_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static const struct clk_ops vc5_mux_ops = { - .set_parent = vc5_mux_set_parent, - .get_parent = vc5_mux_get_parent, - .recalc_rate = vc5_mux_recalc_rate, - .round_rate = vc5_mux_round_rate, - .set_rate = vc5_mux_set_rate, +static const struct clk_ops vc5_pfd_ops = { + .recalc_rate = vc5_pfd_recalc_rate, + .round_rate = vc5_pfd_round_rate, + .set_rate = vc5_pfd_set_rate, }; /* @@ -426,6 +492,10 @@ static unsigned long vc5_fod_recalc_rate(struct clk_hw *hw, div_frc = (od_frc[0] << 22) | (od_frc[1] << 14) | (od_frc[2] << 6) | (od_frc[3] >> 2); + /* Avoid division by zero if the output is not configured. */ + if (div_int == 0 && div_frc == 0) + return 0; + /* The PLL divider has 12 integer bits and 30 fractional bits */ return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc); } @@ -503,6 +573,25 @@ static int vc5_clk_out_prepare(struct clk_hw *hw) { struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); struct vc5_driver_data *vc5 = hwdata->vc5; + const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM | + VC5_OUT_DIV_CONTROL_SEL_EXT | + VC5_OUT_DIV_CONTROL_EN_FOD; + unsigned int src; + int ret; + + /* + * If the input mux is disabled, enable it first and + * select source from matching FOD. + */ + regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src); + if ((src & mask) == 0) { + src = VC5_OUT_DIV_CONTROL_RESET | VC5_OUT_DIV_CONTROL_EN_FOD; + ret = regmap_update_bits(vc5->regmap, + VC5_OUT_DIV_CONTROL(hwdata->num), + mask | VC5_OUT_DIV_CONTROL_RESET, src); + if (ret) + return ret; + } /* Enable the clock buffer */ regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), @@ -516,7 +605,7 @@ static void vc5_clk_out_unprepare(struct clk_hw *hw) struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); struct vc5_driver_data *vc5 = hwdata->vc5; - /* Enable the clock buffer */ + /* Disable the clock buffer */ regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, 0); } @@ -537,6 +626,9 @@ static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw) regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src); src &= mask; + if (src == 0) /* Input mux set to DISABLED */ + return 0; + if ((src & fodclkmask) == VC5_OUT_DIV_CONTROL_EN_FOD) return 0; @@ -595,7 +687,9 @@ static int vc5_map_index_to_output(const enum vc5_model model, case IDT_VC5_5P49V5933: return (n == 0) ? 0 : 3; case IDT_VC5_5P49V5923: + case IDT_VC5_5P49V5925: case IDT_VC5_5P49V5935: + case IDT_VC6_5P49V6901: default: return n; } @@ -672,12 +766,46 @@ static int vc5_probe(struct i2c_client *client, goto err_clk; } + if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) { + /* Register frequency doubler */ + memset(&init, 0, sizeof(init)); + init.name = vc5_dbl_names[0]; + init.ops = &vc5_dbl_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = vc5_mux_names; + init.num_parents = 1; + vc5->clk_mul.init = &init; + ret = devm_clk_hw_register(&client->dev, &vc5->clk_mul); + if (ret) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + goto err_clk; + } + } + + /* Register PFD */ + memset(&init, 0, sizeof(init)); + init.name = vc5_pfd_names[0]; + init.ops = &vc5_pfd_ops; + init.flags = CLK_SET_RATE_PARENT; + if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) + init.parent_names = vc5_dbl_names; + else + init.parent_names = vc5_mux_names; + init.num_parents = 1; + vc5->clk_pfd.init = &init; + ret = devm_clk_hw_register(&client->dev, &vc5->clk_pfd); + if (ret) { + dev_err(&client->dev, "unable to register %s\n", init.name); + goto err_clk; + } + /* Register PLL */ memset(&init, 0, sizeof(init)); init.name = vc5_pll_names[0]; init.ops = &vc5_pll_ops; init.flags = CLK_SET_RATE_PARENT; - init.parent_names = vc5_mux_names; + init.parent_names = vc5_pfd_names; init.num_parents = 1; vc5->clk_pll.num = 0; vc5->clk_pll.vc5 = vc5; @@ -785,6 +913,13 @@ static const struct vc5_chip_info idt_5p49v5923_info = { .flags = 0, }; +static const struct vc5_chip_info idt_5p49v5925_info = { + .model = IDT_VC5_5P49V5925, + .clk_fod_cnt = 4, + .clk_out_cnt = 5, + .flags = 0, +}; + static const struct vc5_chip_info idt_5p49v5933_info = { .model = IDT_VC5_5P49V5933, .clk_fod_cnt = 2, @@ -799,18 +934,29 @@ static const struct vc5_chip_info idt_5p49v5935_info = { .flags = VC5_HAS_INTERNAL_XTAL, }; +static const struct vc5_chip_info idt_5p49v6901_info = { + .model = IDT_VC6_5P49V6901, + .clk_fod_cnt = 4, + .clk_out_cnt = 5, + .flags = VC5_HAS_PFD_FREQ_DBL, +}; + static const struct i2c_device_id vc5_id[] = { { "5p49v5923", .driver_data = IDT_VC5_5P49V5923 }, + { "5p49v5925", .driver_data = IDT_VC5_5P49V5925 }, { "5p49v5933", .driver_data = IDT_VC5_5P49V5933 }, { "5p49v5935", .driver_data = IDT_VC5_5P49V5935 }, + { "5p49v6901", .driver_data = IDT_VC6_5P49V6901 }, { } }; MODULE_DEVICE_TABLE(i2c, vc5_id); static const struct of_device_id clk_vc5_of_match[] = { { .compatible = "idt,5p49v5923", .data = &idt_5p49v5923_info }, + { .compatible = "idt,5p49v5925", .data = &idt_5p49v5925_info }, { .compatible = "idt,5p49v5933", .data = &idt_5p49v5933_info }, { .compatible = "idt,5p49v5935", .data = &idt_5p49v5935_info }, + { .compatible = "idt,5p49v6901", .data = &idt_5p49v6901_info }, { }, }; MODULE_DEVICE_TABLE(of, clk_vc5_of_match); diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index bc37030e38ba..4c75821a3933 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -192,7 +192,7 @@ static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_ty reg = of_iomap(np, 0); if (reg == NULL) { - pr_err("Unable to map CSR register for %s\n", np->full_name); + pr_err("Unable to map CSR register for %pOF\n", np); return; } of_property_read_string(np, "clock-output-names", &clk_name); @@ -409,12 +409,12 @@ static void xgene_pmdclk_init(struct device_node *np) /* Parse the DTS register for resource */ rc = of_address_to_resource(np, 0, &res); if (rc != 0) { - pr_err("no DTS register for %s\n", np->full_name); + pr_err("no DTS register for %pOF\n", np); return; } csr_reg = of_iomap(np, 0); if (!csr_reg) { - pr_err("Unable to map resource for %s\n", np->full_name); + pr_err("Unable to map resource for %pOF\n", np); return; } of_property_read_string(np, "clock-output-names", &clk_name); @@ -703,16 +703,14 @@ static void __init xgene_devclk_init(struct device_node *np) rc = of_address_to_resource(np, i, &res); if (rc != 0) { if (i == 0) { - pr_err("no DTS register for %s\n", - np->full_name); + pr_err("no DTS register for %pOF\n", np); return; } break; } map_res = of_iomap(np, i); if (map_res == NULL) { - pr_err("Unable to map resource %d for %s\n", - i, np->full_name); + pr_err("Unable to map resource %d for %pOF\n", i, np); goto err; } if (strcmp(res.name, "div-reg") == 0) @@ -747,8 +745,7 @@ static void __init xgene_devclk_init(struct device_node *np) pr_debug("Add %s clock\n", clk_name); rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); if (rc != 0) - pr_err("%s: could register provider clk %s\n", __func__, - np->full_name); + pr_err("%s: could register provider clk %pOF\n", __func__, np); return; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index fc58c52a26b4..c8d83acda006 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3132,7 +3132,7 @@ int of_clk_add_provider(struct device_node *np, mutex_lock(&of_clk_mutex); list_add(&cp->link, &of_clk_providers); mutex_unlock(&of_clk_mutex); - pr_debug("Added clock from %s\n", np->full_name); + pr_debug("Added clock from %pOF\n", np); ret = of_clk_set_defaults(np, true); if (ret < 0) @@ -3167,7 +3167,7 @@ int of_clk_add_hw_provider(struct device_node *np, mutex_lock(&of_clk_mutex); list_add(&cp->link, &of_clk_providers); mutex_unlock(&of_clk_mutex); - pr_debug("Added clk_hw provider from %s\n", np->full_name); + pr_debug("Added clk_hw provider from %pOF\n", np); ret = of_clk_set_defaults(np, true); if (ret < 0) diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index bb8a77a5985f..6b2f29df3f70 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -77,8 +77,8 @@ static struct clk *__of_clk_get_by_name(struct device_node *np, break; } else if (name && index >= 0) { if (PTR_ERR(clk) != -EPROBE_DEFER) - pr_err("ERROR: could not get clock %s:%s(%i)\n", - np->full_name, name ? name : "", index); + pr_err("ERROR: could not get clock %pOF:%s(%i)\n", + np, name ? name : "", index); return clk; } diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index 4181b6808545..e786d717f75d 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -55,9 +55,9 @@ static struct hisi_fixed_factor_clock hi6220_fixed_factor_clks[] __initdata = { }; static struct hisi_gate_clock hi6220_separated_gate_clks_ao[] __initdata = { - { HI6220_WDT0_PCLK, "wdt0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, }, - { HI6220_WDT1_PCLK, "wdt1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, }, - { HI6220_WDT2_PCLK, "wdt2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 14, 0, }, + { HI6220_WDT0_PCLK, "wdt0_pclk", "ref32k", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, }, + { HI6220_WDT1_PCLK, "wdt1_pclk", "ref32k", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, }, + { HI6220_WDT2_PCLK, "wdt2_pclk", "ref32k", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 14, 0, }, { HI6220_TIMER0_PCLK, "timer0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 15, 0, }, { HI6220_TIMER1_PCLK, "timer1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 16, 0, }, { HI6220_TIMER2_PCLK, "timer2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 17, 0, }, diff --git a/drivers/clk/imx/clk-imx51-imx53.c b/drivers/clk/imx/clk-imx51-imx53.c index 1e3c9ea5f9dc..7bcaf270db11 100644 --- a/drivers/clk/imx/clk-imx51-imx53.c +++ b/drivers/clk/imx/clk-imx51-imx53.c @@ -416,10 +416,10 @@ static void __init mx51_clocks_init(struct device_node *np) clk[IMX5_CLK_LP_APM] = imx_clk_mux("lp_apm", MXC_CCM_CCSR, 9, 1, lp_apm_sel, ARRAY_SIZE(lp_apm_sel)); - clk[IMX5_CLK_IPU_DI0_SEL] = imx_clk_mux("ipu_di0_sel", MXC_CCM_CSCMR2, 26, 3, - mx51_ipu_di0_sel, ARRAY_SIZE(mx51_ipu_di0_sel)); - clk[IMX5_CLK_IPU_DI1_SEL] = imx_clk_mux("ipu_di1_sel", MXC_CCM_CSCMR2, 29, 3, - mx51_ipu_di1_sel, ARRAY_SIZE(mx51_ipu_di1_sel)); + clk[IMX5_CLK_IPU_DI0_SEL] = imx_clk_mux_flags("ipu_di0_sel", MXC_CCM_CSCMR2, 26, 3, + mx51_ipu_di0_sel, ARRAY_SIZE(mx51_ipu_di0_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_IPU_DI1_SEL] = imx_clk_mux_flags("ipu_di1_sel", MXC_CCM_CSCMR2, 29, 3, + mx51_ipu_di1_sel, ARRAY_SIZE(mx51_ipu_di1_sel), CLK_SET_RATE_PARENT); clk[IMX5_CLK_TVE_EXT_SEL] = imx_clk_mux_flags("tve_ext_sel", MXC_CCM_CSCMR1, 6, 1, mx51_tve_ext_sel, ARRAY_SIZE(mx51_tve_ext_sel), CLK_SET_RATE_PARENT); clk[IMX5_CLK_TVE_SEL] = imx_clk_mux("tve_sel", MXC_CCM_CSCMR1, 7, 1, diff --git a/drivers/clk/imx/clk-imx6sl.c b/drivers/clk/imx/clk-imx6sl.c index 5fd4ddac1bf1..9642cdf0fb88 100644 --- a/drivers/clk/imx/clk-imx6sl.c +++ b/drivers/clk/imx/clk-imx6sl.c @@ -71,7 +71,7 @@ static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", }; static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", }; static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; -static struct clk_div_table clk_enet_ref_table[] = { +static const struct clk_div_table clk_enet_ref_table[] = { { .val = 0, .div = 20, }, { .val = 1, .div = 10, }, { .val = 2, .div = 5, }, @@ -79,14 +79,14 @@ static struct clk_div_table clk_enet_ref_table[] = { { } }; -static struct clk_div_table post_div_table[] = { +static const struct clk_div_table post_div_table[] = { { .val = 2, .div = 1, }, { .val = 1, .div = 2, }, { .val = 0, .div = 4, }, { } }; -static struct clk_div_table video_div_table[] = { +static const struct clk_div_table video_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 2, .div = 1, }, diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index b5c96de41ccf..e6d389e333d7 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -105,7 +105,7 @@ static int const clks_init_on[] __initconst = { IMX6SX_CLK_EPIT2, }; -static struct clk_div_table clk_enet_ref_table[] = { +static const struct clk_div_table clk_enet_ref_table[] = { { .val = 0, .div = 20, }, { .val = 1, .div = 10, }, { .val = 2, .div = 5, }, @@ -113,14 +113,14 @@ static struct clk_div_table clk_enet_ref_table[] = { { } }; -static struct clk_div_table post_div_table[] = { +static const struct clk_div_table post_div_table[] = { { .val = 2, .div = 1, }, { .val = 1, .div = 2, }, { .val = 0, .div = 4, }, { } }; -static struct clk_div_table video_div_table[] = { +static const struct clk_div_table video_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 2, .div = 1, }, diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index b4e0dff3c8c2..5e8c18afce9a 100644 --- a/drivers/clk/imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c @@ -78,7 +78,7 @@ static int const clks_init_on[] __initconst = { IMX6UL_CLK_MMDC_P0_FAST, IMX6UL_CLK_MMDC_P0_IPG, }; -static struct clk_div_table clk_enet_ref_table[] = { +static const struct clk_div_table clk_enet_ref_table[] = { { .val = 0, .div = 20, }, { .val = 1, .div = 10, }, { .val = 2, .div = 5, }, @@ -86,14 +86,14 @@ static struct clk_div_table clk_enet_ref_table[] = { { } }; -static struct clk_div_table post_div_table[] = { +static const struct clk_div_table post_div_table[] = { { .val = 2, .div = 1, }, { .val = 1, .div = 2, }, { .val = 0, .div = 4, }, { } }; -static struct clk_div_table video_div_table[] = { +static const struct clk_div_table video_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 2, .div = 1, }, diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index 3da121826b1b..2305699db467 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -27,7 +27,7 @@ static u32 share_count_sai2; static u32 share_count_sai3; static u32 share_count_nand; -static struct clk_div_table test_div_table[] = { +static const struct clk_div_table test_div_table[] = { { .val = 3, .div = 1, }, { .val = 2, .div = 1, }, { .val = 1, .div = 2, }, @@ -35,7 +35,7 @@ static struct clk_div_table test_div_table[] = { { } }; -static struct clk_div_table post_div_table[] = { +static const struct clk_div_table post_div_table[] = { { .val = 3, .div = 4, }, { .val = 2, .div = 1, }, { .val = 1, .div = 2, }, diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c index 59b1863deb88..6dae54325a91 100644 --- a/drivers/clk/imx/clk-vf610.c +++ b/drivers/clk/imx/clk-vf610.c @@ -102,7 +102,7 @@ static const char *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ static const char *ftm_fix_sels[] = { "sxosc", "ipg_bus", }; -static struct clk_div_table pll4_audio_div_table[] = { +static const struct clk_div_table pll4_audio_div_table[] = { { .val = 0, .div = 1 }, { .val = 1, .div = 2 }, { .val = 2, .div = 6 }, diff --git a/drivers/clk/mediatek/clk-cpumux.c b/drivers/clk/mediatek/clk-cpumux.c index edd8e6918050..16e56772d280 100644 --- a/drivers/clk/mediatek/clk-cpumux.c +++ b/drivers/clk/mediatek/clk-cpumux.c @@ -27,7 +27,6 @@ static inline struct mtk_clk_cpumux *to_mtk_clk_cpumux(struct clk_hw *_hw) static u8 clk_cpumux_get_parent(struct clk_hw *hw) { struct mtk_clk_cpumux *mux = to_mtk_clk_cpumux(hw); - int num_parents = clk_hw_get_num_parents(hw); unsigned int val; regmap_read(mux->regmap, mux->reg, &val); @@ -35,9 +34,6 @@ static u8 clk_cpumux_get_parent(struct clk_hw *hw) val >>= mux->shift; val &= mux->mask; - if (val >= num_parents) - return -EINVAL; - return val; } @@ -98,7 +94,7 @@ int __init mtk_clk_register_cpumuxes(struct device_node *node, regmap = syscon_node_to_regmap(node); if (IS_ERR(regmap)) { - pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + pr_err("Cannot find regmap for %pOF: %ld\n", node, PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c index 0541df78141c..9c0ae4278a94 100644 --- a/drivers/clk/mediatek/clk-mtk.c +++ b/drivers/clk/mediatek/clk-mtk.c @@ -114,7 +114,7 @@ int mtk_clk_register_gates(struct device_node *node, regmap = syscon_node_to_regmap(node); if (IS_ERR(regmap)) { - pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + pr_err("Cannot find regmap for %pOF: %ld\n", node, PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c index 309049d41f1b..d3551d5efef2 100644 --- a/drivers/clk/mediatek/reset.c +++ b/drivers/clk/mediatek/reset.c @@ -72,7 +72,7 @@ void mtk_register_reset_controller(struct device_node *np, regmap = syscon_node_to_regmap(np); if (IS_ERR(regmap)) { - pr_err("Cannot find regmap for %s: %ld\n", np->full_name, + pr_err("Cannot find regmap for %pOF: %ld\n", np, PTR_ERR(regmap)); return; } diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index 5588f75a8414..d2d0174a6eca 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -6,6 +6,7 @@ config COMMON_CLK_AMLOGIC config COMMON_CLK_MESON8B bool depends on COMMON_CLK_AMLOGIC + select RESET_CONTROLLER help Support for the clock controller on AmLogic S802 (Meson8), S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index 83b6d9d65aa1..b139d41b25da 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o clk-audio-divider.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o -obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o +obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-regmap.o gxbb-aoclk-32k.o diff --git a/drivers/clk/meson/gxbb-aoclk-32k.c b/drivers/clk/meson/gxbb-aoclk-32k.c new file mode 100644 index 000000000000..491634dbc985 --- /dev/null +++ b/drivers/clk/meson/gxbb-aoclk-32k.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2017 BayLibre, SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/clk-provider.h> +#include <linux/bitfield.h> +#include <linux/regmap.h> +#include "gxbb-aoclk.h" + +/* + * The AO Domain embeds a dual/divider to generate a more precise + * 32,768KHz clock for low-power suspend mode and CEC. + * ______ ______ + * | | | | + * ______ | Div1 |-| Cnt1 | ______ + * | | /|______| |______|\ | | + * Xtal-->| Gate |---| ______ ______ X-X--| Gate |--> + * |______| | \| | | |/ | |______| + * | | Div2 |-| Cnt2 | | + * | |______| |______| | + * |_______________________| + * + * The dividing can be switched to single or dual, with a counter + * for each divider to set when the switching is done. + * The entire dividing mechanism can be also bypassed. + */ + +#define CLK_CNTL0_N1_MASK GENMASK(11, 0) +#define CLK_CNTL0_N2_MASK GENMASK(23, 12) +#define CLK_CNTL0_DUALDIV_EN BIT(28) +#define CLK_CNTL0_OUT_GATE_EN BIT(30) +#define CLK_CNTL0_IN_GATE_EN BIT(31) + +#define CLK_CNTL1_M1_MASK GENMASK(11, 0) +#define CLK_CNTL1_M2_MASK GENMASK(23, 12) +#define CLK_CNTL1_BYPASS_EN BIT(24) +#define CLK_CNTL1_SELECT_OSC BIT(27) + +#define PWR_CNTL_ALT_32K_SEL GENMASK(13, 10) + +struct cec_32k_freq_table { + unsigned long parent_rate; + unsigned long target_rate; + bool dualdiv; + unsigned int n1; + unsigned int n2; + unsigned int m1; + unsigned int m2; +}; + +static const struct cec_32k_freq_table aoclk_cec_32k_table[] = { + [0] = { + .parent_rate = 24000000, + .target_rate = 32768, + .dualdiv = true, + .n1 = 733, + .n2 = 732, + .m1 = 8, + .m2 = 11, + }, +}; + +/* + * If CLK_CNTL0_DUALDIV_EN == 0 + * - will use N1 divider only + * If CLK_CNTL0_DUALDIV_EN == 1 + * - hold M1 cycles of N1 divider then changes to N2 + * - hold M2 cycles of N2 divider then changes to N1 + * Then we can get more accurate division. + */ +static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw); + unsigned long n1; + u32 reg0, reg1; + + regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, ®0); + regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, ®1); + + if (reg1 & CLK_CNTL1_BYPASS_EN) + return parent_rate; + + if (reg0 & CLK_CNTL0_DUALDIV_EN) { + unsigned long n2, m1, m2, f1, f2, p1, p2; + + n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1; + n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1; + + m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1; + m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1; + + f1 = DIV_ROUND_CLOSEST(parent_rate, n1); + f2 = DIV_ROUND_CLOSEST(parent_rate, n2); + + p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2)); + p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2)); + + return DIV_ROUND_UP(100000000, p1 + p2); + } + + n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1; + + return DIV_ROUND_CLOSEST(parent_rate, n1); +} + +static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate, + unsigned long prate) +{ + int i; + + for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i) + if (aoclk_cec_32k_table[i].parent_rate == prate && + aoclk_cec_32k_table[i].target_rate == rate) + return &aoclk_cec_32k_table[i]; + + return NULL; +} + +static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate, + *prate); + + /* If invalid return first one */ + if (!freq) + return aoclk_cec_32k_table[0].target_rate; + + return freq->target_rate; +} + +/* + * From the Amlogic init procedure, the IN and OUT gates needs to be handled + * in the init procedure to avoid any glitches. + */ + +static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate, + parent_rate); + struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw); + u32 reg = 0; + + if (!freq) + return -EINVAL; + + /* Disable clock */ + regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, + CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN, 0); + + reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1); + if (freq->dualdiv) + reg |= CLK_CNTL0_DUALDIV_EN | + FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1); + + regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, reg); + + reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1); + if (freq->dualdiv) + reg |= FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1); + + regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, reg); + + /* Enable clock */ + regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, + CLK_CNTL0_IN_GATE_EN, CLK_CNTL0_IN_GATE_EN); + + udelay(200); + + regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, + CLK_CNTL0_OUT_GATE_EN, CLK_CNTL0_OUT_GATE_EN); + + regmap_update_bits(cec_32k->regmap, AO_CRT_CLK_CNTL1, + CLK_CNTL1_SELECT_OSC, CLK_CNTL1_SELECT_OSC); + + /* Select 32k from XTAL */ + regmap_update_bits(cec_32k->regmap, + AO_RTI_PWR_CNTL_REG0, + PWR_CNTL_ALT_32K_SEL, + FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4)); + + return 0; +} + +const struct clk_ops meson_aoclk_cec_32k_ops = { + .recalc_rate = aoclk_cec_32k_recalc_rate, + .round_rate = aoclk_cec_32k_round_rate, + .set_rate = aoclk_cec_32k_set_rate, +}; diff --git a/drivers/clk/meson/gxbb-aoclk-regmap.c b/drivers/clk/meson/gxbb-aoclk-regmap.c new file mode 100644 index 000000000000..2515fbfa0467 --- /dev/null +++ b/drivers/clk/meson/gxbb-aoclk-regmap.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 BayLibre, SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/clk-provider.h> +#include <linux/bitfield.h> +#include <linux/regmap.h> +#include "gxbb-aoclk.h" + +static int aoclk_gate_regmap_enable(struct clk_hw *hw) +{ + struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw); + + return regmap_update_bits(gate->regmap, AO_RTI_GEN_CNTL_REG0, + BIT(gate->bit_idx), BIT(gate->bit_idx)); +} + +static void aoclk_gate_regmap_disable(struct clk_hw *hw) +{ + struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw); + + regmap_update_bits(gate->regmap, AO_RTI_GEN_CNTL_REG0, + BIT(gate->bit_idx), 0); +} + +static int aoclk_gate_regmap_is_enabled(struct clk_hw *hw) +{ + struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw); + unsigned int val; + int ret; + + ret = regmap_read(gate->regmap, AO_RTI_GEN_CNTL_REG0, &val); + if (ret) + return ret; + + return (val & BIT(gate->bit_idx)) != 0; +} + +const struct clk_ops meson_aoclk_gate_regmap_ops = { + .enable = aoclk_gate_regmap_enable, + .disable = aoclk_gate_regmap_disable, + .is_enabled = aoclk_gate_regmap_is_enabled, +}; diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c index b45c5fba7e35..6c161e0a8e59 100644 --- a/drivers/clk/meson/gxbb-aoclk.c +++ b/drivers/clk/meson/gxbb-aoclk.c @@ -56,16 +56,20 @@ #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/reset-controller.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> #include <linux/init.h> +#include <linux/delay.h> #include <dt-bindings/clock/gxbb-aoclkc.h> #include <dt-bindings/reset/gxbb-aoclkc.h> +#include "gxbb-aoclk.h" static DEFINE_SPINLOCK(gxbb_aoclk_lock); struct gxbb_aoclk_reset_controller { struct reset_controller_dev reset; unsigned int *data; - void __iomem *base; + struct regmap *regmap; }; static int gxbb_aoclk_do_reset(struct reset_controller_dev *rcdev, @@ -74,9 +78,8 @@ static int gxbb_aoclk_do_reset(struct reset_controller_dev *rcdev, struct gxbb_aoclk_reset_controller *reset = container_of(rcdev, struct gxbb_aoclk_reset_controller, reset); - writel(BIT(reset->data[id]), reset->base); - - return 0; + return regmap_write(reset->regmap, AO_RTI_GEN_CNTL_REG0, + BIT(reset->data[id])); } static const struct reset_control_ops gxbb_aoclk_reset_ops = { @@ -84,13 +87,12 @@ static const struct reset_control_ops gxbb_aoclk_reset_ops = { }; #define GXBB_AO_GATE(_name, _bit) \ -static struct clk_gate _name##_ao = { \ - .reg = (void __iomem *)0, \ +static struct aoclk_gate_regmap _name##_ao = { \ .bit_idx = (_bit), \ .lock = &gxbb_aoclk_lock, \ .hw.init = &(struct clk_init_data) { \ .name = #_name "_ao", \ - .ops = &clk_gate_ops, \ + .ops = &meson_aoclk_gate_regmap_ops, \ .parent_names = (const char *[]){ "clk81" }, \ .num_parents = 1, \ .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ @@ -104,6 +106,17 @@ GXBB_AO_GATE(uart1, 3); GXBB_AO_GATE(uart2, 5); GXBB_AO_GATE(ir_blaster, 6); +static struct aoclk_cec_32k cec_32k_ao = { + .lock = &gxbb_aoclk_lock, + .hw.init = &(struct clk_init_data) { + .name = "cec_32k_ao", + .ops = &meson_aoclk_cec_32k_ops, + .parent_names = (const char *[]){ "xtal" }, + .num_parents = 1, + .flags = CLK_IGNORE_UNUSED, + }, +}; + static unsigned int gxbb_aoclk_reset[] = { [RESET_AO_REMOTE] = 16, [RESET_AO_I2C_MASTER] = 18, @@ -113,7 +126,7 @@ static unsigned int gxbb_aoclk_reset[] = { [RESET_AO_IR_BLASTER] = 23, }; -static struct clk_gate *gxbb_aoclk_gate[] = { +static struct aoclk_gate_regmap *gxbb_aoclk_gate[] = { [CLKID_AO_REMOTE] = &remote_ao, [CLKID_AO_I2C_MASTER] = &i2c_master_ao, [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao, @@ -130,30 +143,30 @@ static struct clk_hw_onecell_data gxbb_aoclk_onecell_data = { [CLKID_AO_UART1] = &uart1_ao.hw, [CLKID_AO_UART2] = &uart2_ao.hw, [CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw, + [CLKID_AO_CEC_32K] = &cec_32k_ao.hw, }, - .num = ARRAY_SIZE(gxbb_aoclk_gate), + .num = 7, }; static int gxbb_aoclkc_probe(struct platform_device *pdev) { - struct resource *res; - void __iomem *base; - int ret, clkid; - struct device *dev = &pdev->dev; struct gxbb_aoclk_reset_controller *rstc; + struct device *dev = &pdev->dev; + struct regmap *regmap; + int ret, clkid; rstc = devm_kzalloc(dev, sizeof(*rstc), GFP_KERNEL); if (!rstc) return -ENOMEM; - /* Generic clocks */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + regmap = syscon_node_to_regmap(of_get_parent(dev->of_node)); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to get regmap\n"); + return -ENODEV; + } /* Reset Controller */ - rstc->base = base; + rstc->regmap = regmap; rstc->data = gxbb_aoclk_reset; rstc->reset.ops = &gxbb_aoclk_reset_ops; rstc->reset.nr_resets = ARRAY_SIZE(gxbb_aoclk_reset); @@ -161,10 +174,10 @@ static int gxbb_aoclkc_probe(struct platform_device *pdev) ret = devm_reset_controller_register(dev, &rstc->reset); /* - * Populate base address and register all clks + * Populate regmap and register all clks */ - for (clkid = 0; clkid < gxbb_aoclk_onecell_data.num; clkid++) { - gxbb_aoclk_gate[clkid]->reg = base; + for (clkid = 0; clkid < ARRAY_SIZE(gxbb_aoclk_gate); clkid++) { + gxbb_aoclk_gate[clkid]->regmap = regmap; ret = devm_clk_hw_register(dev, gxbb_aoclk_onecell_data.hws[clkid]); @@ -172,12 +185,18 @@ static int gxbb_aoclkc_probe(struct platform_device *pdev) return ret; } + /* Specific clocks */ + cec_32k_ao.regmap = regmap; + ret = devm_clk_hw_register(dev, &cec_32k_ao.hw); + if (ret) + return ret; + return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, &gxbb_aoclk_onecell_data); } static const struct of_device_id gxbb_aoclkc_match_table[] = { - { .compatible = "amlogic,gxbb-aoclkc" }, + { .compatible = "amlogic,meson-gx-aoclkc" }, { } }; diff --git a/drivers/clk/meson/gxbb-aoclk.h b/drivers/clk/meson/gxbb-aoclk.h new file mode 100644 index 000000000000..e8604c8f7eee --- /dev/null +++ b/drivers/clk/meson/gxbb-aoclk.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __GXBB_AOCLKC_H +#define __GXBB_AOCLKC_H + +/* AO Configuration Clock registers offsets */ +#define AO_RTI_PWR_CNTL_REG1 0x0c +#define AO_RTI_PWR_CNTL_REG0 0x10 +#define AO_RTI_GEN_CNTL_REG0 0x40 +#define AO_OSCIN_CNTL 0x58 +#define AO_CRT_CLK_CNTL1 0x68 +#define AO_RTC_ALT_CLK_CNTL0 0x94 +#define AO_RTC_ALT_CLK_CNTL1 0x98 + +struct aoclk_gate_regmap { + struct clk_hw hw; + unsigned bit_idx; + struct regmap *regmap; + spinlock_t *lock; +}; + +#define to_aoclk_gate_regmap(_hw) \ + container_of(_hw, struct aoclk_gate_regmap, hw) + +extern const struct clk_ops meson_aoclk_gate_regmap_ops; + +struct aoclk_cec_32k { + struct clk_hw hw; + struct regmap *regmap; + spinlock_t *lock; +}; + +#define to_aoclk_cec_32k(_hw) container_of(_hw, struct aoclk_cec_32k, hw) + +extern const struct clk_ops meson_aoclk_cec_32k_ops; + +#endif /* __GXBB_AOCLKC_H */ diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index a7ea5f3da89d..b2d1e8ed7152 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -850,13 +850,14 @@ static struct meson_clk_audio_divider gxbb_cts_amclk_div = { .shift = 0, .width = 8, }, + .flags = CLK_DIVIDER_ROUND_CLOSEST, .lock = &clk_lock, .hw.init = &(struct clk_init_data){ .name = "cts_amclk_div", .ops = &meson_clk_audio_divider_ops, .parent_names = (const char *[]){ "cts_amclk_sel" }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT | CLK_DIVIDER_ROUND_CLOSEST, + .flags = CLK_SET_RATE_PARENT, }, }; @@ -880,7 +881,7 @@ static struct clk_mux gxbb_cts_mclk_i958_sel = { /* Default parent unknown (register reset value: 0) */ .table = (u32[]){ 1, 2, 3 }, .lock = &clk_lock, - .hw.init = &(struct clk_init_data){ + .hw.init = &(struct clk_init_data) { .name = "cts_mclk_i958_sel", .ops = &clk_mux_ops, .parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" }, @@ -894,12 +895,13 @@ static struct clk_divider gxbb_cts_mclk_i958_div = { .shift = 16, .width = 8, .lock = &clk_lock, - .hw.init = &(struct clk_init_data){ + .flags = CLK_DIVIDER_ROUND_CLOSEST, + .hw.init = &(struct clk_init_data) { .name = "cts_mclk_i958_div", .ops = &clk_divider_ops, .parent_names = (const char *[]){ "cts_mclk_i958_sel" }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT | CLK_DIVIDER_ROUND_CLOSEST, + .flags = CLK_SET_RATE_PARENT, }, }; @@ -979,6 +981,156 @@ static struct clk_mux gxbb_32k_clk_sel = { }, }; +static const char * const gxbb_sd_emmc_clk0_parent_names[] = { + "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", + + /* + * Following these parent clocks, we should also have had mpll2, mpll3 + * and gp0_pll but these clocks are too precious to be used here. All + * the necessary rates for MMC and NAND operation can be acheived using + * xtal or fclk_div clocks + */ +}; + +/* SDIO clock */ +static struct clk_mux gxbb_sd_emmc_a_clk0_sel = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .mask = 0x7, + .shift = 9, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_a_clk0_sel", + .ops = &clk_mux_ops, + .parent_names = gxbb_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_divider gxbb_sd_emmc_a_clk0_div = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .shift = 0, + .width = 7, + .lock = &clk_lock, + .flags = CLK_DIVIDER_ROUND_CLOSEST, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_a_clk0_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_a_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_gate gxbb_sd_emmc_a_clk0 = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .bit_idx = 7, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_a_clk0", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_a_clk0_div" }, + .num_parents = 1, + + /* + * FIXME: + * We need CLK_IGNORE_UNUSED because mmc DT node point to xtal + * instead of this clock. CCF would gate this on boot, killing + * the mmc controller. Please remove this flag once DT properly + * point to this clock instead of xtal + * + * Same goes for emmc B and C clocks + */ + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +/* SDcard clock */ +static struct clk_mux gxbb_sd_emmc_b_clk0_sel = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .mask = 0x7, + .shift = 25, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_b_clk0_sel", + .ops = &clk_mux_ops, + .parent_names = gxbb_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_divider gxbb_sd_emmc_b_clk0_div = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .shift = 16, + .width = 7, + .lock = &clk_lock, + .flags = CLK_DIVIDER_ROUND_CLOSEST, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_b_clk0_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_b_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_gate gxbb_sd_emmc_b_clk0 = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .bit_idx = 23, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_b_clk0", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_b_clk0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +/* EMMC/NAND clock */ +static struct clk_mux gxbb_sd_emmc_c_clk0_sel = { + .reg = (void *)HHI_NAND_CLK_CNTL, + .mask = 0x7, + .shift = 9, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_c_clk0_sel", + .ops = &clk_mux_ops, + .parent_names = gxbb_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_divider gxbb_sd_emmc_c_clk0_div = { + .reg = (void *)HHI_NAND_CLK_CNTL, + .shift = 0, + .width = 7, + .lock = &clk_lock, + .flags = CLK_DIVIDER_ROUND_CLOSEST, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_c_clk0_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_c_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_gate gxbb_sd_emmc_c_clk0 = { + .reg = (void *)HHI_NAND_CLK_CNTL, + .bit_idx = 7, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_c_clk0", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_c_clk0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + /* Everything Else (EE) domain gates */ static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0); static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1); @@ -1188,6 +1340,16 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = { [CLKID_32K_CLK] = &gxbb_32k_clk.hw, [CLKID_32K_CLK_SEL] = &gxbb_32k_clk_sel.hw, [CLKID_32K_CLK_DIV] = &gxbb_32k_clk_div.hw, + [CLKID_SD_EMMC_A_CLK0_SEL] = &gxbb_sd_emmc_a_clk0_sel.hw, + [CLKID_SD_EMMC_A_CLK0_DIV] = &gxbb_sd_emmc_a_clk0_div.hw, + [CLKID_SD_EMMC_A_CLK0] = &gxbb_sd_emmc_a_clk0.hw, + [CLKID_SD_EMMC_B_CLK0_SEL] = &gxbb_sd_emmc_b_clk0_sel.hw, + [CLKID_SD_EMMC_B_CLK0_DIV] = &gxbb_sd_emmc_b_clk0_div.hw, + [CLKID_SD_EMMC_B_CLK0] = &gxbb_sd_emmc_b_clk0.hw, + [CLKID_SD_EMMC_C_CLK0_SEL] = &gxbb_sd_emmc_c_clk0_sel.hw, + [CLKID_SD_EMMC_C_CLK0_DIV] = &gxbb_sd_emmc_c_clk0_div.hw, + [CLKID_SD_EMMC_C_CLK0] = &gxbb_sd_emmc_c_clk0.hw, + [NR_CLKS] = NULL, }, .num = NR_CLKS, }; @@ -1310,6 +1472,16 @@ static struct clk_hw_onecell_data gxl_hw_onecell_data = { [CLKID_32K_CLK] = &gxbb_32k_clk.hw, [CLKID_32K_CLK_SEL] = &gxbb_32k_clk_sel.hw, [CLKID_32K_CLK_DIV] = &gxbb_32k_clk_div.hw, + [CLKID_SD_EMMC_A_CLK0_SEL] = &gxbb_sd_emmc_a_clk0_sel.hw, + [CLKID_SD_EMMC_A_CLK0_DIV] = &gxbb_sd_emmc_a_clk0_div.hw, + [CLKID_SD_EMMC_A_CLK0] = &gxbb_sd_emmc_a_clk0.hw, + [CLKID_SD_EMMC_B_CLK0_SEL] = &gxbb_sd_emmc_b_clk0_sel.hw, + [CLKID_SD_EMMC_B_CLK0_DIV] = &gxbb_sd_emmc_b_clk0_div.hw, + [CLKID_SD_EMMC_B_CLK0] = &gxbb_sd_emmc_b_clk0.hw, + [CLKID_SD_EMMC_C_CLK0_SEL] = &gxbb_sd_emmc_c_clk0_sel.hw, + [CLKID_SD_EMMC_C_CLK0_DIV] = &gxbb_sd_emmc_c_clk0_div.hw, + [CLKID_SD_EMMC_C_CLK0] = &gxbb_sd_emmc_c_clk0.hw, + [NR_CLKS] = NULL, }, .num = NR_CLKS, }; @@ -1425,6 +1597,9 @@ static struct clk_gate *const gxbb_clk_gates[] = { &gxbb_cts_amclk, &gxbb_cts_mclk_i958, &gxbb_32k_clk, + &gxbb_sd_emmc_a_clk0, + &gxbb_sd_emmc_b_clk0, + &gxbb_sd_emmc_c_clk0, }; static struct clk_mux *const gxbb_clk_muxes[] = { @@ -1437,6 +1612,9 @@ static struct clk_mux *const gxbb_clk_muxes[] = { &gxbb_cts_mclk_i958_sel, &gxbb_cts_i958, &gxbb_32k_clk_sel, + &gxbb_sd_emmc_a_clk0_sel, + &gxbb_sd_emmc_b_clk0_sel, + &gxbb_sd_emmc_c_clk0_sel, }; static struct clk_divider *const gxbb_clk_dividers[] = { @@ -1446,6 +1624,9 @@ static struct clk_divider *const gxbb_clk_dividers[] = { &gxbb_mali_1_div, &gxbb_cts_mclk_i958_div, &gxbb_32k_clk_div, + &gxbb_sd_emmc_a_clk0_div, + &gxbb_sd_emmc_b_clk0_div, + &gxbb_sd_emmc_c_clk0_div, }; static struct meson_clk_audio_divider *const gxbb_audio_dividers[] = { diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h index d63e77e8433d..5b1d4b374d1c 100644 --- a/drivers/clk/meson/gxbb.h +++ b/drivers/clk/meson/gxbb.h @@ -167,130 +167,33 @@ * CLKID index values * * These indices are entirely contrived and do not map onto the hardware. - * Migrate them out of this header and into the DT header file when they need - * to be exposed to client nodes in DT: include/dt-bindings/clock/gxbb-clkc.h + * It has now been decided to expose everything by default in the DT header: + * include/dt-bindings/clock/gxbb-clkc.h. Only the clocks ids we don't want + * to expose, such as the internal muxes and dividers of composite clocks, + * will remain defined here. */ -#define CLKID_SYS_PLL 0 /* ID 1 is unused (it was used by the non-existing CLKID_CPUCLK before) */ -/* CLKID_HDMI_PLL */ -#define CLKID_FIXED_PLL 3 -/* CLKID_FCLK_DIV2 */ -/* CLKID_FCLK_DIV3 */ -/* CLKID_FCLK_DIV4 */ -#define CLKID_FCLK_DIV5 7 -#define CLKID_FCLK_DIV7 8 -/* CLKID_GP0_PLL */ #define CLKID_MPEG_SEL 10 #define CLKID_MPEG_DIV 11 -/* CLKID_CLK81 */ -#define CLKID_MPLL0 13 -#define CLKID_MPLL1 14 -/* CLKID_MPLL2 */ -#define CLKID_DDR 16 -#define CLKID_DOS 17 -#define CLKID_ISA 18 -#define CLKID_PL301 19 -#define CLKID_PERIPHS 20 -/* CLKID_SPICC */ -/* CLKID_I2C */ -/* #define CLKID_SAR_ADC */ -#define CLKID_SMART_CARD 24 -/* CLKID_RNG0 */ -/* CLKID_UART0 */ -#define CLKID_SDHC 27 -#define CLKID_STREAM 28 -#define CLKID_ASYNC_FIFO 29 -#define CLKID_SDIO 30 -#define CLKID_ABUF 31 -#define CLKID_HIU_IFACE 32 -#define CLKID_ASSIST_MISC 33 -/* CLKID_SPI */ -#define CLKID_I2S_SPDIF 35 -/* CLKID_ETH */ -#define CLKID_DEMUX 37 -/* CLKID_AIU_GLUE */ -/* CLKID_IEC958 */ -/* CLKID_I2S_OUT */ -#define CLKID_AMCLK 41 -#define CLKID_AIFIFO2 42 -#define CLKID_MIXER 43 -/* CLKID_MIXER_IFACE */ -#define CLKID_ADC 45 -#define CLKID_BLKMV 46 -/* CLKID_AIU */ -/* CLKID_UART1 */ -#define CLKID_G2D 49 -/* CLKID_USB0 */ -/* CLKID_USB1 */ -#define CLKID_RESET 52 -#define CLKID_NAND 53 -#define CLKID_DOS_PARSER 54 -/* CLKID_USB */ -#define CLKID_VDIN1 56 -#define CLKID_AHB_ARB0 57 -#define CLKID_EFUSE 58 -#define CLKID_BOOT_ROM 59 -#define CLKID_AHB_DATA_BUS 60 -#define CLKID_AHB_CTRL_BUS 61 -#define CLKID_HDMI_INTR_SYNC 62 -/* CLKID_HDMI_PCLK */ -/* CLKID_USB1_DDR_BRIDGE */ -/* CLKID_USB0_DDR_BRIDGE */ -#define CLKID_MMC_PCLK 66 -#define CLKID_DVIN 67 -/* CLKID_UART2 */ -/* #define CLKID_SANA */ -#define CLKID_VPU_INTR 70 -#define CLKID_SEC_AHB_AHB3_BRIDGE 71 -#define CLKID_CLK81_A53 72 -#define CLKID_VCLK2_VENCI0 73 -#define CLKID_VCLK2_VENCI1 74 -#define CLKID_VCLK2_VENCP0 75 -#define CLKID_VCLK2_VENCP1 76 -/* CLKID_GCLK_VENCI_INT0 */ -#define CLKID_GCLK_VENCI_INT 78 -#define CLKID_DAC_CLK 79 -/* CLKID_AOCLK_GATE */ -/* CLKID_IEC958_GATE */ -#define CLKID_ENC480P 82 -#define CLKID_RNG1 83 -#define CLKID_GCLK_VENCI_INT1 84 -#define CLKID_VCLK2_VENCLMCC 85 -#define CLKID_VCLK2_VENCL 86 -#define CLKID_VCLK_OTHER 87 -#define CLKID_EDP 88 -#define CLKID_AO_MEDIA_CPU 89 -#define CLKID_AO_AHB_SRAM 90 -#define CLKID_AO_AHB_BUS 91 -#define CLKID_AO_IFACE 92 -/* CLKID_AO_I2C */ -/* CLKID_SD_EMMC_A */ -/* CLKID_SD_EMMC_B */ -/* CLKID_SD_EMMC_C */ -/* CLKID_SAR_ADC_CLK */ -/* CLKID_SAR_ADC_SEL */ #define CLKID_SAR_ADC_DIV 99 -/* CLKID_MALI_0_SEL */ -#define CLKID_MALI_0_DIV 101 -/* CLKID_MALI_0 */ -/* CLKID_MALI_1_SEL */ -#define CLKID_MALI_1_DIV 104 -/* CLKID_MALI_1 */ -/* CLKID_MALI */ -/* CLKID_CTS_AMCLK */ +#define CLKID_MALI_0_DIV 101 +#define CLKID_MALI_1_DIV 104 #define CLKID_CTS_AMCLK_SEL 108 #define CLKID_CTS_AMCLK_DIV 109 -/* CLKID_CTS_MCLK_I958 */ #define CLKID_CTS_MCLK_I958_SEL 111 #define CLKID_CTS_MCLK_I958_DIV 112 -/* CLKID_CTS_I958 */ -#define CLKID_32K_CLK 114 #define CLKID_32K_CLK_SEL 115 #define CLKID_32K_CLK_DIV 116 +#define CLKID_SD_EMMC_A_CLK0_SEL 117 +#define CLKID_SD_EMMC_A_CLK0_DIV 118 +#define CLKID_SD_EMMC_B_CLK0_SEL 120 +#define CLKID_SD_EMMC_B_CLK0_DIV 121 +#define CLKID_SD_EMMC_C_CLK0_SEL 123 +#define CLKID_SD_EMMC_C_CLK0_DIV 124 -#define NR_CLKS 117 +#define NR_CLKS 126 -/* include the CLKIDs that have been made part of the stable DT binding */ +/* include the CLKIDs that have been made part of the DT binding */ #include <dt-bindings/clock/gxbb-clkc.h> #endif /* __GXBB_H */ diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index 6ec512ad2598..20ab7190d328 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -25,6 +25,8 @@ #include <linux/clk-provider.h> #include <linux/of_address.h> #include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> #include <linux/init.h> #include "clkc.h" @@ -32,6 +34,13 @@ static DEFINE_SPINLOCK(clk_lock); +static void __iomem *clk_base; + +struct meson8b_clk_reset { + struct reset_controller_dev reset; + void __iomem *base; +}; + static const struct pll_rate_table sys_pll_rate_table[] = { PLL_RATE(312000000, 52, 1, 2), PLL_RATE(336000000, 56, 1, 2), @@ -590,6 +599,7 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = { [CLKID_MPLL0] = &meson8b_mpll0.hw, [CLKID_MPLL1] = &meson8b_mpll1.hw, [CLKID_MPLL2] = &meson8b_mpll2.hw, + [CLK_NR_CLKS] = NULL, }, .num = CLK_NR_CLKS, }; @@ -695,20 +705,114 @@ static struct clk_divider *const meson8b_clk_dividers[] = { &meson8b_mpeg_clk_div, }; +static const struct meson8b_clk_reset_line { + u32 reg; + u8 bit_idx; +} meson8b_clk_reset_bits[] = { + [CLKC_RESET_L2_CACHE_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 30 + }, + [CLKC_RESET_AXI_64_TO_128_BRIDGE_A5_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 29 + }, + [CLKC_RESET_SCU_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 28 + }, + [CLKC_RESET_CPU3_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 27 + }, + [CLKC_RESET_CPU2_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 26 + }, + [CLKC_RESET_CPU1_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 25 + }, + [CLKC_RESET_CPU0_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 24 + }, + [CLKC_RESET_A5_GLOBAL_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 18 + }, + [CLKC_RESET_A5_AXI_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 17 + }, + [CLKC_RESET_A5_ABP_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 16 + }, + [CLKC_RESET_AXI_64_TO_128_BRIDGE_MMC_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL1, .bit_idx = 30 + }, + [CLKC_RESET_VID_CLK_CNTL_SOFT_RESET] = { + .reg = HHI_VID_CLK_CNTL, .bit_idx = 15 + }, + [CLKC_RESET_VID_DIVIDER_CNTL_SOFT_RESET_POST] = { + .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 7 + }, + [CLKC_RESET_VID_DIVIDER_CNTL_SOFT_RESET_PRE] = { + .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 3 + }, + [CLKC_RESET_VID_DIVIDER_CNTL_RESET_N_POST] = { + .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 1 + }, + [CLKC_RESET_VID_DIVIDER_CNTL_RESET_N_PRE] = { + .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 0 + }, +}; + +static int meson8b_clk_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct meson8b_clk_reset *meson8b_clk_reset = + container_of(rcdev, struct meson8b_clk_reset, reset); + unsigned long flags; + const struct meson8b_clk_reset_line *reset; + u32 val; + + if (id >= ARRAY_SIZE(meson8b_clk_reset_bits)) + return -EINVAL; + + reset = &meson8b_clk_reset_bits[id]; + + spin_lock_irqsave(&clk_lock, flags); + + val = readl(meson8b_clk_reset->base + reset->reg); + if (assert) + val |= BIT(reset->bit_idx); + else + val &= ~BIT(reset->bit_idx); + writel(val, meson8b_clk_reset->base + reset->reg); + + spin_unlock_irqrestore(&clk_lock, flags); + + return 0; +} + +static int meson8b_clk_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return meson8b_clk_reset_update(rcdev, id, true); +} + +static int meson8b_clk_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return meson8b_clk_reset_update(rcdev, id, false); +} + +static const struct reset_control_ops meson8b_clk_reset_ops = { + .assert = meson8b_clk_reset_assert, + .deassert = meson8b_clk_reset_deassert, +}; + static int meson8b_clkc_probe(struct platform_device *pdev) { - void __iomem *clk_base; int ret, clkid, i; struct clk_hw *parent_hw; struct clk *parent_clk; struct device *dev = &pdev->dev; - /* Generic clocks and PLLs */ - clk_base = of_iomap(dev->of_node, 1); - if (!clk_base) { - pr_err("%s: Unable to map clk base\n", __func__); + if (!clk_base) return -ENXIO; - } /* Populate base address for PLLs */ for (i = 0; i < ARRAY_SIZE(meson8b_clk_plls); i++) @@ -748,7 +852,7 @@ static int meson8b_clkc_probe(struct platform_device *pdev) /* FIXME convert to devm_clk_register */ ret = devm_clk_hw_register(dev, meson8b_hw_onecell_data.hws[clkid]); if (ret) - goto iounmap; + return ret; } /* @@ -771,15 +875,11 @@ static int meson8b_clkc_probe(struct platform_device *pdev) if (ret) { pr_err("%s: failed to register clock notifier for cpu_clk\n", __func__); - goto iounmap; + return ret; } return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, &meson8b_hw_onecell_data); - -iounmap: - iounmap(clk_base); - return ret; } static const struct of_device_id meson8b_clkc_match_table[] = { @@ -798,3 +898,39 @@ static struct platform_driver meson8b_driver = { }; builtin_platform_driver(meson8b_driver); + +static void __init meson8b_clkc_reset_init(struct device_node *np) +{ + struct meson8b_clk_reset *rstc; + int ret; + + /* Generic clocks, PLLs and some of the reset-bits */ + clk_base = of_iomap(np, 1); + if (!clk_base) { + pr_err("%s: Unable to map clk base\n", __func__); + return; + } + + rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); + if (!rstc) + return; + + /* Reset Controller */ + rstc->base = clk_base; + rstc->reset.ops = &meson8b_clk_reset_ops; + rstc->reset.nr_resets = ARRAY_SIZE(meson8b_clk_reset_bits); + rstc->reset.of_node = np; + ret = reset_controller_register(&rstc->reset); + if (ret) { + pr_err("%s: Failed to register clkc reset controller: %d\n", + __func__, ret); + return; + } +} + +CLK_OF_DECLARE_DRIVER(meson8_clkc, "amlogic,meson8-clkc", + meson8b_clkc_reset_init); +CLK_OF_DECLARE_DRIVER(meson8b_clkc, "amlogic,meson8b-clkc", + meson8b_clkc_reset_init); +CLK_OF_DECLARE_DRIVER(meson8m2_clkc, "amlogic,meson8m2-clkc", + meson8b_clkc_reset_init); diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h index a687e02547dc..2eaf8a52e7dd 100644 --- a/drivers/clk/meson/meson8b.h +++ b/drivers/clk/meson/meson8b.h @@ -37,6 +37,9 @@ #define HHI_GCLK_AO 0x154 /* 0x55 offset in data sheet */ #define HHI_SYS_CPU_CLK_CNTL1 0x15c /* 0x57 offset in data sheet */ #define HHI_MPEG_CLK_CNTL 0x174 /* 0x5d offset in data sheet */ +#define HHI_VID_CLK_CNTL 0x17c /* 0x5f offset in data sheet */ +#define HHI_VID_DIVIDER_CNTL 0x198 /* 0x66 offset in data sheet */ +#define HHI_SYS_CPU_CLK_CNTL0 0x19c /* 0x67 offset in data sheet */ #define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */ #define HHI_SYS_PLL_CNTL 0x300 /* 0xc0 offset in data sheet */ #define HHI_VID_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */ @@ -60,110 +63,19 @@ * CLKID index values * * These indices are entirely contrived and do not map onto the hardware. - * Migrate them out of this header and into the DT header file when they need - * to be exposed to client nodes in DT: include/dt-bindings/clock/meson8b-clkc.h + * It has now been decided to expose everything by default in the DT header: + * include/dt-bindings/clock/gxbb-clkc.h. Only the clocks ids we don't want + * to expose, such as the internal muxes and dividers of composite clocks, + * will remain defined here. */ -/* CLKID_UNUSED */ -/* CLKID_XTAL */ -/* CLKID_PLL_FIXED */ -/* CLKID_PLL_VID */ -/* CLKID_PLL_SYS */ -/* CLKID_FCLK_DIV2 */ -/* CLKID_FCLK_DIV3 */ -/* CLKID_FCLK_DIV4 */ -/* CLKID_FCLK_DIV5 */ -/* CLKID_FCLK_DIV7 */ -/* CLKID_CLK81 */ -/* CLKID_MALI */ -/* CLKID_CPUCLK */ -/* CLKID_ZERO */ -/* CLKID_MPEG_SEL */ -/* CLKID_MPEG_DIV */ -#define CLKID_DDR 16 -#define CLKID_DOS 17 -#define CLKID_ISA 18 -#define CLKID_PL301 19 -#define CLKID_PERIPHS 20 -#define CLKID_SPICC 21 -#define CLKID_I2C 22 -/* #define CLKID_SAR_ADC */ -#define CLKID_SMART_CARD 24 -/* #define CLKID_RNG0 */ -#define CLKID_UART0 26 -#define CLKID_SDHC 27 -#define CLKID_STREAM 28 -#define CLKID_ASYNC_FIFO 29 -/* #define CLKID_SDIO */ -#define CLKID_ABUF 31 -#define CLKID_HIU_IFACE 32 -#define CLKID_ASSIST_MISC 33 -#define CLKID_SPI 34 -#define CLKID_I2S_SPDIF 35 -/* #define CLKID_ETH */ -#define CLKID_DEMUX 37 -#define CLKID_AIU_GLUE 38 -#define CLKID_IEC958 39 -#define CLKID_I2S_OUT 40 -#define CLKID_AMCLK 41 -#define CLKID_AIFIFO2 42 -#define CLKID_MIXER 43 -#define CLKID_MIXER_IFACE 44 -#define CLKID_ADC 45 -#define CLKID_BLKMV 46 -#define CLKID_AIU 47 -#define CLKID_UART1 48 -#define CLKID_G2D 49 -/* #define CLKID_USB0 */ -/* #define CLKID_USB1 */ -#define CLKID_RESET 52 -#define CLKID_NAND 53 -#define CLKID_DOS_PARSER 54 -/* #define CLKID_USB */ -#define CLKID_VDIN1 56 -#define CLKID_AHB_ARB0 57 -#define CLKID_EFUSE 58 -#define CLKID_BOOT_ROM 59 -#define CLKID_AHB_DATA_BUS 60 -#define CLKID_AHB_CTRL_BUS 61 -#define CLKID_HDMI_INTR_SYNC 62 -#define CLKID_HDMI_PCLK 63 -/* CLKID_USB1_DDR_BRIDGE */ -/* CLKID_USB0_DDR_BRIDGE */ -#define CLKID_MMC_PCLK 66 -#define CLKID_DVIN 67 -#define CLKID_UART2 68 -/* #define CLKID_SANA */ -#define CLKID_VPU_INTR 70 -#define CLKID_SEC_AHB_AHB3_BRIDGE 71 -#define CLKID_CLK81_A9 72 -#define CLKID_VCLK2_VENCI0 73 -#define CLKID_VCLK2_VENCI1 74 -#define CLKID_VCLK2_VENCP0 75 -#define CLKID_VCLK2_VENCP1 76 -#define CLKID_GCLK_VENCI_INT 77 -#define CLKID_GCLK_VENCP_INT 78 -#define CLKID_DAC_CLK 79 -#define CLKID_AOCLK_GATE 80 -#define CLKID_IEC958_GATE 81 -#define CLKID_ENC480P 82 -#define CLKID_RNG1 83 -#define CLKID_GCLK_VENCL_INT 84 -#define CLKID_VCLK2_VENCLMCC 85 -#define CLKID_VCLK2_VENCL 86 -#define CLKID_VCLK2_OTHER 87 -#define CLKID_EDP 88 -#define CLKID_AO_MEDIA_CPU 89 -#define CLKID_AO_AHB_SRAM 90 -#define CLKID_AO_AHB_BUS 91 -#define CLKID_AO_IFACE 92 -#define CLKID_MPLL0 93 -#define CLKID_MPLL1 94 -#define CLKID_MPLL2 95 - #define CLK_NR_CLKS 96 -/* include the CLKIDs that have been made part of the stable DT binding */ +/* + * include the CLKID and RESETID that have + * been made part of the stable DT binding + */ #include <dt-bindings/clock/meson8b-clkc.h> +#include <dt-bindings/reset/amlogic,meson8b-clkc-reset.h> #endif /* __MESON8B_H */ diff --git a/drivers/clk/mmp/clk.c b/drivers/clk/mmp/clk.c index 61893fe73251..089927e4cda2 100644 --- a/drivers/clk/mmp/clk.c +++ b/drivers/clk/mmp/clk.c @@ -9,7 +9,7 @@ void mmp_clk_init(struct device_node *np, struct mmp_clk_unit *unit, int nr_clks) { - static struct clk **clk_table; + struct clk **clk_table; clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); if (!clk_table) diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c index 5b98ff9076f3..7b359afd620e 100644 --- a/drivers/clk/nxp/clk-lpc32xx.c +++ b/drivers/clk/nxp/clk-lpc32xx.c @@ -885,7 +885,7 @@ static const struct clk_ops clk_usb_i2c_ops = { .recalc_rate = clk_usb_i2c_recalc_rate, }; -static int clk_gate_enable(struct clk_hw *hw) +static int lpc32xx_clk_gate_enable(struct clk_hw *hw) { struct lpc32xx_clk_gate *clk = to_lpc32xx_gate(hw); u32 mask = BIT(clk->bit_idx); @@ -894,7 +894,7 @@ static int clk_gate_enable(struct clk_hw *hw) return regmap_update_bits(clk_regmap, clk->reg, mask, val); } -static void clk_gate_disable(struct clk_hw *hw) +static void lpc32xx_clk_gate_disable(struct clk_hw *hw) { struct lpc32xx_clk_gate *clk = to_lpc32xx_gate(hw); u32 mask = BIT(clk->bit_idx); @@ -903,7 +903,7 @@ static void clk_gate_disable(struct clk_hw *hw) regmap_update_bits(clk_regmap, clk->reg, mask, val); } -static int clk_gate_is_enabled(struct clk_hw *hw) +static int lpc32xx_clk_gate_is_enabled(struct clk_hw *hw) { struct lpc32xx_clk_gate *clk = to_lpc32xx_gate(hw); u32 val; @@ -916,9 +916,9 @@ static int clk_gate_is_enabled(struct clk_hw *hw) } static const struct clk_ops lpc32xx_clk_gate_ops = { - .enable = clk_gate_enable, - .disable = clk_gate_disable, - .is_enabled = clk_gate_is_enabled, + .enable = lpc32xx_clk_gate_enable, + .disable = lpc32xx_clk_gate_disable, + .is_enabled = lpc32xx_clk_gate_is_enabled, }; #define div_mask(width) ((1 << (width)) - 1) diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index d990fe44aef3..cc03d5508627 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -412,8 +412,6 @@ static const struct clk_ops clk_smd_rpm_ops = { static const struct clk_ops clk_smd_rpm_branch_ops = { .prepare = clk_smd_rpm_prepare, .unprepare = clk_smd_rpm_unprepare, - .round_rate = clk_smd_rpm_round_rate, - .recalc_rate = clk_smd_rpm_recalc_rate, }; /* msm8916 */ diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index 2cfe7000fc60..3410ee68d4bc 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -1176,7 +1176,7 @@ static struct clk_rcg2 bimc_gpu_clk_src = { .parent_names = gcc_xo_gpll0_bimc, .num_parents = 3, .flags = CLK_GET_RATE_NOCACHE, - .ops = &clk_rcg2_shared_ops, + .ops = &clk_rcg2_ops, }, }; diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index 8abc200d4fd3..7ddec886fcd3 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -2730,6 +2730,32 @@ static struct clk_fixed_factor ufs_rx_cfg_clk_src = { }, }; +static struct clk_branch gcc_hlos1_vote_lpass_core_smmu_clk = { + .halt_reg = 0x7d010, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "hlos1_vote_lpass_core_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_hlos1_vote_lpass_adsp_smmu_clk = { + .halt_reg = 0x7d014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "hlos1_vote_lpass_adsp_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_ufs_rx_cfg_clk = { .halt_reg = 0x75014, .clkr = { @@ -3307,6 +3333,8 @@ static struct clk_regmap *gcc_msm8996_clocks[] = { [GCC_UFS_AHB_CLK] = &gcc_ufs_ahb_clk.clkr, [GCC_UFS_TX_CFG_CLK] = &gcc_ufs_tx_cfg_clk.clkr, [GCC_UFS_RX_CFG_CLK] = &gcc_ufs_rx_cfg_clk.clkr, + [GCC_HLOS1_VOTE_LPASS_CORE_SMMU_CLK] = &gcc_hlos1_vote_lpass_core_smmu_clk.clkr, + [GCC_HLOS1_VOTE_LPASS_ADSP_SMMU_CLK] = &gcc_hlos1_vote_lpass_adsp_smmu_clk.clkr, [GCC_UFS_TX_SYMBOL_0_CLK] = &gcc_ufs_tx_symbol_0_clk.clkr, [GCC_UFS_RX_SYMBOL_0_CLK] = &gcc_ufs_rx_symbol_0_clk.clkr, [GCC_UFS_RX_SYMBOL_1_CLK] = &gcc_ufs_rx_symbol_1_clk.clkr, diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 78d1df9112ba..acbb38151ba1 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -15,6 +15,7 @@ config CLK_RENESAS select CLK_R8A7794 if ARCH_R8A7794 select CLK_R8A7795 if ARCH_R8A7795 select CLK_R8A7796 if ARCH_R8A7796 + select CLK_R8A77995 if ARCH_R8A77995 select CLK_SH73A0 if ARCH_SH73A0 if CLK_RENESAS @@ -34,94 +35,103 @@ config CLK_EMEV2 bool "Emma Mobile EV2 clock support" if COMPILE_TEST config CLK_RZA1 - bool + bool "RZ/A1H clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP config CLK_R8A73A4 - bool + bool "R-Mobile APE6 clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP select CLK_RENESAS_DIV6 config CLK_R8A7740 - bool + bool "R-Mobile A1 clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP select CLK_RENESAS_DIV6 config CLK_R8A7743 - bool + bool "RZ/G1M clock support" if COMPILE_TEST select CLK_RCAR_GEN2_CPG config CLK_R8A7745 - bool + bool "RZ/G1E clock support" if COMPILE_TEST select CLK_RCAR_GEN2_CPG config CLK_R8A7778 - bool + bool "R-Car M1A clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP config CLK_R8A7779 - bool + bool "R-Car H1 clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP config CLK_R8A7790 - bool + bool "R-Car H2 clock support" if COMPILE_TEST select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG select CLK_RENESAS_DIV6 config CLK_R8A7791 - bool + bool "R-Car M2-W/N clock support" if COMPILE_TEST select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG select CLK_RENESAS_DIV6 config CLK_R8A7792 - bool + bool "R-Car V2H clock support" if COMPILE_TEST select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG config CLK_R8A7794 - bool + bool "R-Car E2 clock support" if COMPILE_TEST select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG select CLK_RENESAS_DIV6 config CLK_R8A7795 - bool + bool "R-Car H3 clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG config CLK_R8A7796 - bool + bool "R-Car M3-W clock support" if COMPILE_TEST + select CLK_RCAR_GEN3_CPG + +config CLK_R8A77995 + bool "R-Car D3 clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG config CLK_SH73A0 - bool + bool "SH-Mobile AG5 clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP select CLK_RENESAS_DIV6 # Family config CLK_RCAR_GEN2 - bool + bool "R-Car Gen2 legacy clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP select CLK_RENESAS_DIV6 config CLK_RCAR_GEN2_CPG - bool + bool "R-Car Gen2 CPG clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSSR config CLK_RCAR_GEN3_CPG - bool + bool "R-Car Gen3 CPG clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSSR +config CLK_RCAR_USB2_CLOCK_SEL + bool "Renesas R-Car USB2 clock selector support" + depends on ARCH_RENESAS || COMPILE_TEST + help + This is a driver for R-Car USB2 clock selector # Generic config CLK_RENESAS_CPG_MSSR - bool + bool "CPG/MSSR clock support" if COMPILE_TEST select CLK_RENESAS_DIV6 config CLK_RENESAS_CPG_MSTP - bool + bool "MSTP clock support" if COMPILE_TEST config CLK_RENESAS_DIV6 bool "DIV6 clock support" if COMPILE_TEST diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index 02d04124371f..9bda3ec5b199 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -13,12 +13,14 @@ obj-$(CONFIG_CLK_R8A7792) += r8a7792-cpg-mssr.o obj-$(CONFIG_CLK_R8A7794) += r8a7794-cpg-mssr.o obj-$(CONFIG_CLK_R8A7795) += r8a7795-cpg-mssr.o obj-$(CONFIG_CLK_R8A7796) += r8a7796-cpg-mssr.o +obj-$(CONFIG_CLK_R8A77995) += r8a77995-cpg-mssr.o obj-$(CONFIG_CLK_SH73A0) += clk-sh73a0.o # Family obj-$(CONFIG_CLK_RCAR_GEN2) += clk-rcar-gen2.o obj-$(CONFIG_CLK_RCAR_GEN2_CPG) += rcar-gen2-cpg.o obj-$(CONFIG_CLK_RCAR_GEN3_CPG) += rcar-gen3-cpg.o +obj-$(CONFIG_CLK_RCAR_USB2_CLOCK_SEL) += rcar-usb2-clock-sel.o # Generic obj-$(CONFIG_CLK_RENESAS_CPG_MSSR) += renesas-cpg-mssr.o diff --git a/drivers/clk/renesas/clk-div6.c b/drivers/clk/renesas/clk-div6.c index 0627860233cb..3e0040c0ac87 100644 --- a/drivers/clk/renesas/clk-div6.c +++ b/drivers/clk/renesas/clk-div6.c @@ -29,6 +29,9 @@ * @hw: handle between common and hardware-specific interfaces * @reg: IO-remapped register * @div: divisor value (1-64) + * @src_shift: Shift to access the register bits to select the parent clock + * @src_width: Number of register bits to select the parent clock (may be 0) + * @parents: Array to map from valid parent clocks indices to hardware indices */ struct div6_clock { struct clk_hw hw; diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c index f1617dd044cb..500a9e4e03c4 100644 --- a/drivers/clk/renesas/clk-mstp.c +++ b/drivers/clk/renesas/clk-mstp.c @@ -335,7 +335,7 @@ void __init cpg_mstp_add_clk_domain(struct device_node *np) u32 ncells; if (of_property_read_u32(np, "#power-domain-cells", &ncells)) { - pr_warn("%s lacks #power-domain-cells\n", np->full_name); + pr_warn("%pOF lacks #power-domain-cells\n", np); return; } diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c index 51a2479ed5d7..0b2e56d0d94b 100644 --- a/drivers/clk/renesas/clk-rcar-gen2.c +++ b/drivers/clk/renesas/clk-rcar-gen2.c @@ -407,8 +407,7 @@ static void __init rcar_gen2_cpg_clocks_init(struct device_node *np) if (rcar_rst_read_mode_pins(&cpg_mode)) { /* Backward-compatibility with old DT */ - pr_warn("%s: failed to obtain mode pins from RST\n", - np->full_name); + pr_warn("%pOF: failed to obtain mode pins from RST\n", np); cpg_mode = rcar_gen2_read_mode_pins(); } diff --git a/drivers/clk/renesas/r8a7792-cpg-mssr.c b/drivers/clk/renesas/r8a7792-cpg-mssr.c index a832b9b6f7b0..7f85bbf20bf7 100644 --- a/drivers/clk/renesas/r8a7792-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7792-cpg-mssr.c @@ -118,6 +118,13 @@ static const struct mssr_mod_clk r8a7792_mod_clks[] __initconst = { DEF_MOD("vin1", 810, R8A7792_CLK_ZG), DEF_MOD("vin0", 811, R8A7792_CLK_ZG), DEF_MOD("etheravb", 812, R8A7792_CLK_HP), + DEF_MOD("imr-lx3", 821, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-1", 822, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-0", 823, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-5", 825, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-4", 826, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-3", 827, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-2", 828, R8A7792_CLK_ZG), DEF_MOD("gyro-adc", 901, R8A7792_CLK_P), DEF_MOD("gpio7", 904, R8A7792_CLK_CP), DEF_MOD("gpio6", 905, R8A7792_CLK_CP), diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index c091a8e024b8..762b2f8824f1 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -305,23 +305,23 @@ static const unsigned int r8a7795_crit_mod_clks[] __initconst = { (((md) & BIT(17)) >> 17)) static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = { - /* EXTAL div PLL1 mult PLL3 mult */ - { 1, 192, 192, }, - { 1, 192, 128, }, - { 0, /* Prohibited setting */ }, - { 1, 192, 192, }, - { 1, 160, 160, }, - { 1, 160, 106, }, - { 0, /* Prohibited setting */ }, - { 1, 160, 160, }, - { 1, 128, 128, }, - { 1, 128, 84, }, - { 0, /* Prohibited setting */ }, - { 1, 128, 128, }, - { 2, 192, 192, }, - { 2, 192, 128, }, - { 0, /* Prohibited setting */ }, - { 2, 192, 192, }, + /* EXTAL div PLL1 mult/div PLL3 mult/div */ + { 1, 192, 1, 192, 1, }, + { 1, 192, 1, 128, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 192, 1, 192, 1, }, + { 1, 160, 1, 160, 1, }, + { 1, 160, 1, 106, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 160, 1, 160, 1, }, + { 1, 128, 1, 128, 1, }, + { 1, 128, 1, 84, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 128, 1, 128, 1, }, + { 2, 192, 1, 192, 1, }, + { 2, 192, 1, 128, 1, }, + { 0, /* Prohibited setting */ }, + { 2, 192, 1, 192, 1, }, }; static const struct soc_device_attribute r8a7795es1[] __initconst = { diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c index acc6d0f153e1..e5e7fb212288 100644 --- a/drivers/clk/renesas/r8a7796-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c @@ -138,6 +138,7 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { DEF_MOD("sdif0", 314, R8A7796_CLK_SD0), DEF_MOD("pcie1", 318, R8A7796_CLK_S3D1), DEF_MOD("pcie0", 319, R8A7796_CLK_S3D1), + DEF_MOD("usb3-if0", 328, R8A7796_CLK_S3D1), DEF_MOD("usb-dmac0", 330, R8A7796_CLK_S3D1), DEF_MOD("usb-dmac1", 331, R8A7796_CLK_S3D1), DEF_MOD("rwdt", 402, R8A7796_CLK_R), @@ -277,23 +278,23 @@ static const unsigned int r8a7796_crit_mod_clks[] __initconst = { (((md) & BIT(17)) >> 17)) static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = { - /* EXTAL div PLL1 mult PLL3 mult */ - { 1, 192, 192, }, - { 1, 192, 128, }, - { 0, /* Prohibited setting */ }, - { 1, 192, 192, }, - { 1, 160, 160, }, - { 1, 160, 106, }, - { 0, /* Prohibited setting */ }, - { 1, 160, 160, }, - { 1, 128, 128, }, - { 1, 128, 84, }, - { 0, /* Prohibited setting */ }, - { 1, 128, 128, }, - { 2, 192, 192, }, - { 2, 192, 128, }, - { 0, /* Prohibited setting */ }, - { 2, 192, 192, }, + /* EXTAL div PLL1 mult/div PLL3 mult/div */ + { 1, 192, 1, 192, 1, }, + { 1, 192, 1, 128, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 192, 1, 192, 1, }, + { 1, 160, 1, 160, 1, }, + { 1, 160, 1, 106, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 160, 1, 160, 1, }, + { 1, 128, 1, 128, 1, }, + { 1, 128, 1, 84, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 128, 1, 128, 1, }, + { 2, 192, 1, 192, 1, }, + { 2, 192, 1, 128, 1, }, + { 0, /* Prohibited setting */ }, + { 2, 192, 1, 192, 1, }, }; static int __init r8a7796_cpg_mssr_init(struct device *dev) diff --git a/drivers/clk/renesas/r8a77995-cpg-mssr.c b/drivers/clk/renesas/r8a77995-cpg-mssr.c new file mode 100644 index 000000000000..e594cf8ee63b --- /dev/null +++ b/drivers/clk/renesas/r8a77995-cpg-mssr.c @@ -0,0 +1,236 @@ +/* + * r8a77995 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2017 Glider bvba + * + * Based on r8a7795-cpg-mssr.c + * + * Copyright (C) 2015 Glider bvba + * Copyright (C) 2015 Renesas Electronics Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/soc/renesas/rcar-rst.h> + +#include <dt-bindings/clock/r8a77995-cpg-mssr.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen3-cpg.h" + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R8A77995_CLK_CP, + + /* External Input Clocks */ + CLK_EXTAL, + + /* Internal Core Clocks */ + CLK_MAIN, + CLK_PLL0, + CLK_PLL1, + CLK_PLL3, + CLK_PLL0D2, + CLK_PLL0D3, + CLK_PLL0D5, + CLK_PLL1D2, + CLK_PE, + CLK_S0, + CLK_S1, + CLK_S2, + CLK_S3, + CLK_SDSRC, + CLK_SSPSRC, + + /* Module Clocks */ + MOD_CLK_BASE +}; + +static const struct cpg_core_clk r8a77995_core_clks[] __initconst = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + + /* Internal Core Clocks */ + DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN3_MAIN, CLK_EXTAL), + DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN3_PLL1, CLK_MAIN), + DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN3_PLL3, CLK_MAIN), + + DEF_FIXED(".pll0", CLK_PLL0, CLK_MAIN, 4, 250), + DEF_FIXED(".pll0d2", CLK_PLL0D2, CLK_PLL0, 2, 1), + DEF_FIXED(".pll0d3", CLK_PLL0D3, CLK_PLL0, 3, 1), + DEF_FIXED(".pll0d5", CLK_PLL0D5, CLK_PLL0, 5, 1), + DEF_FIXED(".pll1d2", CLK_PLL1D2, CLK_PLL1, 2, 1), + DEF_FIXED(".pe", CLK_PE, CLK_PLL0D3, 4, 1), + DEF_FIXED(".s0", CLK_S0, CLK_PLL1, 2, 1), + DEF_FIXED(".s1", CLK_S1, CLK_PLL1, 3, 1), + DEF_FIXED(".s2", CLK_S2, CLK_PLL1, 4, 1), + DEF_FIXED(".s3", CLK_S3, CLK_PLL1, 6, 1), + DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1, 2, 1), + + /* Core Clock Outputs */ + DEF_FIXED("z2", R8A77995_CLK_Z2, CLK_PLL0D3, 1, 1), + DEF_FIXED("ztr", R8A77995_CLK_ZTR, CLK_PLL1, 6, 1), + DEF_FIXED("zt", R8A77995_CLK_ZT, CLK_PLL1, 4, 1), + DEF_FIXED("zx", R8A77995_CLK_ZX, CLK_PLL1, 3, 1), + DEF_FIXED("s0d1", R8A77995_CLK_S0D1, CLK_S0, 1, 1), + DEF_FIXED("s1d1", R8A77995_CLK_S1D1, CLK_S1, 1, 1), + DEF_FIXED("s1d2", R8A77995_CLK_S1D2, CLK_S1, 2, 1), + DEF_FIXED("s1d4", R8A77995_CLK_S1D4, CLK_S1, 4, 1), + DEF_FIXED("s2d1", R8A77995_CLK_S2D1, CLK_S2, 1, 1), + DEF_FIXED("s2d2", R8A77995_CLK_S2D2, CLK_S2, 2, 1), + DEF_FIXED("s2d4", R8A77995_CLK_S2D4, CLK_S2, 4, 1), + DEF_FIXED("s3d1", R8A77995_CLK_S3D1, CLK_S3, 1, 1), + DEF_FIXED("s3d2", R8A77995_CLK_S3D2, CLK_S3, 2, 1), + DEF_FIXED("s3d4", R8A77995_CLK_S3D4, CLK_S3, 4, 1), + + DEF_FIXED("cl", R8A77995_CLK_CL, CLK_PLL1, 48, 1), + DEF_FIXED("cp", R8A77995_CLK_CP, CLK_EXTAL, 2, 1), + DEF_FIXED("osc", R8A77995_CLK_OSC, CLK_EXTAL, 384, 1), + DEF_FIXED("r", R8A77995_CLK_R, CLK_EXTAL, 1536, 1), + + DEF_GEN3_PE("s1d4c", R8A77995_CLK_S1D4C, CLK_S1, 4, CLK_PE, 2), + DEF_GEN3_PE("s3d1c", R8A77995_CLK_S3D1C, CLK_S3, 1, CLK_PE, 1), + DEF_GEN3_PE("s3d2c", R8A77995_CLK_S3D2C, CLK_S3, 2, CLK_PE, 2), + DEF_GEN3_PE("s3d4c", R8A77995_CLK_S3D4C, CLK_S3, 4, CLK_PE, 4), + + DEF_GEN3_SD("sd0", R8A77995_CLK_SD0, CLK_SDSRC, 0x268), + + DEF_DIV6P1("canfd", R8A77995_CLK_CANFD, CLK_PLL0D3, 0x244), + DEF_DIV6P1("mso", R8A77995_CLK_MSO, CLK_PLL1D2, 0x014), +}; + +static const struct mssr_mod_clk r8a77995_mod_clks[] __initconst = { + DEF_MOD("scif5", 202, R8A77995_CLK_S3D4C), + DEF_MOD("scif4", 203, R8A77995_CLK_S3D4C), + DEF_MOD("scif3", 204, R8A77995_CLK_S3D4C), + DEF_MOD("scif1", 206, R8A77995_CLK_S3D4C), + DEF_MOD("scif0", 207, R8A77995_CLK_S3D4C), + DEF_MOD("msiof3", 208, R8A77995_CLK_MSO), + DEF_MOD("msiof2", 209, R8A77995_CLK_MSO), + DEF_MOD("msiof1", 210, R8A77995_CLK_MSO), + DEF_MOD("msiof0", 211, R8A77995_CLK_MSO), + DEF_MOD("sys-dmac2", 217, R8A77995_CLK_S3D1), + DEF_MOD("sys-dmac1", 218, R8A77995_CLK_S3D1), + DEF_MOD("sys-dmac0", 219, R8A77995_CLK_S3D1), + DEF_MOD("cmt3", 300, R8A77995_CLK_R), + DEF_MOD("cmt2", 301, R8A77995_CLK_R), + DEF_MOD("cmt1", 302, R8A77995_CLK_R), + DEF_MOD("cmt0", 303, R8A77995_CLK_R), + DEF_MOD("scif2", 310, R8A77995_CLK_S3D4C), + DEF_MOD("emmc0", 312, R8A77995_CLK_SD0), + DEF_MOD("usb-dmac0", 330, R8A77995_CLK_S3D1), + DEF_MOD("usb-dmac1", 331, R8A77995_CLK_S3D1), + DEF_MOD("rwdt", 402, R8A77995_CLK_R), + DEF_MOD("intc-ex", 407, R8A77995_CLK_CP), + DEF_MOD("intc-ap", 408, R8A77995_CLK_S3D1), + DEF_MOD("audmac0", 502, R8A77995_CLK_S3D1), + DEF_MOD("hscif3", 517, R8A77995_CLK_S3D1C), + DEF_MOD("hscif0", 520, R8A77995_CLK_S3D1C), + DEF_MOD("thermal", 522, R8A77995_CLK_CP), + DEF_MOD("pwm", 523, R8A77995_CLK_S3D4C), + DEF_MOD("fcpvd1", 602, R8A77995_CLK_S1D2), + DEF_MOD("fcpvd0", 603, R8A77995_CLK_S1D2), + DEF_MOD("fcpvbs", 607, R8A77995_CLK_S0D1), + DEF_MOD("vspd1", 622, R8A77995_CLK_S1D2), + DEF_MOD("vspd0", 623, R8A77995_CLK_S1D2), + DEF_MOD("vspbs", 627, R8A77995_CLK_S0D1), + DEF_MOD("ehci0", 703, R8A77995_CLK_S3D2), + DEF_MOD("hsusb", 704, R8A77995_CLK_S3D2), + DEF_MOD("du1", 723, R8A77995_CLK_S2D1), + DEF_MOD("du0", 724, R8A77995_CLK_S2D1), + DEF_MOD("lvds", 727, R8A77995_CLK_S2D1), + DEF_MOD("vin7", 804, R8A77995_CLK_S1D2), + DEF_MOD("vin6", 805, R8A77995_CLK_S1D2), + DEF_MOD("vin5", 806, R8A77995_CLK_S1D2), + DEF_MOD("vin4", 807, R8A77995_CLK_S1D2), + DEF_MOD("etheravb", 812, R8A77995_CLK_S3D2), + DEF_MOD("imr0", 823, R8A77995_CLK_S1D2), + DEF_MOD("gpio6", 906, R8A77995_CLK_S3D4), + DEF_MOD("gpio5", 907, R8A77995_CLK_S3D4), + DEF_MOD("gpio4", 908, R8A77995_CLK_S3D4), + DEF_MOD("gpio3", 909, R8A77995_CLK_S3D4), + DEF_MOD("gpio2", 910, R8A77995_CLK_S3D4), + DEF_MOD("gpio1", 911, R8A77995_CLK_S3D4), + DEF_MOD("gpio0", 912, R8A77995_CLK_S3D4), + DEF_MOD("can-fd", 914, R8A77995_CLK_S3D2), + DEF_MOD("can-if1", 915, R8A77995_CLK_S3D4), + DEF_MOD("can-if0", 916, R8A77995_CLK_S3D4), + DEF_MOD("i2c3", 928, R8A77995_CLK_S3D2), + DEF_MOD("i2c2", 929, R8A77995_CLK_S3D2), + DEF_MOD("i2c1", 930, R8A77995_CLK_S3D2), + DEF_MOD("i2c0", 931, R8A77995_CLK_S3D2), + DEF_MOD("ssi-all", 1005, R8A77995_CLK_S3D4), + DEF_MOD("ssi4", 1011, MOD_CLK_ID(1005)), + DEF_MOD("ssi3", 1012, MOD_CLK_ID(1005)), + DEF_MOD("scu-all", 1017, R8A77995_CLK_S3D4), + DEF_MOD("scu-dvc1", 1018, MOD_CLK_ID(1017)), + DEF_MOD("scu-dvc0", 1019, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu1-mix1", 1020, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu0-mix0", 1021, MOD_CLK_ID(1017)), + DEF_MOD("scu-src6", 1025, MOD_CLK_ID(1017)), + DEF_MOD("scu-src5", 1026, MOD_CLK_ID(1017)), +}; + +static const unsigned int r8a77995_crit_mod_clks[] __initconst = { + MOD_CLK_ID(408), /* INTC-AP (GIC) */ +}; + + +/* + * CPG Clock Data + */ + +/* + * MD19 EXTAL (MHz) PLL0 PLL1 PLL3 + *-------------------------------------------------------------------- + * 0 48 x 1 x250/4 x100/3 x100/3 + * 1 48 x 1 x250/4 x100/3 x116/6 + */ +#define CPG_PLL_CONFIG_INDEX(md) (((md) & BIT(19)) >> 19) + +static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[2] __initconst = { + /* EXTAL div PLL1 mult/div PLL3 mult/div */ + { 1, 100, 3, 100, 3, }, + { 1, 100, 3, 116, 6, }, +}; + +static int __init r8a77995_cpg_mssr_init(struct device *dev) +{ + const struct rcar_gen3_cpg_pll_config *cpg_pll_config; + u32 cpg_mode; + int error; + + error = rcar_rst_read_mode_pins(&cpg_mode); + if (error) + return error; + + cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; + + return rcar_gen3_cpg_init(cpg_pll_config, 0, cpg_mode); +} + +const struct cpg_mssr_info r8a77995_cpg_mssr_info __initconst = { + /* Core Clocks */ + .core_clks = r8a77995_core_clks, + .num_core_clks = ARRAY_SIZE(r8a77995_core_clks), + .last_dt_core_clk = LAST_DT_CORE_CLK, + .num_total_core_clks = MOD_CLK_BASE, + + /* Module Clocks */ + .mod_clks = r8a77995_mod_clks, + .num_mod_clks = ARRAY_SIZE(r8a77995_mod_clks), + .num_hw_mod_clks = 12 * 32, + + /* Critical Module Clocks */ + .crit_mod_clks = r8a77995_crit_mod_clks, + .num_crit_mod_clks = ARRAY_SIZE(r8a77995_crit_mod_clks), + + /* Callbacks */ + .init = r8a77995_cpg_mssr_init, + .cpg_clk_register = rcar_gen3_cpg_clk_register, +}; diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index 3dee900522b7..951105816547 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -60,6 +60,7 @@ struct sd_clock { unsigned int div_num; unsigned int div_min; unsigned int div_max; + unsigned int cur_div_idx; }; /* SDn divider @@ -96,21 +97,10 @@ static const struct sd_div_table cpg_sd_div_table[] = { static int cpg_sd_clock_enable(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - u32 val, sd_fc; - unsigned int i; - - val = readl(clock->reg); - - sd_fc = val & CPG_SD_FC_MASK; - for (i = 0; i < clock->div_num; i++) - if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) - break; - - if (i >= clock->div_num) - return -EINVAL; + u32 val = readl(clock->reg); val &= ~(CPG_SD_STP_MASK); - val |= clock->div_table[i].val & CPG_SD_STP_MASK; + val |= clock->div_table[clock->cur_div_idx].val & CPG_SD_STP_MASK; writel(val, clock->reg); @@ -135,21 +125,9 @@ static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct sd_clock *clock = to_sd_clock(hw); - unsigned long rate = parent_rate; - u32 val, sd_fc; - unsigned int i; - val = readl(clock->reg); - - sd_fc = val & CPG_SD_FC_MASK; - for (i = 0; i < clock->div_num; i++) - if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) - break; - - if (i >= clock->div_num) - return -EINVAL; - - return DIV_ROUND_CLOSEST(rate, clock->div_table[i].div); + return DIV_ROUND_CLOSEST(parent_rate, + clock->div_table[clock->cur_div_idx].div); } static unsigned int cpg_sd_clock_calc_div(struct sd_clock *clock, @@ -190,6 +168,8 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, if (i >= clock->div_num) return -EINVAL; + clock->cur_div_idx = i; + val = readl(clock->reg); val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK); val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK); @@ -215,6 +195,7 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, struct sd_clock *clock; struct clk *clk; unsigned int i; + u32 sd_fc; clock = kzalloc(sizeof(*clock), GFP_KERNEL); if (!clock) @@ -231,6 +212,18 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, clock->div_table = cpg_sd_div_table; clock->div_num = ARRAY_SIZE(cpg_sd_div_table); + sd_fc = readl(clock->reg) & CPG_SD_FC_MASK; + for (i = 0; i < clock->div_num; i++) + if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) + break; + + if (WARN_ON(i >= clock->div_num)) { + kfree(clock); + return ERR_PTR(-EINVAL); + } + + clock->cur_div_idx = i; + clock->div_max = clock->div_table[0].div; clock->div_min = clock->div_max; for (i = 1; i < clock->div_num; i++) { @@ -279,7 +272,7 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, unsigned int div = 1; u32 value; - parent = clks[core->parent]; + parent = clks[core->parent & 0xffff]; /* CLK_TYPE_PE uses high bits */ if (IS_ERR(parent)) return ERR_CAST(parent); @@ -303,6 +296,7 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, case CLK_TYPE_GEN3_PLL1: mult = cpg_pll_config->pll1_mult; + div = cpg_pll_config->pll1_div; break; case CLK_TYPE_GEN3_PLL2: @@ -320,6 +314,7 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, case CLK_TYPE_GEN3_PLL3: mult = cpg_pll_config->pll3_mult; + div = cpg_pll_config->pll3_div; break; case CLK_TYPE_GEN3_PLL4: @@ -360,6 +355,24 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, parent = clks[cpg_clk_extalr]; break; + case CLK_TYPE_GEN3_PE: + /* + * Peripheral clock with a fixed divider, selectable between + * clean and spread spectrum parents using MD12 + */ + if (cpg_mode & BIT(12)) { + /* Clean */ + div = core->div & 0xffff; + } else { + /* SCCG */ + parent = clks[core->parent >> 16]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + div = core->div >> 16; + } + mult = 1; + break; + default: return ERR_PTR(-EINVAL); } diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index 073be54b5d03..d756ef8b78eb 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -20,15 +20,24 @@ enum rcar_gen3_clk_types { CLK_TYPE_GEN3_PLL4, CLK_TYPE_GEN3_SD, CLK_TYPE_GEN3_R, + CLK_TYPE_GEN3_PE, }; #define DEF_GEN3_SD(_name, _id, _parent, _offset) \ DEF_BASE(_name, _id, CLK_TYPE_GEN3_SD, _parent, .offset = _offset) +#define DEF_GEN3_PE(_name, _id, _parent_sscg, _div_sscg, _parent_clean, \ + _div_clean) \ + DEF_BASE(_name, _id, CLK_TYPE_GEN3_PE, \ + (_parent_sscg) << 16 | (_parent_clean), \ + .div = (_div_sscg) << 16 | (_div_clean)) + struct rcar_gen3_cpg_pll_config { - unsigned int extal_div; - unsigned int pll1_mult; - unsigned int pll3_mult; + u8 extal_div; + u8 pll1_mult; + u8 pll1_div; + u8 pll3_mult; + u8 pll3_div; }; #define CPG_RCKCR 0x240 diff --git a/drivers/clk/renesas/rcar-usb2-clock-sel.c b/drivers/clk/renesas/rcar-usb2-clock-sel.c new file mode 100644 index 000000000000..6cd030a58964 --- /dev/null +++ b/drivers/clk/renesas/rcar-usb2-clock-sel.c @@ -0,0 +1,188 @@ +/* + * Renesas R-Car USB2.0 clock selector + * + * Copyright (C) 2017 Renesas Electronics Corp. + * + * Based on renesas-cpg-mssr.c + * + * Copyright (C) 2015 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#define USB20_CLKSET0 0x00 +#define CLKSET0_INTCLK_EN BIT(11) +#define CLKSET0_PRIVATE BIT(0) +#define CLKSET0_EXTAL_ONLY (CLKSET0_INTCLK_EN | CLKSET0_PRIVATE) + +struct usb2_clock_sel_priv { + void __iomem *base; + struct clk_hw hw; + bool extal; + bool xtal; +}; +#define to_priv(_hw) container_of(_hw, struct usb2_clock_sel_priv, hw) + +static void usb2_clock_sel_enable_extal_only(struct usb2_clock_sel_priv *priv) +{ + u16 val = readw(priv->base + USB20_CLKSET0); + + pr_debug("%s: enter %d %d %x\n", __func__, + priv->extal, priv->xtal, val); + + if (priv->extal && !priv->xtal && val != CLKSET0_EXTAL_ONLY) + writew(CLKSET0_EXTAL_ONLY, priv->base + USB20_CLKSET0); +} + +static void usb2_clock_sel_disable_extal_only(struct usb2_clock_sel_priv *priv) +{ + if (priv->extal && !priv->xtal) + writew(CLKSET0_PRIVATE, priv->base + USB20_CLKSET0); +} + +static int usb2_clock_sel_enable(struct clk_hw *hw) +{ + usb2_clock_sel_enable_extal_only(to_priv(hw)); + + return 0; +} + +static void usb2_clock_sel_disable(struct clk_hw *hw) +{ + usb2_clock_sel_disable_extal_only(to_priv(hw)); +} + +/* + * This module seems a mux, but this driver assumes a gate because + * ehci/ohci platform drivers don't support clk_set_parent() for now. + * If this driver acts as a gate, ehci/ohci-platform drivers don't need + * any modification. + */ +static const struct clk_ops usb2_clock_sel_clock_ops = { + .enable = usb2_clock_sel_enable, + .disable = usb2_clock_sel_disable, +}; + +static const struct of_device_id rcar_usb2_clock_sel_match[] = { + { .compatible = "renesas,rcar-gen3-usb2-clock-sel" }, + { } +}; + +static int rcar_usb2_clock_sel_suspend(struct device *dev) +{ + struct usb2_clock_sel_priv *priv = dev_get_drvdata(dev); + + usb2_clock_sel_disable_extal_only(priv); + pm_runtime_put(dev); + + return 0; +} + +static int rcar_usb2_clock_sel_resume(struct device *dev) +{ + struct usb2_clock_sel_priv *priv = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + usb2_clock_sel_enable_extal_only(priv); + + return 0; +} + +static int rcar_usb2_clock_sel_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb2_clock_sel_priv *priv = platform_get_drvdata(pdev); + + of_clk_del_provider(dev->of_node); + clk_hw_unregister(&priv->hw); + pm_runtime_put(dev); + pm_runtime_disable(dev); + + return 0; +} + +static int rcar_usb2_clock_sel_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct usb2_clock_sel_priv *priv; + struct resource *res; + struct clk *clk; + struct clk_init_data init; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + clk = devm_clk_get(dev, "usb_extal"); + if (!IS_ERR(clk) && !clk_prepare_enable(clk)) { + priv->extal = !!clk_get_rate(clk); + clk_disable_unprepare(clk); + } + clk = devm_clk_get(dev, "usb_xtal"); + if (!IS_ERR(clk) && !clk_prepare_enable(clk)) { + priv->xtal = !!clk_get_rate(clk); + clk_disable_unprepare(clk); + } + + if (!priv->extal && !priv->xtal) { + dev_err(dev, "This driver needs usb_extal or usb_xtal\n"); + return -ENOENT; + } + + platform_set_drvdata(pdev, priv); + dev_set_drvdata(dev, priv); + + init.name = "rcar_usb2_clock_sel"; + init.ops = &usb2_clock_sel_clock_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + priv->hw.init = &init; + + clk = clk_register(NULL, &priv->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_hw_provider(np, of_clk_hw_simple_get, &priv->hw); +} + +static const struct dev_pm_ops rcar_usb2_clock_sel_pm_ops = { + .suspend = rcar_usb2_clock_sel_suspend, + .resume = rcar_usb2_clock_sel_resume, +}; + +static struct platform_driver rcar_usb2_clock_sel_driver = { + .driver = { + .name = "rcar-usb2-clock-sel", + .of_match_table = rcar_usb2_clock_sel_match, + .pm = &rcar_usb2_clock_sel_pm_ops, + }, + .probe = rcar_usb2_clock_sel_probe, + .remove = rcar_usb2_clock_sel_remove, +}; +builtin_platform_driver(rcar_usb2_clock_sel_driver); + +MODULE_DESCRIPTION("Renesas R-Car USB2 clock selector Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 1f607c806f9b..e580a5e6346c 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -680,6 +680,12 @@ static const struct of_device_id cpg_mssr_match[] = { .data = &r8a7796_cpg_mssr_info, }, #endif +#ifdef CONFIG_CLK_R8A77995 + { + .compatible = "renesas,r8a77995-cpg-mssr", + .data = &r8a77995_cpg_mssr_info, + }, +#endif { /* sentinel */ } }; diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index 43d7c7f6832d..94b9071d1061 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -138,6 +138,7 @@ extern const struct cpg_mssr_info r8a7792_cpg_mssr_info; extern const struct cpg_mssr_info r8a7794_cpg_mssr_info; extern const struct cpg_mssr_info r8a7795_cpg_mssr_info; extern const struct cpg_mssr_info r8a7796_cpg_mssr_info; +extern const struct cpg_mssr_info r8a77995_cpg_mssr_info; /* diff --git a/drivers/clk/rockchip/clk-rk3128.c b/drivers/clk/rockchip/clk-rk3128.c index e243f2eae68f..62d7854e4b87 100644 --- a/drivers/clk/rockchip/clk-rk3128.c +++ b/drivers/clk/rockchip/clk-rk3128.c @@ -201,7 +201,7 @@ static struct rockchip_clk_branch rk3128_uart2_fracmux __initdata = MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT, RK2928_CLKSEL_CON(15), 8, 2, MFLAGS); -static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { +static struct rockchip_clk_branch common_clk_branches[] __initdata = { /* * Clock-Architecture Diagram 1 */ @@ -459,10 +459,6 @@ static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { RK2928_CLKSEL_CON(2), 14, 2, MFLAGS, 8, 5, DFLAGS, RK2928_CLKGATE_CON(10), 15, GFLAGS), - COMPOSITE(SCLK_SFC, "sclk_sfc", mux_sclk_sfc_src_p, 0, - RK2928_CLKSEL_CON(11), 14, 2, MFLAGS, 8, 5, DFLAGS, - RK2928_CLKGATE_CON(3), 15, GFLAGS), - COMPOSITE_NOMUX(PCLK_PMU_PRE, "pclk_pmu_pre", "cpll", 0, RK2928_CLKSEL_CON(29), 8, 6, DFLAGS, RK2928_CLKGATE_CON(1), 0, GFLAGS), @@ -495,7 +491,6 @@ static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { GATE(ACLK_DMAC, "aclk_dmac", "aclk_peri", 0, RK2928_CLKGATE_CON(5), 1, GFLAGS), GATE(0, "aclk_peri_niu", "aclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(9), 15, GFLAGS), GATE(0, "aclk_cpu_to_peri", "aclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 2, GFLAGS), - GATE(HCLK_GPS, "hclk_gps", "aclk_peri", 0, RK2928_CLKGATE_CON(3), 14, GFLAGS), GATE(HCLK_I2S_8CH, "hclk_i2s_8ch", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS), GATE(0, "hclk_peri_matrix", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 0, GFLAGS), @@ -541,7 +536,6 @@ static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { GATE(0, "hclk_rom", "hclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 6, GFLAGS), GATE(HCLK_CRYPTO, "hclk_crypto", "hclk_cpu", 0, RK2928_CLKGATE_CON(3), 5, GFLAGS), - GATE(PCLK_HDMI, "pclk_hdmi", "pclk_cpu", 0, RK2928_CLKGATE_CON(3), 8, GFLAGS), GATE(PCLK_ACODEC, "pclk_acodec", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 14, GFLAGS), GATE(0, "pclk_ddrupctl", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 7, GFLAGS), GATE(0, "pclk_grf", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 4, GFLAGS), @@ -561,6 +555,21 @@ static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc", RK3228_EMMC_CON1, 0), }; +static struct rockchip_clk_branch rk3126_clk_branches[] __initdata = { + GATE(0, "pclk_stimer", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 15, GFLAGS), + GATE(0, "pclk_s_efuse", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 14, GFLAGS), + GATE(0, "pclk_sgrf", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 8, GFLAGS), +}; + +static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { + COMPOSITE(SCLK_SFC, "sclk_sfc", mux_sclk_sfc_src_p, 0, + RK2928_CLKSEL_CON(11), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK2928_CLKGATE_CON(3), 15, GFLAGS), + + GATE(HCLK_GPS, "hclk_gps", "aclk_peri", 0, RK2928_CLKGATE_CON(3), 14, GFLAGS), + GATE(PCLK_HDMI, "pclk_hdmi", "pclk_cpu", 0, RK2928_CLKGATE_CON(3), 8, GFLAGS), +}; + static const char *const rk3128_critical_clocks[] __initconst = { "aclk_cpu", "hclk_cpu", @@ -570,7 +579,7 @@ static const char *const rk3128_critical_clocks[] __initconst = { "pclk_peri", }; -static void __init rk3128_clk_init(struct device_node *np) +static struct rockchip_clk_provider *__init rk3128_common_clk_init(struct device_node *np) { struct rockchip_clk_provider *ctx; void __iomem *reg_base; @@ -578,23 +587,21 @@ static void __init rk3128_clk_init(struct device_node *np) reg_base = of_iomap(np, 0); if (!reg_base) { pr_err("%s: could not map cru region\n", __func__); - return; + return ERR_PTR(-ENOMEM); } ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); if (IS_ERR(ctx)) { pr_err("%s: rockchip clk init failed\n", __func__); iounmap(reg_base); - return; + return ERR_PTR(-ENOMEM); } rockchip_clk_register_plls(ctx, rk3128_pll_clks, ARRAY_SIZE(rk3128_pll_clks), RK3128_GRF_SOC_STATUS0); - rockchip_clk_register_branches(ctx, rk3128_clk_branches, - ARRAY_SIZE(rk3128_clk_branches)); - rockchip_clk_protect_critical(rk3128_critical_clocks, - ARRAY_SIZE(rk3128_critical_clocks)); + rockchip_clk_register_branches(ctx, common_clk_branches, + ARRAY_SIZE(common_clk_branches)); rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), @@ -606,6 +613,40 @@ static void __init rk3128_clk_init(struct device_node *np) rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL); + return ctx; +} + +static void __init rk3126_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + + ctx = rk3128_common_clk_init(np); + if (IS_ERR(ctx)) + return; + + rockchip_clk_register_branches(ctx, rk3126_clk_branches, + ARRAY_SIZE(rk3126_clk_branches)); + rockchip_clk_protect_critical(rk3128_critical_clocks, + ARRAY_SIZE(rk3128_critical_clocks)); + + rockchip_clk_of_add_provider(np, ctx); +} + +CLK_OF_DECLARE(rk3126_cru, "rockchip,rk3126-cru", rk3126_clk_init); + +static void __init rk3128_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + + ctx = rk3128_common_clk_init(np); + if (IS_ERR(ctx)) + return; + + rockchip_clk_register_branches(ctx, rk3128_clk_branches, + ARRAY_SIZE(rk3128_clk_branches)); + rockchip_clk_protect_critical(rk3128_critical_clocks, + ARRAY_SIZE(rk3128_critical_clocks)); + rockchip_clk_of_add_provider(np, ctx); } diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c index bb405d9044a3..11e7f2d1c054 100644 --- a/drivers/clk/rockchip/clk-rk3228.c +++ b/drivers/clk/rockchip/clk-rk3228.c @@ -391,7 +391,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = { RK2928_CLKSEL_CON(11), 8, 2, MFLAGS, 0, 8, DFLAGS, RK2928_CLKGATE_CON(2), 11, GFLAGS), - COMPOSITE_NODIV(0, "sclk_sdio_src", mux_mmc_src_p, 0, + COMPOSITE_NODIV(SCLK_SDIO_SRC, "sclk_sdio_src", mux_mmc_src_p, 0, RK2928_CLKSEL_CON(11), 10, 2, MFLAGS, RK2928_CLKGATE_CON(2), 13, GFLAGS), DIV(SCLK_SDIO, "sclk_sdio", "sclk_sdio_src", 0, diff --git a/drivers/clk/rockchip/clk-rv1108.c b/drivers/clk/rockchip/clk-rv1108.c index 7c05ab366348..089cb17925e5 100644 --- a/drivers/clk/rockchip/clk-rv1108.c +++ b/drivers/clk/rockchip/clk-rv1108.c @@ -93,9 +93,24 @@ static struct rockchip_pll_rate_table rv1108_pll_rates[] = { } static struct rockchip_cpuclk_rate_table rv1108_cpuclk_rates[] __initdata = { - RV1108_CPUCLK_RATE(816000000, 4), - RV1108_CPUCLK_RATE(600000000, 4), - RV1108_CPUCLK_RATE(312000000, 4), + RV1108_CPUCLK_RATE(1608000000, 7), + RV1108_CPUCLK_RATE(1512000000, 7), + RV1108_CPUCLK_RATE(1488000000, 5), + RV1108_CPUCLK_RATE(1416000000, 5), + RV1108_CPUCLK_RATE(1392000000, 5), + RV1108_CPUCLK_RATE(1296000000, 5), + RV1108_CPUCLK_RATE(1200000000, 5), + RV1108_CPUCLK_RATE(1104000000, 5), + RV1108_CPUCLK_RATE(1008000000, 5), + RV1108_CPUCLK_RATE(912000000, 5), + RV1108_CPUCLK_RATE(816000000, 3), + RV1108_CPUCLK_RATE(696000000, 3), + RV1108_CPUCLK_RATE(600000000, 3), + RV1108_CPUCLK_RATE(500000000, 3), + RV1108_CPUCLK_RATE(408000000, 1), + RV1108_CPUCLK_RATE(312000000, 1), + RV1108_CPUCLK_RATE(216000000, 1), + RV1108_CPUCLK_RATE(96000000, 1), }; static const struct rockchip_cpuclk_reg_data rv1108_cpuclk_data = { @@ -105,7 +120,7 @@ static const struct rockchip_cpuclk_reg_data rv1108_cpuclk_data = { .mux_core_alt = 1, .mux_core_main = 0, .mux_core_shift = 8, - .mux_core_mask = 0x1, + .mux_core_mask = 0x3, }; PNAME(mux_pll_p) = { "xin24m", "xin24m"}; @@ -114,30 +129,42 @@ PNAME(mux_armclk_p) = { "apll_core", "gpll_core", "dpll_core" }; PNAME(mux_usb480m_pre_p) = { "usbphy", "xin24m" }; PNAME(mux_hdmiphy_phy_p) = { "hdmiphy", "xin24m" }; PNAME(mux_dclk_hdmiphy_pre_p) = { "dclk_hdmiphy_src_gpll", "dclk_hdmiphy_src_dpll" }; -PNAME(mux_pll_src_4plls_p) = { "dpll", "hdmiphy", "gpll", "usb480m" }; +PNAME(mux_pll_src_4plls_p) = { "dpll", "gpll", "hdmiphy", "usb480m" }; PNAME(mux_pll_src_3plls_p) = { "apll", "gpll", "dpll" }; PNAME(mux_pll_src_2plls_p) = { "dpll", "gpll" }; PNAME(mux_pll_src_apll_gpll_p) = { "apll", "gpll" }; -PNAME(mux_aclk_peri_src_p) = { "aclk_peri_src_dpll", "aclk_peri_src_gpll" }; +PNAME(mux_aclk_peri_src_p) = { "aclk_peri_src_gpll", "aclk_peri_src_dpll" }; PNAME(mux_aclk_bus_src_p) = { "aclk_bus_src_gpll", "aclk_bus_src_apll", "aclk_bus_src_dpll" }; PNAME(mux_mmc_src_p) = { "dpll", "gpll", "xin24m", "usb480m" }; PNAME(mux_pll_src_dpll_gpll_usb480m_p) = { "dpll", "gpll", "usb480m" }; PNAME(mux_uart0_p) = { "uart0_src", "uart0_frac", "xin24m" }; PNAME(mux_uart1_p) = { "uart1_src", "uart1_frac", "xin24m" }; PNAME(mux_uart2_p) = { "uart2_src", "uart2_frac", "xin24m" }; -PNAME(mux_sclk_macphy_p) = { "sclk_macphy_pre", "ext_gmac" }; +PNAME(mux_sclk_mac_p) = { "sclk_mac_pre", "ext_gmac" }; PNAME(mux_i2s0_pre_p) = { "i2s0_src", "i2s0_frac", "ext_i2s", "xin12m" }; PNAME(mux_i2s_out_p) = { "i2s0_pre", "xin12m" }; -PNAME(mux_i2s1_p) = { "i2s1_src", "i2s1_frac", "xin12m" }; -PNAME(mux_i2s2_p) = { "i2s2_src", "i2s2_frac", "xin12m" }; +PNAME(mux_i2s1_p) = { "i2s1_src", "i2s1_frac", "dummy", "xin12m" }; +PNAME(mux_i2s2_p) = { "i2s2_src", "i2s2_frac", "dummy", "xin12m" }; +PNAME(mux_wifi_src_p) = { "gpll", "xin24m" }; +PNAME(mux_cifout_src_p) = { "hdmiphy", "gpll" }; +PNAME(mux_cifout_p) = { "sclk_cifout_src", "xin24m" }; +PNAME(mux_sclk_cif0_src_p) = { "pclk_vip", "clk_cif0_chn_out", "pclkin_cvbs2cif" }; +PNAME(mux_sclk_cif1_src_p) = { "pclk_vip", "clk_cif1_chn_out", "pclkin_cvbs2cif" }; +PNAME(mux_sclk_cif2_src_p) = { "pclk_vip", "clk_cif2_chn_out", "pclkin_cvbs2cif" }; +PNAME(mux_sclk_cif3_src_p) = { "pclk_vip", "clk_cif3_chn_out", "pclkin_cvbs2cif" }; +PNAME(mux_dsp_src_p) = { "dpll", "gpll", "apll", "usb480m" }; +PNAME(mux_dclk_hdmiphy_p) = { "hdmiphy", "xin24m" }; +PNAME(mux_dclk_vop_p) = { "dclk_hdmiphy", "dclk_vop_src" }; +PNAME(mux_hdmi_cec_src_p) = { "dpll", "gpll", "xin24m" }; +PNAME(mux_cvbs_src_p) = { "apll", "io_cvbs_clkin", "hdmiphy", "gpll" }; static struct rockchip_pll_clock rv1108_pll_clks[] __initdata = { [apll] = PLL(pll_rk3399, PLL_APLL, "apll", mux_pll_p, 0, RV1108_PLL_CON(0), - RV1108_PLL_CON(3), 8, 31, 0, rv1108_pll_rates), + RV1108_PLL_CON(3), 8, 0, 0, rv1108_pll_rates), [dpll] = PLL(pll_rk3399, PLL_DPLL, "dpll", mux_pll_p, 0, RV1108_PLL_CON(8), - RV1108_PLL_CON(11), 8, 31, 0, NULL), + RV1108_PLL_CON(11), 8, 1, 0, NULL), [gpll] = PLL(pll_rk3399, PLL_GPLL, "gpll", mux_pll_p, 0, RV1108_PLL_CON(16), - RV1108_PLL_CON(19), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rv1108_pll_rates), + RV1108_PLL_CON(19), 8, 2, 0, rv1108_pll_rates), }; #define MFLAGS CLK_MUX_HIWORD_MASK @@ -170,10 +197,10 @@ static struct rockchip_clk_branch rv1108_i2s2_fracmux __initdata = RV1108_CLKSEL_CON(7), 12, 2, MFLAGS); static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { - MUX(0, "hdmi_phy", mux_hdmiphy_phy_p, CLK_SET_RATE_PARENT, - RV1108_MISC_CON, 13, 2, MFLAGS), + MUX(0, "hdmiphy", mux_hdmiphy_phy_p, CLK_SET_RATE_PARENT, + RV1108_MISC_CON, 13, 1, MFLAGS), MUX(0, "usb480m", mux_usb480m_pre_p, CLK_SET_RATE_PARENT, - RV1108_MISC_CON, 15, 2, MFLAGS), + RV1108_MISC_CON, 15, 1, MFLAGS), /* * Clock-Architecture Diagram 2 */ @@ -197,50 +224,212 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKGATE_CON(11), 1, GFLAGS), /* PD_RKVENC */ + COMPOSITE(0, "aclk_rkvenc_pre", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(37), 6, 2, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 8, GFLAGS), + FACTOR_GATE(0, "hclk_rkvenc_pre", "aclk_rkvenc_pre", 0, 1, 4, + RV1108_CLKGATE_CON(8), 10, GFLAGS), + COMPOSITE(SCLK_VENC_CORE, "clk_venc_core", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(37), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 9, GFLAGS), + GATE(ACLK_RKVENC, "aclk_rkvenc", "aclk_rkvenc_pre", 0, + RV1108_CLKGATE_CON(19), 8, GFLAGS), + GATE(HCLK_RKVENC, "hclk_rkvenc", "hclk_rkvenc_pre", 0, + RV1108_CLKGATE_CON(19), 9, GFLAGS), + GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(19), 11, GFLAGS), + GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(19), 10, GFLAGS), /* PD_RKVDEC */ + COMPOSITE(SCLK_HEVC_CORE, "sclk_hevc_core", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(36), 6, 2, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 2, GFLAGS), + FACTOR_GATE(0, "hclk_rkvdec_pre", "sclk_hevc_core", 0, 1, 4, + RV1108_CLKGATE_CON(8), 10, GFLAGS), + COMPOSITE(SCLK_HEVC_CABAC, "clk_hevc_cabac", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 1, GFLAGS), + + COMPOSITE(0, "aclk_rkvdec_pre", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(35), 6, 2, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 0, GFLAGS), + COMPOSITE(0, "aclk_vpu_pre", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(36), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 3, GFLAGS), + GATE(ACLK_RKVDEC, "aclk_rkvdec", "aclk_rkvdec_pre", 0, + RV1108_CLKGATE_CON(19), 0, GFLAGS), + GATE(ACLK_VPU, "aclk_vpu", "aclk_vpu_pre", 0, + RV1108_CLKGATE_CON(19), 1, GFLAGS), + GATE(HCLK_RKVDEC, "hclk_rkvdec", "hclk_rkvdec_pre", 0, + RV1108_CLKGATE_CON(19), 2, GFLAGS), + GATE(HCLK_VPU, "hclk_vpu", "hclk_rkvdec_pre", 0, + RV1108_CLKGATE_CON(19), 3, GFLAGS), + GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(19), 4, GFLAGS), + GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(19), 5, GFLAGS), + GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(19), 6, GFLAGS), /* PD_PMU_wrapper */ COMPOSITE_NOMUX(0, "pmu_24m_ena", "gpll", CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(38), 0, 5, DFLAGS, RV1108_CLKGATE_CON(8), 12, GFLAGS), - GATE(0, "pmu", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(0, "pclk_pmu", "pmu_24m_ena", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(10), 0, GFLAGS), - GATE(0, "intmem1", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(0, "pclk_intmem1", "pmu_24m_ena", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(10), 1, GFLAGS), - GATE(0, "gpio0_pmu", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(PCLK_GPIO0_PMU, "pclk_gpio0_pmu", "pmu_24m_ena", 0, RV1108_CLKGATE_CON(10), 2, GFLAGS), - GATE(0, "pmugrf", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(0, "pclk_pmugrf", "pmu_24m_ena", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(10), 3, GFLAGS), - GATE(0, "pmu_noc", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(0, "pclk_pmu_niu", "pmu_24m_ena", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(10), 4, GFLAGS), - GATE(0, "i2c0_pmu_pclk", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(PCLK_I2C0_PMU, "pclk_i2c0_pmu", "pmu_24m_ena", 0, RV1108_CLKGATE_CON(10), 5, GFLAGS), - GATE(0, "pwm0_pmu_pclk", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(PCLK_PWM0_PMU, "pclk_pwm0_pmu", "pmu_24m_ena", 0, RV1108_CLKGATE_CON(10), 6, GFLAGS), - COMPOSITE(0, "pwm0_pmu_clk", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + COMPOSITE(SCLK_PWM0_PMU, "sclk_pwm0_pmu", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(12), 7, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(8), 15, GFLAGS), - COMPOSITE(0, "i2c0_pmu_clk", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + COMPOSITE(SCLK_I2C0_PMU, "sclk_i2c0_pmu", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(19), 7, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(8), 14, GFLAGS), GATE(0, "pvtm_pmu", "xin24m", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(8), 13, GFLAGS), /* + * Clock-Architecture Diagram 3 + */ + COMPOSITE(SCLK_WIFI, "sclk_wifi", mux_wifi_src_p, 0, + RV1108_CLKSEL_CON(28), 15, 1, MFLAGS, 8, 6, DFLAGS, + RV1108_CLKGATE_CON(9), 8, GFLAGS), + COMPOSITE_NODIV(0, "sclk_cifout_src", mux_cifout_src_p, 0, + RV1108_CLKSEL_CON(40), 8, 1, MFLAGS, + RV1108_CLKGATE_CON(9), 11, GFLAGS), + COMPOSITE_NOGATE(SCLK_CIFOUT, "sclk_cifout", mux_cifout_p, 0, + RV1108_CLKSEL_CON(40), 12, 1, MFLAGS, 0, 5, DFLAGS), + COMPOSITE_NOMUX(SCLK_MIPI_CSI_OUT, "sclk_mipi_csi_out", "xin24m", 0, + RV1108_CLKSEL_CON(41), 0, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 12, GFLAGS), + + GATE(0, "pclk_acodecphy", "pclk_top_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 6, GFLAGS), + GATE(0, "pclk_usbgrf", "pclk_top_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 14, GFLAGS), + + GATE(ACLK_CIF0, "aclk_cif0", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(18), 10, GFLAGS), + GATE(HCLK_CIF0, "hclk_cif0", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 10, GFLAGS), + COMPOSITE_NODIV(SCLK_CIF0, "sclk_cif0", mux_sclk_cif0_src_p, 0, + RV1108_CLKSEL_CON(31), 0, 2, MFLAGS, + RV1108_CLKGATE_CON(7), 9, GFLAGS), + GATE(ACLK_CIF1, "aclk_cif1", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(17), 6, GFLAGS), + GATE(HCLK_CIF1, "hclk_cif1", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(17), 7, GFLAGS), + COMPOSITE_NODIV(SCLK_CIF1, "sclk_cif1", mux_sclk_cif1_src_p, 0, + RV1108_CLKSEL_CON(31), 2, 2, MFLAGS, + RV1108_CLKGATE_CON(7), 10, GFLAGS), + GATE(ACLK_CIF2, "aclk_cif2", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(17), 8, GFLAGS), + GATE(HCLK_CIF2, "hclk_cif2", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(17), 9, GFLAGS), + COMPOSITE_NODIV(SCLK_CIF2, "sclk_cif2", mux_sclk_cif2_src_p, 0, + RV1108_CLKSEL_CON(31), 4, 2, MFLAGS, + RV1108_CLKGATE_CON(7), 11, GFLAGS), + GATE(ACLK_CIF3, "aclk_cif3", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(17), 10, GFLAGS), + GATE(HCLK_CIF3, "hclk_cif3", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(17), 11, GFLAGS), + COMPOSITE_NODIV(SCLK_CIF3, "sclk_cif3", mux_sclk_cif3_src_p, 0, + RV1108_CLKSEL_CON(31), 6, 2, MFLAGS, + RV1108_CLKGATE_CON(7), 12, GFLAGS), + GATE(0, "pclk_cif1to4", "pclk_vip", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(7), 8, GFLAGS), + + /* PD_DSP_wrapper */ + COMPOSITE(SCLK_DSP, "sclk_dsp", mux_dsp_src_p, 0, + RV1108_CLKSEL_CON(42), 8, 2, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 0, GFLAGS), + GATE(0, "clk_dsp_sys_wd", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 0, GFLAGS), + GATE(0, "clk_dsp_epp_wd", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 1, GFLAGS), + GATE(0, "clk_dsp_edp_wd", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 2, GFLAGS), + GATE(0, "clk_dsp_iop_wd", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 3, GFLAGS), + GATE(0, "clk_dsp_free", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 13, GFLAGS), + COMPOSITE_NOMUX(SCLK_DSP_IOP, "sclk_dsp_iop", "sclk_dsp", 0, + RV1108_CLKSEL_CON(44), 0, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 1, GFLAGS), + COMPOSITE_NOMUX(SCLK_DSP_EPP, "sclk_dsp_epp", "sclk_dsp", 0, + RV1108_CLKSEL_CON(44), 8, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 2, GFLAGS), + COMPOSITE_NOMUX(SCLK_DSP_EDP, "sclk_dsp_edp", "sclk_dsp", 0, + RV1108_CLKSEL_CON(45), 0, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 3, GFLAGS), + COMPOSITE_NOMUX(SCLK_DSP_EDAP, "sclk_dsp_edap", "sclk_dsp", 0, + RV1108_CLKSEL_CON(45), 8, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 4, GFLAGS), + GATE(0, "pclk_dsp_iop_niu", "sclk_dsp_iop", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 4, GFLAGS), + GATE(0, "aclk_dsp_epp_niu", "sclk_dsp_epp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 5, GFLAGS), + GATE(0, "aclk_dsp_edp_niu", "sclk_dsp_edp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 6, GFLAGS), + GATE(0, "pclk_dsp_dbg_niu", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 7, GFLAGS), + GATE(0, "aclk_dsp_edap_niu", "sclk_dsp_edap", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 14, GFLAGS), + COMPOSITE_NOMUX(SCLK_DSP_PFM, "sclk_dsp_pfm", "sclk_dsp", 0, + RV1108_CLKSEL_CON(43), 0, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 5, GFLAGS), + COMPOSITE_NOMUX(PCLK_DSP_CFG, "pclk_dsp_cfg", "sclk_dsp", 0, + RV1108_CLKSEL_CON(43), 8, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 6, GFLAGS), + GATE(0, "pclk_dsp_cfg_niu", "pclk_dsp_cfg", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 8, GFLAGS), + GATE(0, "pclk_dsp_pfm_mon", "pclk_dsp_cfg", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 9, GFLAGS), + GATE(0, "pclk_intc", "pclk_dsp_cfg", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 10, GFLAGS), + GATE(0, "pclk_dsp_grf", "pclk_dsp_cfg", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 11, GFLAGS), + GATE(0, "pclk_mailbox", "pclk_dsp_cfg", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 12, GFLAGS), + GATE(0, "aclk_dsp_epp_perf", "sclk_dsp_epp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 15, GFLAGS), + GATE(0, "aclk_dsp_edp_perf", "sclk_dsp_edp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(11), 8, GFLAGS), + + /* * Clock-Architecture Diagram 4 */ - COMPOSITE(0, "aclk_vio0_2wrap_occ", mux_pll_src_4plls_p, CLK_IGNORE_UNUSED, + COMPOSITE(0, "aclk_vio0_pre", mux_pll_src_4plls_p, CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 5, DFLAGS, RV1108_CLKGATE_CON(6), 0, GFLAGS), - GATE(0, "aclk_vio0_pre", "aclk_vio0_2wrap_occ", CLK_IGNORE_UNUSED, + GATE(ACLK_VIO0, "aclk_vio0", "aclk_vio0_pre", 0, RV1108_CLKGATE_CON(17), 0, GFLAGS), COMPOSITE_NOMUX(0, "hclk_vio_pre", "aclk_vio0_pre", 0, RV1108_CLKSEL_CON(29), 0, 5, DFLAGS, RV1108_CLKGATE_CON(7), 2, GFLAGS), + GATE(HCLK_VIO, "hclk_vio", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(17), 2, GFLAGS), COMPOSITE_NOMUX(0, "pclk_vio_pre", "aclk_vio0_pre", 0, RV1108_CLKSEL_CON(29), 8, 5, DFLAGS, RV1108_CLKGATE_CON(7), 3, GFLAGS), + GATE(PCLK_VIO, "pclk_vio", "pclk_vio_pre", 0, + RV1108_CLKGATE_CON(17), 3, GFLAGS), + COMPOSITE(0, "aclk_vio1_pre", mux_pll_src_4plls_p, CLK_IGNORE_UNUSED, + RV1108_CLKSEL_CON(28), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(6), 1, GFLAGS), + GATE(ACLK_VIO1, "aclk_vio1", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(17), 1, GFLAGS), INVERTER(0, "pclk_vip", "ext_vip", RV1108_CLKSEL_CON(31), 8, IFLAGS), @@ -252,8 +441,63 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKGATE_CON(6), 5, GFLAGS), GATE(0, "dclk_hdmiphy_src_dpll", "dpll", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(6), 4, GFLAGS), - COMPOSITE_NOGATE(0, "dclk_hdmiphy", mux_dclk_hdmiphy_pre_p, 0, - RV1108_CLKSEL_CON(32), 6, 2, MFLAGS, 8, 6, DFLAGS), + COMPOSITE_NOGATE(0, "dclk_hdmiphy_pre", mux_dclk_hdmiphy_pre_p, 0, + RV1108_CLKSEL_CON(32), 6, 1, MFLAGS, 8, 6, DFLAGS), + COMPOSITE_NOGATE(DCLK_VOP_SRC, "dclk_vop_src", mux_dclk_hdmiphy_pre_p, 0, + RV1108_CLKSEL_CON(32), 6, 1, MFLAGS, 0, 6, DFLAGS), + MUX(DCLK_HDMIPHY, "dclk_hdmiphy", mux_dclk_hdmiphy_p, CLK_SET_RATE_PARENT, + RV1108_CLKSEL_CON(32), 15, 1, MFLAGS), + MUX(DCLK_VOP, "dclk_vop", mux_dclk_vop_p, CLK_SET_RATE_PARENT, + RV1108_CLKSEL_CON(32), 7, 1, MFLAGS), + GATE(ACLK_VOP, "aclk_vop", "aclk_vio0_pre", 0, + RV1108_CLKGATE_CON(18), 0, GFLAGS), + GATE(HCLK_VOP, "hclk_vop", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 1, GFLAGS), + GATE(ACLK_IEP, "aclk_iep", "aclk_vio0_pre", 0, + RV1108_CLKGATE_CON(18), 2, GFLAGS), + GATE(HCLK_IEP, "hclk_iep", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 3, GFLAGS), + + GATE(ACLK_RGA, "aclk_rga", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(18), 4, GFLAGS), + GATE(HCLK_RGA, "hclk_rga", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 5, GFLAGS), + COMPOSITE(SCLK_RGA, "sclk_rga", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(33), 6, 2, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(6), 6, GFLAGS), + + COMPOSITE(SCLK_CVBS_HOST, "sclk_cvbs_host", mux_cvbs_src_p, 0, + RV1108_CLKSEL_CON(33), 13, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(6), 7, GFLAGS), + FACTOR(0, "sclk_cvbs_27m", "sclk_cvbs_host", 0, 1, 2), + + GATE(SCLK_HDMI_SFR, "sclk_hdmi_sfr", "xin24m", 0, + RV1108_CLKGATE_CON(6), 8, GFLAGS), + + COMPOSITE(SCLK_HDMI_CEC, "sclk_hdmi_cec", mux_hdmi_cec_src_p, 0, + RV1108_CLKSEL_CON(34), 14, 2, MFLAGS, 0, 14, DFLAGS, + RV1108_CLKGATE_CON(6), 9, GFLAGS), + GATE(PCLK_MIPI_DSI, "pclk_mipi_dsi", "pclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 8, GFLAGS), + GATE(PCLK_HDMI_CTRL, "pclk_hdmi_ctrl", "pclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 9, GFLAGS), + + GATE(ACLK_ISP, "aclk_isp", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(18), 12, GFLAGS), + GATE(HCLK_ISP, "hclk_isp", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 11, GFLAGS), + COMPOSITE(SCLK_ISP, "sclk_isp", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(30), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(6), 3, GFLAGS), + + GATE(0, "clk_dsiphy24m", "xin24m", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(9), 10, GFLAGS), + GATE(0, "pclk_vdacphy", "pclk_top_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 9, GFLAGS), + GATE(0, "pclk_mipi_dsiphy", "pclk_top_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 11, GFLAGS), + GATE(0, "pclk_mipi_csiphy", "pclk_top_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 12, GFLAGS), /* * Clock-Architecture Diagram 5 @@ -261,10 +505,11 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { FACTOR(0, "xin12m", "xin24m", 0, 1, 2), - COMPOSITE(0, "i2s0_src", mux_pll_src_2plls_p, 0, + + COMPOSITE(SCLK_I2S0_SRC, "i2s0_src", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(5), 8, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(2), 0, GFLAGS), - COMPOSITE_FRACMUX(0, "i2s1_frac", "i2s1_src", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "i2s0_frac", "i2s0_src", CLK_SET_RATE_PARENT, RV1108_CLKSEL_CON(8), 0, RV1108_CLKGATE_CON(2), 1, GFLAGS, &rv1108_i2s0_fracmux), @@ -274,7 +519,7 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKSEL_CON(5), 15, 1, MFLAGS, RV1108_CLKGATE_CON(2), 3, GFLAGS), - COMPOSITE(0, "i2s1_src", mux_pll_src_2plls_p, 0, + COMPOSITE(SCLK_I2S1_SRC, "i2s1_src", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(6), 8, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(2), 4, GFLAGS), COMPOSITE_FRACMUX(0, "i2s1_frac", "i2s1_src", CLK_SET_RATE_PARENT, @@ -284,7 +529,7 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { GATE(SCLK_I2S1, "sclk_i2s1", "i2s1_pre", CLK_SET_RATE_PARENT, RV1108_CLKGATE_CON(2), 6, GFLAGS), - COMPOSITE(0, "i2s2_src", mux_pll_src_2plls_p, 0, + COMPOSITE(SCLK_I2S2_SRC, "i2s2_src", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(7), 8, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(3), 8, GFLAGS), COMPOSITE_FRACMUX(0, "i2s2_frac", "i2s2_src", CLK_SET_RATE_PARENT, @@ -303,32 +548,53 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKGATE_CON(1), 2, GFLAGS), COMPOSITE_NOGATE(ACLK_PRE, "aclk_bus_pre", mux_aclk_bus_src_p, 0, RV1108_CLKSEL_CON(2), 8, 2, MFLAGS, 0, 5, DFLAGS), - COMPOSITE_NOMUX(0, "hclk_bus_pre", "aclk_bus_2wrap_occ", 0, + COMPOSITE_NOMUX(HCLK_BUS, "hclk_bus_pre", "aclk_bus_pre", 0, RV1108_CLKSEL_CON(3), 0, 5, DFLAGS, RV1108_CLKGATE_CON(1), 4, GFLAGS), - COMPOSITE_NOMUX(0, "pclken_bus", "aclk_bus_2wrap_occ", 0, + COMPOSITE_NOMUX(0, "pclk_bus_pre", "aclk_bus_pre", 0, RV1108_CLKSEL_CON(3), 8, 5, DFLAGS, RV1108_CLKGATE_CON(1), 5, GFLAGS), - GATE(0, "pclk_bus_pre", "pclken_bus", CLK_IGNORE_UNUSED, + GATE(PCLK_BUS, "pclk_bus", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(1), 6, GFLAGS), - GATE(0, "pclk_top_pre", "pclken_bus", CLK_IGNORE_UNUSED, + GATE(0, "pclk_top_pre", "pclk_bus_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(1), 7, GFLAGS), - GATE(0, "pclk_ddr_pre", "pclken_bus", CLK_IGNORE_UNUSED, + GATE(0, "pclk_ddr_pre", "pclk_bus_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(1), 8, GFLAGS), - GATE(0, "clk_timer0", "mux_pll_p", CLK_IGNORE_UNUSED, + GATE(SCLK_TIMER0, "clk_timer0", "xin24m", 0, RV1108_CLKGATE_CON(1), 9, GFLAGS), - GATE(0, "clk_timer1", "mux_pll_p", CLK_IGNORE_UNUSED, + GATE(SCLK_TIMER1, "clk_timer1", "xin24m", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(1), 10, GFLAGS), - GATE(0, "pclk_timer", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_TIMER, "pclk_timer", "pclk_bus_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(13), 4, GFLAGS), - COMPOSITE(0, "uart0_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, + GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 7, GFLAGS), + GATE(HCLK_I2S1_2CH, "hclk_i2s1_2ch", "hclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 8, GFLAGS), + GATE(HCLK_I2S2_2CH, "hclk_i2s2_2ch", "hclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 9, GFLAGS), + + GATE(HCLK_CRYPTO_MST, "hclk_crypto_mst", "hclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 10, GFLAGS), + GATE(HCLK_CRYPTO_SLV, "hclk_crypto_slv", "hclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 11, GFLAGS), + COMPOSITE(SCLK_CRYPTO, "sclk_crypto", mux_pll_src_2plls_p, 0, + RV1108_CLKSEL_CON(11), 7, 1, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(2), 12, GFLAGS), + + COMPOSITE(SCLK_SPI, "sclk_spi", mux_pll_src_2plls_p, 0, + RV1108_CLKSEL_CON(11), 15, 1, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(3), 0, GFLAGS), + GATE(PCLK_SPI, "pclk_spi", "pclk_bus_pre", 0, + RV1108_CLKGATE_CON(13), 5, GFLAGS), + + COMPOSITE(SCLK_UART0_SRC, "uart0_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(13), 12, 2, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(3), 1, GFLAGS), - COMPOSITE(0, "uart1_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, + COMPOSITE(SCLK_UART1_SRC, "uart1_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(14), 12, 2, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(3), 3, GFLAGS), - COMPOSITE(0, "uart21_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, + COMPOSITE(SCLK_UART2_SRC, "uart2_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(15), 12, 2, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(3), 5, GFLAGS), @@ -344,44 +610,58 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKSEL_CON(18), 0, RV1108_CLKGATE_CON(3), 6, GFLAGS, &rv1108_uart2_fracmux), - GATE(PCLK_UART0, "pclk_uart0", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_UART0, "pclk_uart0", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 10, GFLAGS), - GATE(PCLK_UART1, "pclk_uart1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_UART1, "pclk_uart1", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 11, GFLAGS), - GATE(PCLK_UART2, "pclk_uart2", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_UART2, "pclk_uart2", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 12, GFLAGS), - COMPOSITE(0, "clk_i2c1", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, - RV1108_CLKSEL_CON(19), 15, 2, MFLAGS, 8, 7, DFLAGS, + COMPOSITE(SCLK_I2C1, "clk_i2c1", mux_pll_src_2plls_p, 0, + RV1108_CLKSEL_CON(19), 15, 1, MFLAGS, 8, 7, DFLAGS, RV1108_CLKGATE_CON(3), 7, GFLAGS), - COMPOSITE(0, "clk_i2c2", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, - RV1108_CLKSEL_CON(20), 7, 2, MFLAGS, 0, 7, DFLAGS, + COMPOSITE(SCLK_I2C2, "clk_i2c2", mux_pll_src_2plls_p, 0, + RV1108_CLKSEL_CON(20), 7, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(3), 8, GFLAGS), - COMPOSITE(0, "clk_i2c3", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, - RV1108_CLKSEL_CON(20), 15, 2, MFLAGS, 8, 7, DFLAGS, + COMPOSITE(SCLK_I2C3, "clk_i2c3", mux_pll_src_2plls_p, 0, + RV1108_CLKSEL_CON(20), 15, 1, MFLAGS, 8, 7, DFLAGS, RV1108_CLKGATE_CON(3), 9, GFLAGS), - GATE(0, "pclk_i2c1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_I2C1, "pclk_i2c1", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 0, GFLAGS), - GATE(0, "pclk_i2c2", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_I2C2, "pclk_i2c2", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 1, GFLAGS), - GATE(0, "pclk_i2c3", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_I2C3, "pclk_i2c3", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 2, GFLAGS), - COMPOSITE(0, "clk_pwm1", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + COMPOSITE(SCLK_PWM, "clk_pwm", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(12), 15, 2, MFLAGS, 8, 7, DFLAGS, RV1108_CLKGATE_CON(3), 10, GFLAGS), - GATE(0, "pclk_pwm1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_PWM, "pclk_pwm", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 6, GFLAGS), - GATE(0, "pclk_wdt", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_WDT, "pclk_wdt", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 3, GFLAGS), - GATE(0, "pclk_gpio1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 7, GFLAGS), - GATE(0, "pclk_gpio2", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 8, GFLAGS), - GATE(0, "pclk_gpio3", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 9, GFLAGS), GATE(0, "pclk_grf", "pclk_bus_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(14), 0, GFLAGS), + GATE(PCLK_EFUSE0, "pclk_efuse0", "pclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 12, GFLAGS), + GATE(PCLK_EFUSE1, "pclk_efuse1", "pclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 13, GFLAGS), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_bus_pre", 0, + RV1108_CLKGATE_CON(13), 13, GFLAGS), + COMPOSITE_NOMUX(SCLK_TSADC, "sclk_tsadc", "xin24m", 0, + RV1108_CLKSEL_CON(21), 0, 10, DFLAGS, + RV1108_CLKGATE_CON(3), 11, GFLAGS), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_bus_pre", 0, + RV1108_CLKGATE_CON(13), 14, GFLAGS), + COMPOSITE_NOMUX(SCLK_SARADC, "sclk_saradc", "xin24m", 0, + RV1108_CLKSEL_CON(22), 0, 10, DFLAGS, + RV1108_CLKGATE_CON(3), 12, GFLAGS), GATE(ACLK_DMAC, "aclk_dmac", "aclk_bus_pre", 0, RV1108_CLKGATE_CON(12), 2, GFLAGS), @@ -397,18 +677,24 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKGATE_CON(0), 9, GFLAGS), GATE(0, "gpll_ddr", "gpll", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(0), 10, GFLAGS), - COMPOSITE(0, "ddrphy4x", mux_ddrphy_p, CLK_IGNORE_UNUSED, + COMPOSITE_NOGATE(0, "clk_ddrphy_src", mux_ddrphy_p, CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(4), 8, 2, MFLAGS, 0, 3, - DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + DFLAGS | CLK_DIVIDER_POWER_OF_TWO), + FACTOR(0, "clk_ddr", "clk_ddrphy_src", 0, 1, 2), + GATE(0, "clk_ddrphy4x", "clk_ddr", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(10), 9, GFLAGS), - GATE(0, "ddrupctl", "ddrphy_pre", CLK_IGNORE_UNUSED, + GATE(0, "pclk_ddrupctl", "pclk_ddr_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(12), 4, GFLAGS), - GATE(0, "ddrc", "ddrphy", CLK_IGNORE_UNUSED, + GATE(0, "nclk_ddrupctl", "clk_ddr", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(12), 5, GFLAGS), - GATE(0, "ddrmon", "ddrphy_pre", CLK_IGNORE_UNUSED, + GATE(0, "pclk_ddrmon", "pclk_ddr_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(12), 6, GFLAGS), GATE(0, "timer_clk", "xin24m", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(0), 11, GFLAGS), + GATE(0, "pclk_mschniu", "pclk_ddr_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 2, GFLAGS), + GATE(0, "pclk_ddrphy", "pclk_ddr_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 4, GFLAGS), /* * Clock-Architecture Diagram 6 @@ -418,23 +704,23 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "pclk_periph_pre", "gpll", 0, RV1108_CLKSEL_CON(23), 10, 5, DFLAGS, RV1108_CLKGATE_CON(4), 5, GFLAGS), - GATE(0, "pclk_periph", "pclk_periph_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_PERI, "pclk_periph", "pclk_periph_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(15), 13, GFLAGS), COMPOSITE_NOMUX(0, "hclk_periph_pre", "gpll", 0, RV1108_CLKSEL_CON(23), 5, 5, DFLAGS, RV1108_CLKGATE_CON(4), 4, GFLAGS), - GATE(0, "hclk_periph", "hclk_periph_pre", CLK_IGNORE_UNUSED, + GATE(HCLK_PERI, "hclk_periph", "hclk_periph_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(15), 12, GFLAGS), GATE(0, "aclk_peri_src_dpll", "dpll", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(4), 1, GFLAGS), GATE(0, "aclk_peri_src_gpll", "gpll", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(4), 2, GFLAGS), - COMPOSITE(0, "aclk_periph", mux_aclk_peri_src_p, CLK_IGNORE_UNUSED, - RV1108_CLKSEL_CON(23), 15, 2, MFLAGS, 0, 5, DFLAGS, + COMPOSITE(ACLK_PERI, "aclk_periph", mux_aclk_peri_src_p, 0, + RV1108_CLKSEL_CON(23), 15, 1, MFLAGS, 0, 5, DFLAGS, RV1108_CLKGATE_CON(15), 11, GFLAGS), - COMPOSITE(SCLK_SDMMC, "sclk_sdmmc0", mux_mmc_src_p, 0, + COMPOSITE(SCLK_SDMMC, "sclk_sdmmc", mux_mmc_src_p, 0, RV1108_CLKSEL_CON(25), 8, 2, MFLAGS, 0, 8, DFLAGS, RV1108_CLKGATE_CON(5), 0, GFLAGS), @@ -454,23 +740,31 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { GATE(HCLK_EMMC, "hclk_emmc", "hclk_periph", 0, RV1108_CLKGATE_CON(15), 2, GFLAGS), COMPOSITE(SCLK_NANDC, "sclk_nandc", mux_pll_src_2plls_p, 0, - RV1108_CLKSEL_CON(27), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKSEL_CON(27), 14, 1, MFLAGS, 8, 5, DFLAGS, RV1108_CLKGATE_CON(5), 3, GFLAGS), GATE(HCLK_NANDC, "hclk_nandc", "hclk_periph", 0, RV1108_CLKGATE_CON(15), 3, GFLAGS), + GATE(HCLK_HOST0, "hclk_host0", "hclk_periph", 0, RV1108_CLKGATE_CON(15), 6, GFLAGS), + GATE(0, "hclk_host0_arb", "hclk_periph", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(15), 7, GFLAGS), + GATE(HCLK_OTG, "hclk_otg", "hclk_periph", 0, RV1108_CLKGATE_CON(15), 8, GFLAGS), + GATE(0, "hclk_otg_pmu", "hclk_periph", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(15), 9, GFLAGS), + GATE(SCLK_USBPHY, "clk_usbphy", "xin24m", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(5), 5, GFLAGS), + COMPOSITE(SCLK_SFC, "sclk_sfc", mux_pll_src_2plls_p, 0, - RV1108_CLKSEL_CON(27), 7, 2, MFLAGS, 0, 7, DFLAGS, + RV1108_CLKSEL_CON(27), 7, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(5), 4, GFLAGS), GATE(HCLK_SFC, "hclk_sfc", "hclk_periph", 0, RV1108_CLKGATE_CON(15), 10, GFLAGS), - COMPOSITE(0, "sclk_macphy_pre", mux_pll_src_apll_gpll_p, 0, - RV1108_CLKSEL_CON(24), 12, 2, MFLAGS, 0, 5, DFLAGS, + COMPOSITE(SCLK_MAC_PRE, "sclk_mac_pre", mux_pll_src_apll_gpll_p, 0, + RV1108_CLKSEL_CON(24), 12, 1, MFLAGS, 0, 5, DFLAGS, RV1108_CLKGATE_CON(4), 10, GFLAGS), - MUX(0, "sclk_macphy", mux_sclk_macphy_p, CLK_SET_RATE_PARENT, - RV1108_CLKSEL_CON(24), 8, 2, MFLAGS), - GATE(0, "sclk_macphy_rx", "sclk_macphy", 0, RV1108_CLKGATE_CON(4), 8, GFLAGS), - GATE(0, "sclk_mac_ref", "sclk_macphy", 0, RV1108_CLKGATE_CON(4), 6, GFLAGS), - GATE(0, "sclk_mac_refout", "sclk_macphy", 0, RV1108_CLKGATE_CON(4), 7, GFLAGS), + MUX(SCLK_MAC, "sclk_mac", mux_sclk_mac_p, CLK_SET_RATE_PARENT, + RV1108_CLKSEL_CON(24), 8, 1, MFLAGS), + GATE(SCLK_MAC_RX, "sclk_mac_rx", "sclk_mac", 0, RV1108_CLKGATE_CON(4), 8, GFLAGS), + GATE(SCLK_MAC_REF, "sclk_mac_ref", "sclk_mac", 0, RV1108_CLKGATE_CON(4), 6, GFLAGS), + GATE(SCLK_MAC_REFOUT, "sclk_mac_refout", "sclk_mac", 0, RV1108_CLKGATE_CON(4), 7, GFLAGS), + GATE(ACLK_GMAC, "aclk_gmac", "aclk_periph", 0, RV1108_CLKGATE_CON(15), 4, GFLAGS), + GATE(PCLK_GMAC, "pclk_gmac", "pclk_periph", 0, RV1108_CLKGATE_CON(15), 5, GFLAGS), MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", RV1108_SDMMC_CON0, 1), MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RV1108_SDMMC_CON1, 1), @@ -484,10 +778,16 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { static const char *const rv1108_critical_clocks[] __initconst = { "aclk_core", - "aclk_bus_src_gpll", + "aclk_bus", + "hclk_bus", + "pclk_bus", "aclk_periph", "hclk_periph", "pclk_periph", + "nclk_ddrupctl", + "pclk_ddrmon", + "pclk_acodecphy", + "pclk_pmu", }; static void __init rv1108_clk_init(struct device_node *np) diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index fe1d393cf678..35dbd63c2f49 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -29,6 +29,7 @@ #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <linux/reboot.h> +#include <linux/rational.h> #include "clk.h" /** @@ -164,6 +165,40 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, return notifier_from_errno(ret); } +/** + * fractional divider must set that denominator is 20 times larger than + * numerator to generate precise clock frequency. + */ +static void rockchip_fractional_approximation(struct clk_hw *hw, + unsigned long rate, unsigned long *parent_rate, + unsigned long *m, unsigned long *n) +{ + struct clk_fractional_divider *fd = to_clk_fd(hw); + unsigned long p_rate, p_parent_rate; + struct clk_hw *p_parent; + unsigned long scale; + + p_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); + if ((rate * 20 > p_rate) && (p_rate % rate != 0)) { + p_parent = clk_hw_get_parent(clk_hw_get_parent(hw)); + p_parent_rate = clk_hw_get_rate(p_parent); + *parent_rate = p_parent_rate; + } + + /* + * Get rate closer to *parent_rate to guarantee there is no overflow + * for m and n. In the result it will be the nearest rate left shifted + * by (scale - fd->nwidth) bits. + */ + scale = fls_long(*parent_rate / rate - 1); + if (scale > fd->nwidth) + rate <<= scale - fd->nwidth; + + rational_best_approximation(rate, *parent_rate, + GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), + m, n); +} + static struct clk *rockchip_clk_register_frac_branch( struct rockchip_clk_provider *ctx, const char *name, const char *const *parent_names, u8 num_parents, @@ -210,6 +245,7 @@ static struct clk *rockchip_clk_register_frac_branch( div->nwidth = 16; div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift; div->lock = lock; + div->approximation = rockchip_fractional_approximation; div_ops = &clk_fractional_divider_ops; clk = clk_register_composite(NULL, name, parent_names, num_parents, diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c index 1fab56f396d4..b117783ed404 100644 --- a/drivers/clk/samsung/clk-exynos-audss.c +++ b/drivers/clk/samsung/clk-exynos-audss.c @@ -180,7 +180,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) } clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(NULL, "mout_audss", mout_audss_p, ARRAY_SIZE(mout_audss_p), - CLK_SET_RATE_NO_REPARENT, + CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg_base + ASS_CLK_SRC, 0, 1, 0, &lock); cdclk = devm_clk_get(&pdev->dev, "cdclk"); @@ -195,11 +195,11 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) reg_base + ASS_CLK_SRC, 2, 2, 0, &lock); clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(NULL, "dout_srp", - "mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4, - 0, &lock); + "mout_audss", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_DIV, 0, 4, 0, &lock); clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(NULL, - "dout_aud_bus", "dout_srp", 0, + "dout_aud_bus", "dout_srp", CLK_SET_RATE_PARENT, reg_base + ASS_CLK_DIV, 4, 4, 0, &lock); clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(NULL, "dout_i2s", diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 9a6476aa7d81..25601967d1cd 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -537,8 +537,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = { MUX(CLK_MOUT_MX_MSPLL_CCORE, "mout_mx_mspll_ccore", mout_mx_mspll_ccore_p, SRC_TOP7, 16, 2), - MUX(CLK_MOUT_MAU_EPLL, "mout_mau_epll_clk", mout_mau_epll_clk_5800_p, - SRC_TOP7, 20, 2), + MUX_F(CLK_MOUT_MAU_EPLL, "mout_mau_epll_clk", mout_mau_epll_clk_5800_p, + SRC_TOP7, 20, 2, CLK_SET_RATE_PARENT, 0), MUX(0, "sclk_bpll", mout_bpll_p, SRC_TOP7, 24, 1), MUX(0, "mout_epll2", mout_epll2_5800_p, SRC_TOP7, 28, 1), @@ -547,8 +547,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = { MUX(0, "mout_aclk432_cam", mout_group6_5800_p, SRC_TOP8, 24, 2), MUX(0, "mout_aclk432_scaler", mout_group6_5800_p, SRC_TOP8, 28, 2), - MUX(CLK_MOUT_USER_MAU_EPLL, "mout_user_mau_epll", mout_group16_5800_p, - SRC_TOP9, 8, 1), + MUX_F(CLK_MOUT_USER_MAU_EPLL, "mout_user_mau_epll", mout_group16_5800_p, + SRC_TOP9, 8, 1, CLK_SET_RATE_PARENT, 0), MUX(0, "mout_user_aclk550_cam", mout_group15_5800_p, SRC_TOP9, 16, 1), MUX(0, "mout_user_aclkfl1_550_cam", mout_group13_5800_p, @@ -590,6 +590,8 @@ static const struct samsung_gate_clock exynos5800_gate_clks[] __initconst = { GATE_BUS_TOP, 24, 0, 0), GATE(CLK_ACLK432_SCALER, "aclk432_scaler", "mout_user_aclk432_scaler", GATE_BUS_TOP, 27, CLK_IS_CRITICAL, 0), + GATE(CLK_MAU_EPLL, "mau_epll", "mout_user_mau_epll", + SRC_MASK_TOP7, 20, CLK_SET_RATE_PARENT, 0), }; static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = { @@ -629,6 +631,11 @@ static const struct samsung_div_clock exynos5420_div_clks[] __initconst = { "mout_aclk400_wcore_bpll", DIV_TOP0, 16, 3), }; +static const struct samsung_gate_clock exynos5420_gate_clks[] __initconst = { + GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk", + SRC_MASK_TOP7, 20, CLK_SET_RATE_PARENT, 0), +}; + static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = { MUX(0, "mout_user_pclk66_gpio", mout_user_pclk66_gpio_p, SRC_TOP7, 4, 1), @@ -706,7 +713,8 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = { MUX(0, "mout_sclk_spll", mout_spll_p, SRC_TOP6, 8, 1), MUX(0, "mout_sclk_ipll", mout_ipll_p, SRC_TOP6, 12, 1), MUX(0, "mout_sclk_rpll", mout_rpll_p, SRC_TOP6, 16, 1), - MUX(CLK_MOUT_EPLL, "mout_sclk_epll", mout_epll_p, SRC_TOP6, 20, 1), + MUX_F(CLK_MOUT_EPLL, "mout_sclk_epll", mout_epll_p, SRC_TOP6, 20, 1, + CLK_SET_RATE_PARENT, 0), MUX(0, "mout_sclk_dpll", mout_dpll_p, SRC_TOP6, 24, 1), MUX(0, "mout_sclk_cpll", mout_cpll_p, SRC_TOP6, 28, 1), @@ -1001,9 +1009,6 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = { GATE(0, "aclk300_disp1", "mout_user_aclk300_disp1", SRC_MASK_TOP2, 24, CLK_IS_CRITICAL, 0), - GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk", - SRC_MASK_TOP7, 20, 0, 0), - /* sclk */ GATE(CLK_SCLK_UART0, "sclk_uart0", "dout_uart0", GATE_TOP_SCLK_PERIC, 0, CLK_SET_RATE_PARENT, 0), @@ -1440,6 +1445,8 @@ static void __init exynos5x_clk_init(struct device_node *np, ARRAY_SIZE(exynos5420_mux_clks)); samsung_clk_register_div(ctx, exynos5420_div_clks, ARRAY_SIZE(exynos5420_div_clks)); + samsung_clk_register_gate(ctx, exynos5420_gate_clks, + ARRAY_SIZE(exynos5420_gate_clks)); } else { samsung_clk_register_fixed_factor( ctx, exynos5800_fixed_factor_clks, diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index 7342928c35cd..6427d0ebe2de 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -11,6 +11,19 @@ config SUN50I_A64_CCU default ARM64 && ARCH_SUNXI depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST +config SUN4I_A10_CCU + bool "Support for the Allwinner A10/A20 CCU" + select SUNXI_CCU_DIV + select SUNXI_CCU_MULT + select SUNXI_CCU_NK + select SUNXI_CCU_NKM + select SUNXI_CCU_NM + select SUNXI_CCU_MP + select SUNXI_CCU_PHASE + default MACH_SUN4I + default MACH_SUN7I + depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST + config SUN5I_CCU bool "Support for the Allwinner sun5i family CCM" default MACH_SUN5I @@ -48,6 +61,11 @@ config SUN8I_V3S_CCU config SUN8I_DE2_CCU bool "Support for the Allwinner SoCs DE2 CCU" +config SUN8I_R40_CCU + bool "Support for the Allwinner R40 CCU" + default MACH_SUN8I + depends on MACH_SUN8I || COMPILE_TEST + config SUN9I_A80_CCU bool "Support for the Allwinner A80 CCU" default MACH_SUN9I diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile index 45a5910379a5..85a0633c1eac 100644 --- a/drivers/clk/sunxi-ng/Makefile +++ b/drivers/clk/sunxi-ng/Makefile @@ -20,6 +20,7 @@ lib-$(CONFIG_SUNXI_CCU) += ccu_mp.o # SoC support obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o +obj-$(CONFIG_SUN4I_A10_CCU) += ccu-sun4i-a10.o obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.o obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o @@ -29,6 +30,7 @@ obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o obj-$(CONFIG_SUN8I_DE2_CCU) += ccu-sun8i-de2.o obj-$(CONFIG_SUN8I_R_CCU) += ccu-sun8i-r.o +obj-$(CONFIG_SUN8I_R40_CCU) += ccu-sun8i-r40.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-de.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-usb.o diff --git a/drivers/clk/sunxi-ng/ccu-sun4i-a10.c b/drivers/clk/sunxi-ng/ccu-sun4i-a10.c new file mode 100644 index 000000000000..286b0049b7b6 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun4i-a10.c @@ -0,0 +1,1456 @@ +/* + * Copyright (c) 2017 Priit Laes <plaes@plaes.org>. + * Copyright (c) 2017 Maxime Ripard. + * Copyright (c) 2017 Jonathan Liu. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of_address.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun4i-a10.h" + +static struct ccu_nkmp pll_core_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV(16, 2), + .common = { + .reg = 0x000, + .hw.init = CLK_HW_INIT("pll-core", + "hosc", + &ccu_nkmp_ops, + 0), + }, +}; + +/* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from + * the base (2x, 4x and 8x), and one variable divider (the one true + * pll audio). + * + * We don't have any need for the variable divider for now, so we just + * hardcode it to match with the clock names. + */ +#define SUN4I_PLL_AUDIO_REG 0x008 +static struct ccu_nm pll_audio_base_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 7, 0), + .m = _SUNXI_CCU_DIV_OFFSET(0, 5, 0), + .common = { + .reg = 0x008, + .hw.init = CLK_HW_INIT("pll-audio-base", + "hosc", + &ccu_nm_ops, + 0), + }, + +}; + +static struct ccu_mult pll_video0_clk = { + .enable = BIT(31), + .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(0, 7, 0, 9, 127), + .frac = _SUNXI_CCU_FRAC(BIT(15), BIT(14), + 270000000, 297000000), + .common = { + .reg = 0x010, + .features = (CCU_FEATURE_FRACTIONAL | + CCU_FEATURE_ALL_PREDIV), + .prediv = 8, + .hw.init = CLK_HW_INIT("pll-video0", + "hosc", + &ccu_mult_ops, + 0), + }, +}; + +static struct ccu_nkmp pll_ve_sun4i_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV(16, 2), + .common = { + .reg = 0x018, + .hw.init = CLK_HW_INIT("pll-ve", + "hosc", + &ccu_nkmp_ops, + 0), + }, +}; + +static struct ccu_nk pll_ve_sun7i_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .common = { + .reg = 0x018, + .hw.init = CLK_HW_INIT("pll-ve", + "hosc", + &ccu_nk_ops, + 0), + }, +}; + +static struct ccu_nk pll_ddr_base_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .common = { + .reg = 0x020, + .hw.init = CLK_HW_INIT("pll-ddr-base", + "hosc", + &ccu_nk_ops, + 0), + }, +}; + +static SUNXI_CCU_M(pll_ddr_clk, "pll-ddr", "pll-ddr-base", 0x020, 0, 2, + CLK_IS_CRITICAL); + +static struct ccu_div pll_ddr_other_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(16, 2, CLK_DIVIDER_POWER_OF_TWO), + .common = { + .reg = 0x020, + .hw.init = CLK_HW_INIT("pll-ddr-other", "pll-ddr-base", + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_nk pll_periph_base_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .common = { + .reg = 0x028, + .hw.init = CLK_HW_INIT("pll-periph-base", + "hosc", + &ccu_nk_ops, + 0), + }, +}; + +static CLK_FIXED_FACTOR(pll_periph_clk, "pll-periph", "pll-periph-base", + 2, 1, CLK_SET_RATE_PARENT); + +/* Not documented on A10 */ +static struct ccu_div pll_periph_sata_clk = { + .enable = BIT(14), + .div = _SUNXI_CCU_DIV(0, 2), + .fixed_post_div = 6, + .common = { + .reg = 0x028, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph-sata", + "pll-periph-base", + &ccu_div_ops, 0), + }, +}; + +static struct ccu_mult pll_video1_clk = { + .enable = BIT(31), + .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(0, 7, 0, 9, 127), + .frac = _SUNXI_CCU_FRAC(BIT(15), BIT(14), + 270000000, 297000000), + .common = { + .reg = 0x030, + .features = (CCU_FEATURE_FRACTIONAL | + CCU_FEATURE_ALL_PREDIV), + .prediv = 8, + .hw.init = CLK_HW_INIT("pll-video1", + "hosc", + &ccu_mult_ops, + 0), + }, +}; + +/* Not present on A10 */ +static struct ccu_nk pll_gpu_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .common = { + .reg = 0x040, + .hw.init = CLK_HW_INIT("pll-gpu", + "hosc", + &ccu_nk_ops, + 0), + }, +}; + +static SUNXI_CCU_GATE(hosc_clk, "hosc", "osc24M", 0x050, BIT(0), 0); + +static const char *const cpu_parents[] = { "osc32k", "hosc", + "pll-core", "pll-periph" }; +static const struct ccu_mux_fixed_prediv cpu_predivs[] = { + { .index = 3, .div = 3, }, +}; + +#define SUN4I_AHB_REG 0x054 +static struct ccu_mux cpu_clk = { + .mux = { + .shift = 16, + .width = 2, + .fixed_predivs = cpu_predivs, + .n_predivs = ARRAY_SIZE(cpu_predivs), + }, + .common = { + .reg = 0x054, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("cpu", + cpu_parents, + &ccu_mux_ops, + CLK_IS_CRITICAL), + } +}; + +static SUNXI_CCU_M(axi_clk, "axi", "cpu", 0x054, 0, 2, 0); + +static struct ccu_div ahb_sun4i_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + .common = { + .reg = 0x054, + .hw.init = CLK_HW_INIT("ahb", "axi", &ccu_div_ops, 0), + }, +}; + +static const char *const ahb_sun7i_parents[] = { "axi", "pll-periph", + "pll-periph" }; +static const struct ccu_mux_fixed_prediv ahb_sun7i_predivs[] = { + { .index = 1, .div = 2, }, + { /* Sentinel */ }, +}; +static struct ccu_div ahb_sun7i_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = { + .shift = 6, + .width = 2, + .fixed_predivs = ahb_sun7i_predivs, + .n_predivs = ARRAY_SIZE(ahb_sun7i_predivs), + }, + + .common = { + .reg = 0x054, + .hw.init = CLK_HW_INIT_PARENTS("ahb", + ahb_sun7i_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct clk_div_table apb0_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { /* Sentinel */ }, +}; +static SUNXI_CCU_DIV_TABLE(apb0_clk, "apb0", "ahb", + 0x054, 8, 2, apb0_div_table, 0); + +static const char *const apb1_parents[] = { "hosc", "pll-periph", "osc32k" }; +static SUNXI_CCU_MP_WITH_MUX(apb1_clk, "apb1", apb1_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0); + +/* Not present on A20 */ +static SUNXI_CCU_GATE(axi_dram_clk, "axi-dram", "ahb", + 0x05c, BIT(31), 0); + +static SUNXI_CCU_GATE(ahb_otg_clk, "ahb-otg", "ahb", + 0x060, BIT(0), 0); +static SUNXI_CCU_GATE(ahb_ehci0_clk, "ahb-ehci0", "ahb", + 0x060, BIT(1), 0); +static SUNXI_CCU_GATE(ahb_ohci0_clk, "ahb-ohci0", "ahb", + 0x060, BIT(2), 0); +static SUNXI_CCU_GATE(ahb_ehci1_clk, "ahb-ehci1", "ahb", + 0x060, BIT(3), 0); +static SUNXI_CCU_GATE(ahb_ohci1_clk, "ahb-ohci1", "ahb", + 0x060, BIT(4), 0); +static SUNXI_CCU_GATE(ahb_ss_clk, "ahb-ss", "ahb", + 0x060, BIT(5), 0); +static SUNXI_CCU_GATE(ahb_dma_clk, "ahb-dma", "ahb", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(ahb_bist_clk, "ahb-bist", "ahb", + 0x060, BIT(7), 0); +static SUNXI_CCU_GATE(ahb_mmc0_clk, "ahb-mmc0", "ahb", + 0x060, BIT(8), 0); +static SUNXI_CCU_GATE(ahb_mmc1_clk, "ahb-mmc1", "ahb", + 0x060, BIT(9), 0); +static SUNXI_CCU_GATE(ahb_mmc2_clk, "ahb-mmc2", "ahb", + 0x060, BIT(10), 0); +static SUNXI_CCU_GATE(ahb_mmc3_clk, "ahb-mmc3", "ahb", + 0x060, BIT(11), 0); +static SUNXI_CCU_GATE(ahb_ms_clk, "ahb-ms", "ahb", + 0x060, BIT(12), 0); +static SUNXI_CCU_GATE(ahb_nand_clk, "ahb-nand", "ahb", + 0x060, BIT(13), 0); +static SUNXI_CCU_GATE(ahb_sdram_clk, "ahb-sdram", "ahb", + 0x060, BIT(14), CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE(ahb_ace_clk, "ahb-ace", "ahb", + 0x060, BIT(16), 0); +static SUNXI_CCU_GATE(ahb_emac_clk, "ahb-emac", "ahb", + 0x060, BIT(17), 0); +static SUNXI_CCU_GATE(ahb_ts_clk, "ahb-ts", "ahb", + 0x060, BIT(18), 0); +static SUNXI_CCU_GATE(ahb_spi0_clk, "ahb-spi0", "ahb", + 0x060, BIT(20), 0); +static SUNXI_CCU_GATE(ahb_spi1_clk, "ahb-spi1", "ahb", + 0x060, BIT(21), 0); +static SUNXI_CCU_GATE(ahb_spi2_clk, "ahb-spi2", "ahb", + 0x060, BIT(22), 0); +static SUNXI_CCU_GATE(ahb_spi3_clk, "ahb-spi3", "ahb", + 0x060, BIT(23), 0); +static SUNXI_CCU_GATE(ahb_pata_clk, "ahb-pata", "ahb", + 0x060, BIT(24), 0); +/* Not documented on A20 */ +static SUNXI_CCU_GATE(ahb_sata_clk, "ahb-sata", "ahb", + 0x060, BIT(25), 0); +/* Not present on A20 */ +static SUNXI_CCU_GATE(ahb_gps_clk, "ahb-gps", "ahb", + 0x060, BIT(26), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(ahb_hstimer_clk, "ahb-hstimer", "ahb", + 0x060, BIT(28), 0); + +static SUNXI_CCU_GATE(ahb_ve_clk, "ahb-ve", "ahb", + 0x064, BIT(0), 0); +static SUNXI_CCU_GATE(ahb_tvd_clk, "ahb-tvd", "ahb", + 0x064, BIT(1), 0); +static SUNXI_CCU_GATE(ahb_tve0_clk, "ahb-tve0", "ahb", + 0x064, BIT(2), 0); +static SUNXI_CCU_GATE(ahb_tve1_clk, "ahb-tve1", "ahb", + 0x064, BIT(3), 0); +static SUNXI_CCU_GATE(ahb_lcd0_clk, "ahb-lcd0", "ahb", + 0x064, BIT(4), 0); +static SUNXI_CCU_GATE(ahb_lcd1_clk, "ahb-lcd1", "ahb", + 0x064, BIT(5), 0); +static SUNXI_CCU_GATE(ahb_csi0_clk, "ahb-csi0", "ahb", + 0x064, BIT(8), 0); +static SUNXI_CCU_GATE(ahb_csi1_clk, "ahb-csi1", "ahb", + 0x064, BIT(9), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(ahb_hdmi1_clk, "ahb-hdmi1", "ahb", + 0x064, BIT(10), 0); +static SUNXI_CCU_GATE(ahb_hdmi0_clk, "ahb-hdmi0", "ahb", + 0x064, BIT(11), 0); +static SUNXI_CCU_GATE(ahb_de_be0_clk, "ahb-de-be0", "ahb", + 0x064, BIT(12), 0); +static SUNXI_CCU_GATE(ahb_de_be1_clk, "ahb-de-be1", "ahb", + 0x064, BIT(13), 0); +static SUNXI_CCU_GATE(ahb_de_fe0_clk, "ahb-de-fe0", "ahb", + 0x064, BIT(14), 0); +static SUNXI_CCU_GATE(ahb_de_fe1_clk, "ahb-de-fe1", "ahb", + 0x064, BIT(15), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(ahb_gmac_clk, "ahb-gmac", "ahb", + 0x064, BIT(17), 0); +static SUNXI_CCU_GATE(ahb_mp_clk, "ahb-mp", "ahb", + 0x064, BIT(18), 0); +static SUNXI_CCU_GATE(ahb_gpu_clk, "ahb-gpu", "ahb", + 0x064, BIT(20), 0); + +static SUNXI_CCU_GATE(apb0_codec_clk, "apb0-codec", "apb0", + 0x068, BIT(0), 0); +static SUNXI_CCU_GATE(apb0_spdif_clk, "apb0-spdif", "apb0", + 0x068, BIT(1), 0); +static SUNXI_CCU_GATE(apb0_ac97_clk, "apb0-ac97", "apb0", + 0x068, BIT(2), 0); +static SUNXI_CCU_GATE(apb0_i2s0_clk, "apb0-i2s0", "apb0", + 0x068, BIT(3), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(apb0_i2s1_clk, "apb0-i2s1", "apb0", + 0x068, BIT(4), 0); +static SUNXI_CCU_GATE(apb0_pio_clk, "apb0-pio", "apb0", + 0x068, BIT(5), 0); +static SUNXI_CCU_GATE(apb0_ir0_clk, "apb0-ir0", "apb0", + 0x068, BIT(6), 0); +static SUNXI_CCU_GATE(apb0_ir1_clk, "apb0-ir1", "apb0", + 0x068, BIT(7), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(apb0_i2s2_clk, "apb0-i2s2", "apb0", + 0x068, BIT(8), 0); +static SUNXI_CCU_GATE(apb0_keypad_clk, "apb0-keypad", "apb0", + 0x068, BIT(10), 0); + +static SUNXI_CCU_GATE(apb1_i2c0_clk, "apb1-i2c0", "apb1", + 0x06c, BIT(0), 0); +static SUNXI_CCU_GATE(apb1_i2c1_clk, "apb1-i2c1", "apb1", + 0x06c, BIT(1), 0); +static SUNXI_CCU_GATE(apb1_i2c2_clk, "apb1-i2c2", "apb1", + 0x06c, BIT(2), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(apb1_i2c3_clk, "apb1-i2c3", "apb1", + 0x06c, BIT(3), 0); +static SUNXI_CCU_GATE(apb1_can_clk, "apb1-can", "apb1", + 0x06c, BIT(4), 0); +static SUNXI_CCU_GATE(apb1_scr_clk, "apb1-scr", "apb1", + 0x06c, BIT(5), 0); +static SUNXI_CCU_GATE(apb1_ps20_clk, "apb1-ps20", "apb1", + 0x06c, BIT(6), 0); +static SUNXI_CCU_GATE(apb1_ps21_clk, "apb1-ps21", "apb1", + 0x06c, BIT(7), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(apb1_i2c4_clk, "apb1-i2c4", "apb1", + 0x06c, BIT(15), 0); +static SUNXI_CCU_GATE(apb1_uart0_clk, "apb1-uart0", "apb1", + 0x06c, BIT(16), 0); +static SUNXI_CCU_GATE(apb1_uart1_clk, "apb1-uart1", "apb1", + 0x06c, BIT(17), 0); +static SUNXI_CCU_GATE(apb1_uart2_clk, "apb1-uart2", "apb1", + 0x06c, BIT(18), 0); +static SUNXI_CCU_GATE(apb1_uart3_clk, "apb1-uart3", "apb1", + 0x06c, BIT(19), 0); +static SUNXI_CCU_GATE(apb1_uart4_clk, "apb1-uart4", "apb1", + 0x06c, BIT(20), 0); +static SUNXI_CCU_GATE(apb1_uart5_clk, "apb1-uart5", "apb1", + 0x06c, BIT(21), 0); +static SUNXI_CCU_GATE(apb1_uart6_clk, "apb1-uart6", "apb1", + 0x06c, BIT(22), 0); +static SUNXI_CCU_GATE(apb1_uart7_clk, "apb1-uart7", "apb1", + 0x06c, BIT(23), 0); + +static const char *const mod0_default_parents[] = { "hosc", "pll-periph", + "pll-ddr-other" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* Undocumented on A10 */ +static SUNXI_CCU_MP_WITH_MUX_GATE(ms_clk, "ms", mod0_default_parents, 0x084, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* MMC output and sample clocks are not present on A10 */ +static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0", + 0x088, 8, 3, 0); +static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0", + 0x088, 20, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* MMC output and sample clocks are not present on A10 */ +static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1", + 0x08c, 8, 3, 0); +static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1", + 0x08c, 20, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* MMC output and sample clocks are not present on A10 */ +static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2", + 0x090, 8, 3, 0); +static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2", + 0x090, 20, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc3_clk, "mmc3", mod0_default_parents, 0x094, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* MMC output and sample clocks are not present on A10 */ +static SUNXI_CCU_PHASE(mmc3_output_clk, "mmc3_output", "mmc3", + 0x094, 8, 3, 0); +static SUNXI_CCU_PHASE(mmc3_sample_clk, "mmc3_sample", "mmc3", + 0x094, 20, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", mod0_default_parents, 0x098, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ss_clk, "ss", mod0_default_parents, 0x09c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi2_clk, "spi2", mod0_default_parents, 0x0a8, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* Undocumented on A10 */ +static SUNXI_CCU_MP_WITH_MUX_GATE(pata_clk, "pata", mod0_default_parents, 0x0ac, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* TODO: Check whether A10 actually supports osc32k as 4th parent? */ +static const char *const ir_parents_sun4i[] = { "hosc", "pll-periph", + "pll-ddr-other" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ir0_sun4i_clk, "ir0", ir_parents_sun4i, 0x0b0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ir1_sun4i_clk, "ir1", ir_parents_sun4i, 0x0b4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); +static const char *const ir_parents_sun7i[] = { "hosc", "pll-periph", + "pll-ddr-other", "osc32k" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ir0_sun7i_clk, "ir0", ir_parents_sun7i, 0x0b0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ir1_sun7i_clk, "ir1", ir_parents_sun7i, 0x0b4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char *const audio_parents[] = { "pll-audio-8x", "pll-audio-4x", + "pll-audio-2x", "pll-audio" }; +static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", audio_parents, + 0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(ac97_clk, "ac97", audio_parents, + 0x0bc, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +/* Undocumented on A10 */ +static SUNXI_CCU_MUX_WITH_GATE(spdif_clk, "spdif", audio_parents, + 0x0c0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static const char *const keypad_parents[] = { "hosc", "losc"}; +static const u8 keypad_table[] = { 0, 2 }; +static struct ccu_mp keypad_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(0, 5), + .p = _SUNXI_CCU_DIV(16, 2), + .mux = _SUNXI_CCU_MUX_TABLE(24, 2, keypad_table), + .common = { + .reg = 0x0c4, + .hw.init = CLK_HW_INIT_PARENTS("keypad", + keypad_parents, + &ccu_mp_ops, + 0), + }, +}; + +/* + * SATA supports external clock as parent via BIT(24) and is probably an + * optional crystal or oscillator that can be connected to the + * SATA-CLKM / SATA-CLKP pins. + */ +static const char *const sata_parents[] = {"pll-periph-sata", "sata-ext"}; +static SUNXI_CCU_MUX_WITH_GATE(sata_clk, "sata", sata_parents, + 0x0c8, 24, 1, BIT(31), CLK_SET_RATE_PARENT); + + +static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "pll-periph", + 0x0cc, BIT(6), 0); +static SUNXI_CCU_GATE(usb_ohci1_clk, "usb-ohci1", "pll-periph", + 0x0cc, BIT(7), 0); +static SUNXI_CCU_GATE(usb_phy_clk, "usb-phy", "pll-periph", + 0x0cc, BIT(8), 0); + +/* TODO: GPS CLK 0x0d0 */ + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi3_clk, "spi3", mod0_default_parents, 0x0d4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* Not present on A10 */ +static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", audio_parents, + 0x0d8, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +/* Not present on A10 */ +static SUNXI_CCU_MUX_WITH_GATE(i2s2_clk, "i2s2", audio_parents, + 0x0dc, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "pll-ddr", + 0x100, BIT(0), 0); +static SUNXI_CCU_GATE(dram_csi0_clk, "dram-csi0", "pll-ddr", + 0x100, BIT(1), 0); +static SUNXI_CCU_GATE(dram_csi1_clk, "dram-csi1", "pll-ddr", + 0x100, BIT(2), 0); +static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "pll-ddr", + 0x100, BIT(3), 0); +static SUNXI_CCU_GATE(dram_tvd_clk, "dram-tvd", "pll-ddr", + 0x100, BIT(4), 0); +static SUNXI_CCU_GATE(dram_tve0_clk, "dram-tve0", "pll-ddr", + 0x100, BIT(5), 0); +static SUNXI_CCU_GATE(dram_tve1_clk, "dram-tve1", "pll-ddr", + 0x100, BIT(6), 0); + +/* Clock seems to be critical only on sun4i */ +static SUNXI_CCU_GATE(dram_out_clk, "dram-out", "pll-ddr", + 0x100, BIT(15), CLK_IS_CRITICAL); +static SUNXI_CCU_GATE(dram_de_fe1_clk, "dram-de-fe1", "pll-ddr", + 0x100, BIT(24), 0); +static SUNXI_CCU_GATE(dram_de_fe0_clk, "dram-de-fe0", "pll-ddr", + 0x100, BIT(25), 0); +static SUNXI_CCU_GATE(dram_de_be0_clk, "dram-de-be0", "pll-ddr", + 0x100, BIT(26), 0); +static SUNXI_CCU_GATE(dram_de_be1_clk, "dram-de-be1", "pll-ddr", + 0x100, BIT(27), 0); +static SUNXI_CCU_GATE(dram_mp_clk, "dram-mp", "pll-ddr", + 0x100, BIT(28), 0); +static SUNXI_CCU_GATE(dram_ace_clk, "dram-ace", "pll-ddr", + 0x100, BIT(29), 0); + +static const char *const de_parents[] = { "pll-video0", "pll-video1", + "pll-ddr-other" }; +static SUNXI_CCU_M_WITH_MUX_GATE(de_be0_clk, "de-be0", de_parents, + 0x104, 0, 4, 24, 2, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(de_be1_clk, "de-be1", de_parents, + 0x108, 0, 4, 24, 2, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(de_fe0_clk, "de-fe0", de_parents, + 0x10c, 0, 4, 24, 2, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(de_fe1_clk, "de-fe1", de_parents, + 0x110, 0, 4, 24, 2, BIT(31), 0); + +/* Undocumented on A10 */ +static SUNXI_CCU_M_WITH_MUX_GATE(de_mp_clk, "de-mp", de_parents, + 0x114, 0, 4, 24, 2, BIT(31), 0); + +static const char *const disp_parents[] = { "pll-video0", "pll-video1", + "pll-video0-2x", "pll-video1-2x" }; +static SUNXI_CCU_MUX_WITH_GATE(tcon0_ch0_clk, "tcon0-ch0-sclk", disp_parents, + 0x118, 24, 2, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_MUX_WITH_GATE(tcon1_ch0_clk, "tcon1-ch0-sclk", disp_parents, + 0x11c, 24, 2, BIT(31), CLK_SET_RATE_PARENT); + +static const char *const csi_sclk_parents[] = { "pll-video0", "pll-ve", + "pll-ddr-other", "pll-periph" }; + +static SUNXI_CCU_M_WITH_MUX_GATE(csi_sclk_clk, "csi-sclk", + csi_sclk_parents, + 0x120, 0, 4, 24, 2, BIT(31), 0); + +/* TVD clock setup for A10 */ +static const char *const tvd_parents[] = { "pll-video0", "pll-video1" }; +static SUNXI_CCU_MUX_WITH_GATE(tvd_sun4i_clk, "tvd", tvd_parents, + 0x128, 24, 1, BIT(31), 0); + +/* TVD clock setup for A20 */ +static SUNXI_CCU_MP_WITH_MUX_GATE(tvd_sclk2_sun7i_clk, + "tvd-sclk2", tvd_parents, + 0x128, + 0, 4, /* M */ + 16, 4, /* P */ + 8, 1, /* mux */ + BIT(15), /* gate */ + 0); + +static SUNXI_CCU_M_WITH_GATE(tvd_sclk1_sun7i_clk, "tvd-sclk1", "tvd-sclk2", + 0x128, 0, 4, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(tcon0_ch1_sclk2_clk, "tcon0-ch1-sclk2", + disp_parents, + 0x12c, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(tcon0_ch1_clk, + "tcon0-ch1-sclk1", "tcon0-ch1-sclk2", + 0x12c, 11, 1, BIT(15), + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_MUX_GATE(tcon1_ch1_sclk2_clk, "tcon1-ch1-sclk2", + disp_parents, + 0x130, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(tcon1_ch1_clk, + "tcon1-ch1-sclk1", "tcon1-ch1-sclk2", + 0x130, 11, 1, BIT(15), + CLK_SET_RATE_PARENT); + +static const char *const csi_parents[] = { "hosc", "pll-video0", "pll-video1", + "pll-video0-2x", "pll-video1-2x"}; +static const u8 csi_table[] = { 0, 1, 2, 5, 6}; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi0_clk, "csi0", + csi_parents, csi_table, + 0x134, 0, 5, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi1_clk, "csi1", + csi_parents, csi_table, + 0x138, 0, 5, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", 0x13c, 16, 8, BIT(31), 0); + +static SUNXI_CCU_GATE(codec_clk, "codec", "pll-audio", + 0x140, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(avs_clk, "avs", "hosc", 0x144, BIT(31), 0); + +static const char *const ace_parents[] = { "pll-ve", "pll-ddr-other" }; +static SUNXI_CCU_M_WITH_MUX_GATE(ace_clk, "ace", ace_parents, + 0x148, 0, 4, 24, 1, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", disp_parents, + 0x150, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static const char *const gpu_parents_sun4i[] = { "pll-video0", "pll-ve", + "pll-ddr-other", + "pll-video1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(gpu_sun4i_clk, "gpu", gpu_parents_sun4i, + 0x154, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static const char *const gpu_parents_sun7i[] = { "pll-video0", "pll-ve", + "pll-ddr-other", "pll-video1", + "pll-gpu" }; +static const u8 gpu_table_sun7i[] = { 0, 1, 2, 3, 4 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(gpu_sun7i_clk, "gpu", + gpu_parents_sun7i, gpu_table_sun7i, + 0x154, 0, 4, 24, 3, BIT(31), + CLK_SET_RATE_PARENT); + +static const char *const mbus_sun4i_parents[] = { "hosc", "pll-periph", + "pll-ddr-other" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mbus_sun4i_clk, "mbus", mbus_sun4i_parents, + 0x15c, 0, 4, 16, 2, 24, 2, BIT(31), + 0); +static const char *const mbus_sun7i_parents[] = { "hosc", "pll-periph-base", + "pll-ddr-other" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mbus_sun7i_clk, "mbus", mbus_sun7i_parents, + 0x15c, 0, 4, 16, 2, 24, 2, BIT(31), + CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE(hdmi1_slow_clk, "hdmi1-slow", "hosc", 0x178, BIT(31), 0); + +static const char *const hdmi1_parents[] = { "pll-video0", "pll-video1" }; +static const u8 hdmi1_table[] = { 0, 1}; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(hdmi1_clk, "hdmi1", + hdmi1_parents, hdmi1_table, + 0x17c, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static const char *const out_parents[] = { "hosc", "osc32k", "hosc" }; +static const struct ccu_mux_fixed_prediv clk_out_predivs[] = { + { .index = 0, .div = 750, }, +}; + +static struct ccu_mp out_a_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = clk_out_predivs, + .n_predivs = ARRAY_SIZE(clk_out_predivs), + }, + .common = { + .reg = 0x1f0, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("out-a", + out_parents, + &ccu_mp_ops, + 0), + }, +}; +static struct ccu_mp out_b_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = clk_out_predivs, + .n_predivs = ARRAY_SIZE(clk_out_predivs), + }, + .common = { + .reg = 0x1f4, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("out-b", + out_parents, + &ccu_mp_ops, + 0), + }, +}; + +static struct ccu_common *sun4i_sun7i_ccu_clks[] = { + &hosc_clk.common, + &pll_core_clk.common, + &pll_audio_base_clk.common, + &pll_video0_clk.common, + &pll_ve_sun4i_clk.common, + &pll_ve_sun7i_clk.common, + &pll_ddr_base_clk.common, + &pll_ddr_clk.common, + &pll_ddr_other_clk.common, + &pll_periph_base_clk.common, + &pll_periph_sata_clk.common, + &pll_video1_clk.common, + &pll_gpu_clk.common, + &cpu_clk.common, + &axi_clk.common, + &axi_dram_clk.common, + &ahb_sun4i_clk.common, + &ahb_sun7i_clk.common, + &apb0_clk.common, + &apb1_clk.common, + &ahb_otg_clk.common, + &ahb_ehci0_clk.common, + &ahb_ohci0_clk.common, + &ahb_ehci1_clk.common, + &ahb_ohci1_clk.common, + &ahb_ss_clk.common, + &ahb_dma_clk.common, + &ahb_bist_clk.common, + &ahb_mmc0_clk.common, + &ahb_mmc1_clk.common, + &ahb_mmc2_clk.common, + &ahb_mmc3_clk.common, + &ahb_ms_clk.common, + &ahb_nand_clk.common, + &ahb_sdram_clk.common, + &ahb_ace_clk.common, + &ahb_emac_clk.common, + &ahb_ts_clk.common, + &ahb_spi0_clk.common, + &ahb_spi1_clk.common, + &ahb_spi2_clk.common, + &ahb_spi3_clk.common, + &ahb_pata_clk.common, + &ahb_sata_clk.common, + &ahb_gps_clk.common, + &ahb_hstimer_clk.common, + &ahb_ve_clk.common, + &ahb_tvd_clk.common, + &ahb_tve0_clk.common, + &ahb_tve1_clk.common, + &ahb_lcd0_clk.common, + &ahb_lcd1_clk.common, + &ahb_csi0_clk.common, + &ahb_csi1_clk.common, + &ahb_hdmi1_clk.common, + &ahb_hdmi0_clk.common, + &ahb_de_be0_clk.common, + &ahb_de_be1_clk.common, + &ahb_de_fe0_clk.common, + &ahb_de_fe1_clk.common, + &ahb_gmac_clk.common, + &ahb_mp_clk.common, + &ahb_gpu_clk.common, + &apb0_codec_clk.common, + &apb0_spdif_clk.common, + &apb0_ac97_clk.common, + &apb0_i2s0_clk.common, + &apb0_i2s1_clk.common, + &apb0_pio_clk.common, + &apb0_ir0_clk.common, + &apb0_ir1_clk.common, + &apb0_i2s2_clk.common, + &apb0_keypad_clk.common, + &apb1_i2c0_clk.common, + &apb1_i2c1_clk.common, + &apb1_i2c2_clk.common, + &apb1_i2c3_clk.common, + &apb1_can_clk.common, + &apb1_scr_clk.common, + &apb1_ps20_clk.common, + &apb1_ps21_clk.common, + &apb1_i2c4_clk.common, + &apb1_uart0_clk.common, + &apb1_uart1_clk.common, + &apb1_uart2_clk.common, + &apb1_uart3_clk.common, + &apb1_uart4_clk.common, + &apb1_uart5_clk.common, + &apb1_uart6_clk.common, + &apb1_uart7_clk.common, + &nand_clk.common, + &ms_clk.common, + &mmc0_clk.common, + &mmc0_output_clk.common, + &mmc0_sample_clk.common, + &mmc1_clk.common, + &mmc1_output_clk.common, + &mmc1_sample_clk.common, + &mmc2_clk.common, + &mmc2_output_clk.common, + &mmc2_sample_clk.common, + &mmc3_clk.common, + &mmc3_output_clk.common, + &mmc3_sample_clk.common, + &ts_clk.common, + &ss_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &spi2_clk.common, + &pata_clk.common, + &ir0_sun4i_clk.common, + &ir1_sun4i_clk.common, + &ir0_sun7i_clk.common, + &ir1_sun7i_clk.common, + &i2s0_clk.common, + &ac97_clk.common, + &spdif_clk.common, + &keypad_clk.common, + &sata_clk.common, + &usb_ohci0_clk.common, + &usb_ohci1_clk.common, + &usb_phy_clk.common, + &spi3_clk.common, + &i2s1_clk.common, + &i2s2_clk.common, + &dram_ve_clk.common, + &dram_csi0_clk.common, + &dram_csi1_clk.common, + &dram_ts_clk.common, + &dram_tvd_clk.common, + &dram_tve0_clk.common, + &dram_tve1_clk.common, + &dram_out_clk.common, + &dram_de_fe1_clk.common, + &dram_de_fe0_clk.common, + &dram_de_be0_clk.common, + &dram_de_be1_clk.common, + &dram_mp_clk.common, + &dram_ace_clk.common, + &de_be0_clk.common, + &de_be1_clk.common, + &de_fe0_clk.common, + &de_fe1_clk.common, + &de_mp_clk.common, + &tcon0_ch0_clk.common, + &tcon1_ch0_clk.common, + &csi_sclk_clk.common, + &tvd_sun4i_clk.common, + &tvd_sclk1_sun7i_clk.common, + &tvd_sclk2_sun7i_clk.common, + &tcon0_ch1_sclk2_clk.common, + &tcon0_ch1_clk.common, + &tcon1_ch1_sclk2_clk.common, + &tcon1_ch1_clk.common, + &csi0_clk.common, + &csi1_clk.common, + &ve_clk.common, + &codec_clk.common, + &avs_clk.common, + &ace_clk.common, + &hdmi_clk.common, + &gpu_sun4i_clk.common, + &gpu_sun7i_clk.common, + &mbus_sun4i_clk.common, + &mbus_sun7i_clk.common, + &hdmi1_slow_clk.common, + &hdmi1_clk.common, + &out_a_clk.common, + &out_b_clk.common +}; + +/* Post-divider for pll-audio is hardcoded to 4 */ +static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", + "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", + "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", + "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x", + "pll-video0", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_video1_2x_clk, "pll-video1-2x", + "pll-video1", 1, 2, CLK_SET_RATE_PARENT); + + +static struct clk_hw_onecell_data sun4i_a10_hw_clks = { + .hws = { + [CLK_HOSC] = &hosc_clk.common.hw, + [CLK_PLL_CORE] = &pll_core_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_sun4i_clk.common.hw, + [CLK_PLL_DDR_BASE] = &pll_ddr_base_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_DDR_OTHER] = &pll_ddr_other_clk.common.hw, + [CLK_PLL_PERIPH_BASE] = &pll_periph_base_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.hw, + [CLK_PLL_PERIPH_SATA] = &pll_periph_sata_clk.common.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AXI_DRAM] = &axi_dram_clk.common.hw, + [CLK_AHB] = &ahb_sun4i_clk.common.hw, + [CLK_APB0] = &apb0_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_AHB_OTG] = &ahb_otg_clk.common.hw, + [CLK_AHB_EHCI0] = &ahb_ehci0_clk.common.hw, + [CLK_AHB_OHCI0] = &ahb_ohci0_clk.common.hw, + [CLK_AHB_EHCI1] = &ahb_ehci1_clk.common.hw, + [CLK_AHB_OHCI1] = &ahb_ohci1_clk.common.hw, + [CLK_AHB_SS] = &ahb_ss_clk.common.hw, + [CLK_AHB_DMA] = &ahb_dma_clk.common.hw, + [CLK_AHB_BIST] = &ahb_bist_clk.common.hw, + [CLK_AHB_MMC0] = &ahb_mmc0_clk.common.hw, + [CLK_AHB_MMC1] = &ahb_mmc1_clk.common.hw, + [CLK_AHB_MMC2] = &ahb_mmc2_clk.common.hw, + [CLK_AHB_MMC3] = &ahb_mmc3_clk.common.hw, + [CLK_AHB_MS] = &ahb_ms_clk.common.hw, + [CLK_AHB_NAND] = &ahb_nand_clk.common.hw, + [CLK_AHB_SDRAM] = &ahb_sdram_clk.common.hw, + [CLK_AHB_ACE] = &ahb_ace_clk.common.hw, + [CLK_AHB_EMAC] = &ahb_emac_clk.common.hw, + [CLK_AHB_TS] = &ahb_ts_clk.common.hw, + [CLK_AHB_SPI0] = &ahb_spi0_clk.common.hw, + [CLK_AHB_SPI1] = &ahb_spi1_clk.common.hw, + [CLK_AHB_SPI2] = &ahb_spi2_clk.common.hw, + [CLK_AHB_SPI3] = &ahb_spi3_clk.common.hw, + [CLK_AHB_PATA] = &ahb_pata_clk.common.hw, + [CLK_AHB_SATA] = &ahb_sata_clk.common.hw, + [CLK_AHB_GPS] = &ahb_gps_clk.common.hw, + [CLK_AHB_VE] = &ahb_ve_clk.common.hw, + [CLK_AHB_TVD] = &ahb_tvd_clk.common.hw, + [CLK_AHB_TVE0] = &ahb_tve0_clk.common.hw, + [CLK_AHB_TVE1] = &ahb_tve1_clk.common.hw, + [CLK_AHB_LCD0] = &ahb_lcd0_clk.common.hw, + [CLK_AHB_LCD1] = &ahb_lcd1_clk.common.hw, + [CLK_AHB_CSI0] = &ahb_csi0_clk.common.hw, + [CLK_AHB_CSI1] = &ahb_csi1_clk.common.hw, + [CLK_AHB_HDMI0] = &ahb_hdmi0_clk.common.hw, + [CLK_AHB_DE_BE0] = &ahb_de_be0_clk.common.hw, + [CLK_AHB_DE_BE1] = &ahb_de_be1_clk.common.hw, + [CLK_AHB_DE_FE0] = &ahb_de_fe0_clk.common.hw, + [CLK_AHB_DE_FE1] = &ahb_de_fe1_clk.common.hw, + [CLK_AHB_MP] = &ahb_mp_clk.common.hw, + [CLK_AHB_GPU] = &ahb_gpu_clk.common.hw, + [CLK_APB0_CODEC] = &apb0_codec_clk.common.hw, + [CLK_APB0_SPDIF] = &apb0_spdif_clk.common.hw, + [CLK_APB0_AC97] = &apb0_ac97_clk.common.hw, + [CLK_APB0_I2S0] = &apb0_i2s0_clk.common.hw, + [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, + [CLK_APB0_IR0] = &apb0_ir0_clk.common.hw, + [CLK_APB0_IR1] = &apb0_ir1_clk.common.hw, + [CLK_APB0_KEYPAD] = &apb0_keypad_clk.common.hw, + [CLK_APB1_I2C0] = &apb1_i2c0_clk.common.hw, + [CLK_APB1_I2C1] = &apb1_i2c1_clk.common.hw, + [CLK_APB1_I2C2] = &apb1_i2c2_clk.common.hw, + [CLK_APB1_CAN] = &apb1_can_clk.common.hw, + [CLK_APB1_SCR] = &apb1_scr_clk.common.hw, + [CLK_APB1_PS20] = &apb1_ps20_clk.common.hw, + [CLK_APB1_PS21] = &apb1_ps21_clk.common.hw, + [CLK_APB1_UART0] = &apb1_uart0_clk.common.hw, + [CLK_APB1_UART1] = &apb1_uart1_clk.common.hw, + [CLK_APB1_UART2] = &apb1_uart2_clk.common.hw, + [CLK_APB1_UART3] = &apb1_uart3_clk.common.hw, + [CLK_APB1_UART4] = &apb1_uart4_clk.common.hw, + [CLK_APB1_UART5] = &apb1_uart5_clk.common.hw, + [CLK_APB1_UART6] = &apb1_uart6_clk.common.hw, + [CLK_APB1_UART7] = &apb1_uart7_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MS] = &ms_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC3] = &mmc3_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_SS] = &ss_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_PATA] = &pata_clk.common.hw, + [CLK_IR0] = &ir0_sun4i_clk.common.hw, + [CLK_IR1] = &ir1_sun4i_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_AC97] = &ac97_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_KEYPAD] = &keypad_clk.common.hw, + [CLK_SATA] = &sata_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw, + [CLK_USB_PHY] = &usb_phy_clk.common.hw, + /* CLK_GPS is unimplemented */ + [CLK_SPI3] = &spi3_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI0] = &dram_csi0_clk.common.hw, + [CLK_DRAM_CSI1] = &dram_csi1_clk.common.hw, + [CLK_DRAM_TS] = &dram_ts_clk.common.hw, + [CLK_DRAM_TVD] = &dram_tvd_clk.common.hw, + [CLK_DRAM_TVE0] = &dram_tve0_clk.common.hw, + [CLK_DRAM_TVE1] = &dram_tve1_clk.common.hw, + [CLK_DRAM_OUT] = &dram_out_clk.common.hw, + [CLK_DRAM_DE_FE1] = &dram_de_fe1_clk.common.hw, + [CLK_DRAM_DE_FE0] = &dram_de_fe0_clk.common.hw, + [CLK_DRAM_DE_BE0] = &dram_de_be0_clk.common.hw, + [CLK_DRAM_DE_BE1] = &dram_de_be1_clk.common.hw, + [CLK_DRAM_MP] = &dram_mp_clk.common.hw, + [CLK_DRAM_ACE] = &dram_ace_clk.common.hw, + [CLK_DE_BE0] = &de_be0_clk.common.hw, + [CLK_DE_BE1] = &de_be1_clk.common.hw, + [CLK_DE_FE0] = &de_fe0_clk.common.hw, + [CLK_DE_FE1] = &de_fe1_clk.common.hw, + [CLK_DE_MP] = &de_mp_clk.common.hw, + [CLK_TCON0_CH0] = &tcon0_ch0_clk.common.hw, + [CLK_TCON1_CH0] = &tcon1_ch0_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, + [CLK_TVD] = &tvd_sun4i_clk.common.hw, + [CLK_TCON0_CH1_SCLK2] = &tcon0_ch1_sclk2_clk.common.hw, + [CLK_TCON0_CH1] = &tcon0_ch1_clk.common.hw, + [CLK_TCON1_CH1_SCLK2] = &tcon1_ch1_sclk2_clk.common.hw, + [CLK_TCON1_CH1] = &tcon1_ch1_clk.common.hw, + [CLK_CSI0] = &csi0_clk.common.hw, + [CLK_CSI1] = &csi1_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_CODEC] = &codec_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_ACE] = &ace_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_GPU] = &gpu_sun7i_clk.common.hw, + [CLK_MBUS] = &mbus_sun4i_clk.common.hw, + }, + .num = CLK_NUMBER_SUN4I, +}; +static struct clk_hw_onecell_data sun7i_a20_hw_clks = { + .hws = { + [CLK_HOSC] = &hosc_clk.common.hw, + [CLK_PLL_CORE] = &pll_core_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_sun7i_clk.common.hw, + [CLK_PLL_DDR_BASE] = &pll_ddr_base_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_DDR_OTHER] = &pll_ddr_other_clk.common.hw, + [CLK_PLL_PERIPH_BASE] = &pll_periph_base_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.hw, + [CLK_PLL_PERIPH_SATA] = &pll_periph_sata_clk.common.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB] = &ahb_sun7i_clk.common.hw, + [CLK_APB0] = &apb0_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_AHB_OTG] = &ahb_otg_clk.common.hw, + [CLK_AHB_EHCI0] = &ahb_ehci0_clk.common.hw, + [CLK_AHB_OHCI0] = &ahb_ohci0_clk.common.hw, + [CLK_AHB_EHCI1] = &ahb_ehci1_clk.common.hw, + [CLK_AHB_OHCI1] = &ahb_ohci1_clk.common.hw, + [CLK_AHB_SS] = &ahb_ss_clk.common.hw, + [CLK_AHB_DMA] = &ahb_dma_clk.common.hw, + [CLK_AHB_BIST] = &ahb_bist_clk.common.hw, + [CLK_AHB_MMC0] = &ahb_mmc0_clk.common.hw, + [CLK_AHB_MMC1] = &ahb_mmc1_clk.common.hw, + [CLK_AHB_MMC2] = &ahb_mmc2_clk.common.hw, + [CLK_AHB_MMC3] = &ahb_mmc3_clk.common.hw, + [CLK_AHB_MS] = &ahb_ms_clk.common.hw, + [CLK_AHB_NAND] = &ahb_nand_clk.common.hw, + [CLK_AHB_SDRAM] = &ahb_sdram_clk.common.hw, + [CLK_AHB_ACE] = &ahb_ace_clk.common.hw, + [CLK_AHB_EMAC] = &ahb_emac_clk.common.hw, + [CLK_AHB_TS] = &ahb_ts_clk.common.hw, + [CLK_AHB_SPI0] = &ahb_spi0_clk.common.hw, + [CLK_AHB_SPI1] = &ahb_spi1_clk.common.hw, + [CLK_AHB_SPI2] = &ahb_spi2_clk.common.hw, + [CLK_AHB_SPI3] = &ahb_spi3_clk.common.hw, + [CLK_AHB_PATA] = &ahb_pata_clk.common.hw, + [CLK_AHB_SATA] = &ahb_sata_clk.common.hw, + [CLK_AHB_HSTIMER] = &ahb_hstimer_clk.common.hw, + [CLK_AHB_VE] = &ahb_ve_clk.common.hw, + [CLK_AHB_TVD] = &ahb_tvd_clk.common.hw, + [CLK_AHB_TVE0] = &ahb_tve0_clk.common.hw, + [CLK_AHB_TVE1] = &ahb_tve1_clk.common.hw, + [CLK_AHB_LCD0] = &ahb_lcd0_clk.common.hw, + [CLK_AHB_LCD1] = &ahb_lcd1_clk.common.hw, + [CLK_AHB_CSI0] = &ahb_csi0_clk.common.hw, + [CLK_AHB_CSI1] = &ahb_csi1_clk.common.hw, + [CLK_AHB_HDMI1] = &ahb_hdmi1_clk.common.hw, + [CLK_AHB_HDMI0] = &ahb_hdmi0_clk.common.hw, + [CLK_AHB_DE_BE0] = &ahb_de_be0_clk.common.hw, + [CLK_AHB_DE_BE1] = &ahb_de_be1_clk.common.hw, + [CLK_AHB_DE_FE0] = &ahb_de_fe0_clk.common.hw, + [CLK_AHB_DE_FE1] = &ahb_de_fe1_clk.common.hw, + [CLK_AHB_GMAC] = &ahb_gmac_clk.common.hw, + [CLK_AHB_MP] = &ahb_mp_clk.common.hw, + [CLK_AHB_GPU] = &ahb_gpu_clk.common.hw, + [CLK_APB0_CODEC] = &apb0_codec_clk.common.hw, + [CLK_APB0_SPDIF] = &apb0_spdif_clk.common.hw, + [CLK_APB0_AC97] = &apb0_ac97_clk.common.hw, + [CLK_APB0_I2S0] = &apb0_i2s0_clk.common.hw, + [CLK_APB0_I2S1] = &apb0_i2s1_clk.common.hw, + [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, + [CLK_APB0_IR0] = &apb0_ir0_clk.common.hw, + [CLK_APB0_IR1] = &apb0_ir1_clk.common.hw, + [CLK_APB0_I2S2] = &apb0_i2s2_clk.common.hw, + [CLK_APB0_KEYPAD] = &apb0_keypad_clk.common.hw, + [CLK_APB1_I2C0] = &apb1_i2c0_clk.common.hw, + [CLK_APB1_I2C1] = &apb1_i2c1_clk.common.hw, + [CLK_APB1_I2C2] = &apb1_i2c2_clk.common.hw, + [CLK_APB1_I2C3] = &apb1_i2c3_clk.common.hw, + [CLK_APB1_CAN] = &apb1_can_clk.common.hw, + [CLK_APB1_SCR] = &apb1_scr_clk.common.hw, + [CLK_APB1_PS20] = &apb1_ps20_clk.common.hw, + [CLK_APB1_PS21] = &apb1_ps21_clk.common.hw, + [CLK_APB1_I2C4] = &apb1_i2c4_clk.common.hw, + [CLK_APB1_UART0] = &apb1_uart0_clk.common.hw, + [CLK_APB1_UART1] = &apb1_uart1_clk.common.hw, + [CLK_APB1_UART2] = &apb1_uart2_clk.common.hw, + [CLK_APB1_UART3] = &apb1_uart3_clk.common.hw, + [CLK_APB1_UART4] = &apb1_uart4_clk.common.hw, + [CLK_APB1_UART5] = &apb1_uart5_clk.common.hw, + [CLK_APB1_UART6] = &apb1_uart6_clk.common.hw, + [CLK_APB1_UART7] = &apb1_uart7_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MS] = &ms_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw, + [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw, + [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw, + [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw, + [CLK_MMC3] = &mmc3_clk.common.hw, + [CLK_MMC3_OUTPUT] = &mmc3_output_clk.common.hw, + [CLK_MMC3_SAMPLE] = &mmc3_sample_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_SS] = &ss_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_PATA] = &pata_clk.common.hw, + [CLK_IR0] = &ir0_sun7i_clk.common.hw, + [CLK_IR1] = &ir1_sun7i_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_AC97] = &ac97_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_KEYPAD] = &keypad_clk.common.hw, + [CLK_SATA] = &sata_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw, + [CLK_USB_PHY] = &usb_phy_clk.common.hw, + /* CLK_GPS is unimplemented */ + [CLK_SPI3] = &spi3_clk.common.hw, + [CLK_I2S1] = &i2s1_clk.common.hw, + [CLK_I2S2] = &i2s2_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI0] = &dram_csi0_clk.common.hw, + [CLK_DRAM_CSI1] = &dram_csi1_clk.common.hw, + [CLK_DRAM_TS] = &dram_ts_clk.common.hw, + [CLK_DRAM_TVD] = &dram_tvd_clk.common.hw, + [CLK_DRAM_TVE0] = &dram_tve0_clk.common.hw, + [CLK_DRAM_TVE1] = &dram_tve1_clk.common.hw, + [CLK_DRAM_OUT] = &dram_out_clk.common.hw, + [CLK_DRAM_DE_FE1] = &dram_de_fe1_clk.common.hw, + [CLK_DRAM_DE_FE0] = &dram_de_fe0_clk.common.hw, + [CLK_DRAM_DE_BE0] = &dram_de_be0_clk.common.hw, + [CLK_DRAM_DE_BE1] = &dram_de_be1_clk.common.hw, + [CLK_DRAM_MP] = &dram_mp_clk.common.hw, + [CLK_DRAM_ACE] = &dram_ace_clk.common.hw, + [CLK_DE_BE0] = &de_be0_clk.common.hw, + [CLK_DE_BE1] = &de_be1_clk.common.hw, + [CLK_DE_FE0] = &de_fe0_clk.common.hw, + [CLK_DE_FE1] = &de_fe1_clk.common.hw, + [CLK_DE_MP] = &de_mp_clk.common.hw, + [CLK_TCON0_CH0] = &tcon0_ch0_clk.common.hw, + [CLK_TCON1_CH0] = &tcon1_ch0_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, + [CLK_TVD_SCLK2] = &tvd_sclk2_sun7i_clk.common.hw, + [CLK_TVD] = &tvd_sclk1_sun7i_clk.common.hw, + [CLK_TCON0_CH1_SCLK2] = &tcon0_ch1_sclk2_clk.common.hw, + [CLK_TCON0_CH1] = &tcon0_ch1_clk.common.hw, + [CLK_TCON1_CH1_SCLK2] = &tcon1_ch1_sclk2_clk.common.hw, + [CLK_TCON1_CH1] = &tcon1_ch1_clk.common.hw, + [CLK_CSI0] = &csi0_clk.common.hw, + [CLK_CSI1] = &csi1_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_CODEC] = &codec_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_ACE] = &ace_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_GPU] = &gpu_sun7i_clk.common.hw, + [CLK_MBUS] = &mbus_sun7i_clk.common.hw, + [CLK_HDMI1_SLOW] = &hdmi1_slow_clk.common.hw, + [CLK_HDMI1] = &hdmi1_clk.common.hw, + [CLK_OUT_A] = &out_a_clk.common.hw, + [CLK_OUT_B] = &out_b_clk.common.hw, + }, + .num = CLK_NUMBER_SUN7I, +}; + +static struct ccu_reset_map sunxi_a10_a20_ccu_resets[] = { + [RST_USB_PHY0] = { 0x0cc, BIT(0) }, + [RST_USB_PHY1] = { 0x0cc, BIT(1) }, + [RST_USB_PHY2] = { 0x0cc, BIT(2) }, + [RST_GPS] = { 0x0d0, BIT(0) }, + [RST_DE_BE0] = { 0x104, BIT(30) }, + [RST_DE_BE1] = { 0x108, BIT(30) }, + [RST_DE_FE0] = { 0x10c, BIT(30) }, + [RST_DE_FE1] = { 0x110, BIT(30) }, + [RST_DE_MP] = { 0x114, BIT(30) }, + [RST_TVE0] = { 0x118, BIT(29) }, + [RST_TCON0] = { 0x118, BIT(30) }, + [RST_TVE1] = { 0x11c, BIT(29) }, + [RST_TCON1] = { 0x11c, BIT(30) }, + [RST_CSI0] = { 0x134, BIT(30) }, + [RST_CSI1] = { 0x138, BIT(30) }, + [RST_VE] = { 0x13c, BIT(0) }, + [RST_ACE] = { 0x148, BIT(16) }, + [RST_LVDS] = { 0x14c, BIT(0) }, + [RST_GPU] = { 0x154, BIT(30) }, + [RST_HDMI_H] = { 0x170, BIT(0) }, + [RST_HDMI_SYS] = { 0x170, BIT(1) }, + [RST_HDMI_AUDIO_DMA] = { 0x170, BIT(2) }, +}; + +static const struct sunxi_ccu_desc sun4i_a10_ccu_desc = { + .ccu_clks = sun4i_sun7i_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun4i_sun7i_ccu_clks), + + .hw_clks = &sun4i_a10_hw_clks, + + .resets = sunxi_a10_a20_ccu_resets, + .num_resets = ARRAY_SIZE(sunxi_a10_a20_ccu_resets), +}; + +static const struct sunxi_ccu_desc sun7i_a20_ccu_desc = { + .ccu_clks = sun4i_sun7i_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun4i_sun7i_ccu_clks), + + .hw_clks = &sun7i_a20_hw_clks, + + .resets = sunxi_a10_a20_ccu_resets, + .num_resets = ARRAY_SIZE(sunxi_a10_a20_ccu_resets), +}; + +static void __init sun4i_ccu_init(struct device_node *node, + const struct sunxi_ccu_desc *desc) +{ + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", + of_node_full_name(node)); + return; + } + + /* Force the PLL-Audio-1x divider to 4 */ + val = readl(reg + SUN4I_PLL_AUDIO_REG); + val &= ~GENMASK(29, 26); + writel(val | (4 << 26), reg + SUN4I_PLL_AUDIO_REG); + + /* + * Use the peripheral PLL6 as the AHB parent, instead of CPU / + * AXI which have rate changes due to cpufreq. + * + * This is especially a big deal for the HS timer whose parent + * clock is AHB. + * + * NB! These bits are undocumented in A10 manual. + */ + val = readl(reg + SUN4I_AHB_REG); + val &= ~GENMASK(7, 6); + writel(val | (2 << 6), reg + SUN4I_AHB_REG); + + sunxi_ccu_probe(node, reg, desc); +} + +static void __init sun4i_a10_ccu_setup(struct device_node *node) +{ + sun4i_ccu_init(node, &sun4i_a10_ccu_desc); +} +CLK_OF_DECLARE(sun4i_a10_ccu, "allwinner,sun4i-a10-ccu", + sun4i_a10_ccu_setup); + +static void __init sun7i_a20_ccu_setup(struct device_node *node) +{ + sun4i_ccu_init(node, &sun7i_a20_ccu_desc); +} +CLK_OF_DECLARE(sun7i_a20_ccu, "allwinner,sun7i-a20-ccu", + sun7i_a20_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun4i-a10.h b/drivers/clk/sunxi-ng/ccu-sun4i-a10.h new file mode 100644 index 000000000000..c5947c7c050e --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun4i-a10.h @@ -0,0 +1,61 @@ +/* + * Copyright 2017 Priit Laes + * + * Priit Laes <plaes@plaes.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CCU_SUN4I_A10_H_ +#define _CCU_SUN4I_A10_H_ + +#include <dt-bindings/clock/sun4i-a10-ccu.h> +#include <dt-bindings/clock/sun7i-a20-ccu.h> +#include <dt-bindings/reset/sun4i-a10-ccu.h> + +/* The HOSC is exported */ +#define CLK_PLL_CORE 2 +#define CLK_PLL_AUDIO_BASE 3 +#define CLK_PLL_AUDIO 4 +#define CLK_PLL_AUDIO_2X 5 +#define CLK_PLL_AUDIO_4X 6 +#define CLK_PLL_AUDIO_8X 7 +#define CLK_PLL_VIDEO0 8 +#define CLK_PLL_VIDEO0_2X 9 +#define CLK_PLL_VE 10 +#define CLK_PLL_DDR_BASE 11 +#define CLK_PLL_DDR 12 +#define CLK_PLL_DDR_OTHER 13 +#define CLK_PLL_PERIPH_BASE 14 +#define CLK_PLL_PERIPH 15 +#define CLK_PLL_PERIPH_SATA 16 +#define CLK_PLL_VIDEO1 17 +#define CLK_PLL_VIDEO1_2X 18 +#define CLK_PLL_GPU 19 + +/* The CPU clock is exported */ +#define CLK_AXI 21 +#define CLK_AXI_DRAM 22 +#define CLK_AHB 23 +#define CLK_APB0 24 +#define CLK_APB1 25 + +/* AHB gates are exported (23..68) */ +/* APB0 gates are exported (69..78) */ +/* APB1 gates are exported (79..95) */ +/* IP module clocks are exported (96..128) */ +/* DRAM gates are exported (129..142)*/ +/* Media (display engine clocks & etc) are exported (143..169) */ + +#define CLK_NUMBER_SUN4I (CLK_MBUS + 1) +#define CLK_NUMBER_SUN7I (CLK_OUT_B + 1) + +#endif /* _CCU_SUN4I_A10_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.c b/drivers/clk/sunxi-ng/ccu-sun5i.c index 31d7ffda9aab..ab9e850b3707 100644 --- a/drivers/clk/sunxi-ng/ccu-sun5i.c +++ b/drivers/clk/sunxi-ng/ccu-sun5i.c @@ -976,8 +976,7 @@ static void __init sun5i_ccu_init(struct device_node *node, reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c index 4d6078fca9ac..8af434815fba 100644 --- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c +++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c @@ -1217,8 +1217,7 @@ static void __init sun6i_a31_ccu_setup(struct device_node *node) reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c index 8a753ed0426d..d93b452f0df9 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c @@ -716,8 +716,7 @@ static void __init sun8i_a23_ccu_setup(struct device_node *node) reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c index 10b38dc46f75..13eb5b23c5e7 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c @@ -777,8 +777,7 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node) reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c index 62e4f0d2b2fc..1729ff6a5aae 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c @@ -135,7 +135,7 @@ static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de", static const char * const cpux_parents[] = { "osc32k", "osc24M", "pll-cpux" , "pll-cpux" }; static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents, - 0x050, 16, 2, CLK_IS_CRITICAL); + 0x050, 16, 2, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT); static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); @@ -1103,6 +1103,13 @@ static const struct sunxi_ccu_desc sun50i_h5_ccu_desc = { .num_resets = ARRAY_SIZE(sun50i_h5_ccu_resets), }; +static struct ccu_pll_nb sun8i_h3_pll_cpu_nb = { + .common = &pll_cpux_clk.common, + /* copy from pll_cpux_clk */ + .enable = BIT(31), + .lock = BIT(28), +}; + static struct ccu_mux_nb sun8i_h3_cpu_nb = { .common = &cpux_clk.common, .cm = &cpux_clk.mux, @@ -1118,8 +1125,7 @@ static void __init sunxi_h3_h5_ccu_init(struct device_node *node, reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } @@ -1130,6 +1136,10 @@ static void __init sunxi_h3_h5_ccu_init(struct device_node *node, sunxi_ccu_probe(node, reg, desc); + /* Gate then ungate PLL CPU after any rate changes */ + ccu_pll_notifier_register(&sun8i_h3_pll_cpu_nb); + + /* Reparent CPU during PLL CPU rate changes */ ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, &sun8i_h3_cpu_nb); } diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r.c b/drivers/clk/sunxi-ng/ccu-sun8i-r.c index e54816ec1dbe..71feb7b24e8a 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r.c @@ -290,8 +290,7 @@ static void __init sunxi_r_ccu_init(struct device_node *node, reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r.h b/drivers/clk/sunxi-ng/ccu-sun8i-r.h index a7a407f12b56..fb01bffb929d 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-r.h +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r.h @@ -13,7 +13,7 @@ */ #ifndef _CCU_SUN8I_R_H -#define _CCU_SUN8I_R_H_ +#define _CCU_SUN8I_R_H #include <dt-bindings/clock/sun8i-r-ccu.h> #include <dt-bindings/reset/sun8i-r-ccu.h> diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c new file mode 100644 index 000000000000..933f2e68f42a --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c @@ -0,0 +1,1290 @@ +/* + * Copyright (c) 2017 Icenowy Zheng <icenowy@aosc.io> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of_address.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun8i-r40.h" + +/* TODO: The result of N*K is required to be in [10, 88] range. */ +static struct ccu_nkmp pll_cpu_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV_MAX(16, 2, 4), + .common = { + .reg = 0x000, + .hw.init = CLK_HW_INIT("pll-cpu", + "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +/* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from + * the base (2x, 4x and 8x), and one variable divider (the one true + * pll audio). + * + * We don't have any need for the variable divider for now, so we just + * hardcode it to match with the clock names + */ +#define SUN8I_R40_PLL_AUDIO_REG 0x008 + +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", + "osc24M", 0x008, + 8, 7, /* N */ + 0, 5, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: The result of N/M is required to be in [8, 25] range. */ +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video0_clk, "pll-video0", + "osc24M", 0x0010, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: The result of N/M is required to be in [8, 25] range. */ +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", + "osc24M", 0x0018, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: The result of N*K is required to be in [10, 77] range. */ +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr0_clk, "pll-ddr0", + "osc24M", 0x020, + 8, 5, /* N */ + 4, 2, /* K */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: The result of N*K is required to be in [21, 58] range. */ +static struct ccu_nk pll_periph0_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .fixed_post_div = 2, + .common = { + .reg = 0x028, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph0", "osc24M", + &ccu_nk_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_div pll_periph0_sata_clk = { + .enable = BIT(24), + .div = _SUNXI_CCU_DIV(0, 2), + /* + * The formula of pll-periph0 (1x) is 24MHz*N*K/2, and the formula + * of pll-periph0-sata is 24MHz*N*K/M/6, so the postdiv here is + * 6/2 = 3. + */ + .fixed_post_div = 3, + .common = { + .reg = 0x028, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph0-sata", + "pll-periph0", + &ccu_div_ops, 0), + }, +}; + +/* TODO: The result of N*K is required to be in [21, 58] range. */ +static struct ccu_nk pll_periph1_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .fixed_post_div = 2, + .common = { + .reg = 0x02c, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph1", "osc24M", + &ccu_nk_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +/* TODO: The result of N/M is required to be in [8, 25] range. */ +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video1_clk, "pll-video1", + "osc24M", 0x030, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static struct ccu_nkm pll_sata_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .fixed_post_div = 6, + .common = { + .reg = 0x034, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-sata", "osc24M", + &ccu_nkm_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static const char * const pll_sata_out_parents[] = { "pll-sata", + "pll-periph0-sata" }; +static SUNXI_CCU_MUX_WITH_GATE(pll_sata_out_clk, "pll-sata-out", + pll_sata_out_parents, 0x034, + 30, 1, /* mux */ + BIT(14), /* gate */ + CLK_SET_RATE_PARENT); + +/* TODO: The result of N/M is required to be in [8, 25] range. */ +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu", + "osc24M", 0x038, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* + * The MIPI PLL has 2 modes: "MIPI" and "HDMI". + * + * The MIPI mode is a standard NKM-style clock. The HDMI mode is an + * integer / fractional clock with switchable multipliers and dividers. + * This is not supported here. We hardcode the PLL to MIPI mode. + * + * TODO: In the MIPI mode, M/N is required to be equal or lesser than 3, + * which cannot be implemented now. + */ +#define SUN8I_R40_PLL_MIPI_REG 0x040 + +static const char * const pll_mipi_parents[] = { "pll-video0" }; +static struct ccu_nkm pll_mipi_clk = { + .enable = BIT(31) | BIT(23) | BIT(22), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 4), + .k = _SUNXI_CCU_MULT_MIN(4, 2, 2), + .m = _SUNXI_CCU_DIV(0, 4), + .mux = _SUNXI_CCU_MUX(21, 1), + .common = { + .reg = 0x040, + .hw.init = CLK_HW_INIT_PARENTS("pll-mipi", + pll_mipi_parents, + &ccu_nkm_ops, + CLK_SET_RATE_UNGATE) + }, +}; + +/* TODO: The result of N/M is required to be in [8, 25] range. */ +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de", + "osc24M", 0x048, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: The N factor is required to be in [16, 75] range. */ +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1", + "osc24M", 0x04c, + 8, 7, /* N */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static const char * const cpu_parents[] = { "osc32k", "osc24M", + "pll-cpu", "pll-cpu" }; +static SUNXI_CCU_MUX(cpu_clk, "cpu", cpu_parents, + 0x050, 16, 2, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M(axi_clk, "axi", "cpu", 0x050, 0, 2, 0); + +static const char * const ahb1_parents[] = { "osc32k", "osc24M", + "axi", "pll-periph0" }; +static const struct ccu_mux_var_prediv ahb1_predivs[] = { + { .index = 3, .shift = 6, .width = 2 }, +}; +static struct ccu_div ahb1_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + + .mux = { + .shift = 12, + .width = 2, + + .var_predivs = ahb1_predivs, + .n_var_predivs = ARRAY_SIZE(ahb1_predivs), + }, + + .common = { + .reg = 0x054, + .features = CCU_FEATURE_VARIABLE_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ahb1", + ahb1_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct clk_div_table apb1_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { /* Sentinel */ }, +}; +static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1", + 0x054, 8, 2, apb1_div_table, 0); + +static const char * const apb2_parents[] = { "osc32k", "osc24M", + "pll-periph0-2x", + "pll-periph0-2x" }; +static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0); + +static SUNXI_CCU_GATE(bus_mipi_dsi_clk, "bus-mipi-dsi", "ahb1", + 0x060, BIT(1), 0); +static SUNXI_CCU_GATE(bus_ce_clk, "bus-ce", "ahb1", + 0x060, BIT(5), 0); +static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb1", + 0x060, BIT(8), 0); +static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb1", + 0x060, BIT(9), 0); +static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb1", + 0x060, BIT(10), 0); +static SUNXI_CCU_GATE(bus_mmc3_clk, "bus-mmc3", "ahb1", + 0x060, BIT(11), 0); +static SUNXI_CCU_GATE(bus_nand_clk, "bus-nand", "ahb1", + 0x060, BIT(13), 0); +static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb1", + 0x060, BIT(14), 0); +static SUNXI_CCU_GATE(bus_emac_clk, "bus-emac", "ahb1", + 0x060, BIT(17), 0); +static SUNXI_CCU_GATE(bus_ts_clk, "bus-ts", "ahb1", + 0x060, BIT(18), 0); +static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1", + 0x060, BIT(19), 0); +static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb1", + 0x060, BIT(20), 0); +static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb1", + 0x060, BIT(21), 0); +static SUNXI_CCU_GATE(bus_spi2_clk, "bus-spi2", "ahb1", + 0x060, BIT(22), 0); +static SUNXI_CCU_GATE(bus_spi3_clk, "bus-spi3", "ahb1", + 0x060, BIT(23), 0); +static SUNXI_CCU_GATE(bus_sata_clk, "bus-sata", "ahb1", + 0x060, BIT(24), 0); +static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1", + 0x060, BIT(25), 0); +static SUNXI_CCU_GATE(bus_ehci0_clk, "bus-ehci0", "ahb1", + 0x060, BIT(26), 0); +static SUNXI_CCU_GATE(bus_ehci1_clk, "bus-ehci1", "ahb1", + 0x060, BIT(27), 0); +static SUNXI_CCU_GATE(bus_ehci2_clk, "bus-ehci2", "ahb1", + 0x060, BIT(28), 0); +static SUNXI_CCU_GATE(bus_ohci0_clk, "bus-ohci0", "ahb1", + 0x060, BIT(29), 0); +static SUNXI_CCU_GATE(bus_ohci1_clk, "bus-ohci1", "ahb1", + 0x060, BIT(30), 0); +static SUNXI_CCU_GATE(bus_ohci2_clk, "bus-ohci2", "ahb1", + 0x060, BIT(31), 0); + +static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb1", + 0x064, BIT(0), 0); +static SUNXI_CCU_GATE(bus_mp_clk, "bus-mp", "ahb1", + 0x064, BIT(2), 0); +static SUNXI_CCU_GATE(bus_deinterlace_clk, "bus-deinterlace", "ahb1", + 0x064, BIT(5), 0); +static SUNXI_CCU_GATE(bus_csi0_clk, "bus-csi0", "ahb1", + 0x064, BIT(8), 0); +static SUNXI_CCU_GATE(bus_csi1_clk, "bus-csi1", "ahb1", + 0x064, BIT(9), 0); +static SUNXI_CCU_GATE(bus_hdmi0_clk, "bus-hdmi0", "ahb1", + 0x064, BIT(10), 0); +static SUNXI_CCU_GATE(bus_hdmi1_clk, "bus-hdmi1", "ahb1", + 0x064, BIT(11), 0); +static SUNXI_CCU_GATE(bus_de_clk, "bus-de", "ahb1", + 0x064, BIT(12), 0); +static SUNXI_CCU_GATE(bus_tve0_clk, "bus-tve0", "ahb1", + 0x064, BIT(13), 0); +static SUNXI_CCU_GATE(bus_tve1_clk, "bus-tve1", "ahb1", + 0x064, BIT(14), 0); +static SUNXI_CCU_GATE(bus_tve_top_clk, "bus-tve-top", "ahb1", + 0x064, BIT(15), 0); +static SUNXI_CCU_GATE(bus_gmac_clk, "bus-gmac", "ahb1", + 0x064, BIT(17), 0); +static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "ahb1", + 0x064, BIT(20), 0); +static SUNXI_CCU_GATE(bus_tvd0_clk, "bus-tvd0", "ahb1", + 0x064, BIT(21), 0); +static SUNXI_CCU_GATE(bus_tvd1_clk, "bus-tvd1", "ahb1", + 0x064, BIT(22), 0); +static SUNXI_CCU_GATE(bus_tvd2_clk, "bus-tvd2", "ahb1", + 0x064, BIT(23), 0); +static SUNXI_CCU_GATE(bus_tvd3_clk, "bus-tvd3", "ahb1", + 0x064, BIT(24), 0); +static SUNXI_CCU_GATE(bus_tvd_top_clk, "bus-tvd-top", "ahb1", + 0x064, BIT(25), 0); +static SUNXI_CCU_GATE(bus_tcon_lcd0_clk, "bus-tcon-lcd0", "ahb1", + 0x064, BIT(26), 0); +static SUNXI_CCU_GATE(bus_tcon_lcd1_clk, "bus-tcon-lcd1", "ahb1", + 0x064, BIT(27), 0); +static SUNXI_CCU_GATE(bus_tcon_tv0_clk, "bus-tcon-tv0", "ahb1", + 0x064, BIT(28), 0); +static SUNXI_CCU_GATE(bus_tcon_tv1_clk, "bus-tcon-tv1", "ahb1", + 0x064, BIT(29), 0); +static SUNXI_CCU_GATE(bus_tcon_top_clk, "bus-tcon-top", "ahb1", + 0x064, BIT(30), 0); + +static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1", + 0x068, BIT(0), 0); +static SUNXI_CCU_GATE(bus_spdif_clk, "bus-spdif", "apb1", + 0x068, BIT(1), 0); +static SUNXI_CCU_GATE(bus_ac97_clk, "bus-ac97", "apb1", + 0x068, BIT(2), 0); +static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1", + 0x068, BIT(5), 0); +static SUNXI_CCU_GATE(bus_ir0_clk, "bus-ir0", "apb1", + 0x068, BIT(6), 0); +static SUNXI_CCU_GATE(bus_ir1_clk, "bus-ir1", "apb1", + 0x068, BIT(7), 0); +static SUNXI_CCU_GATE(bus_ths_clk, "bus-ths", "apb1", + 0x068, BIT(8), 0); +static SUNXI_CCU_GATE(bus_keypad_clk, "bus-keypad", "apb1", + 0x068, BIT(10), 0); +static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1", + 0x068, BIT(12), 0); +static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb1", + 0x068, BIT(13), 0); +static SUNXI_CCU_GATE(bus_i2s2_clk, "bus-i2s2", "apb1", + 0x068, BIT(14), 0); + +static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2", + 0x06c, BIT(0), 0); +static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2", + 0x06c, BIT(1), 0); +static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2", + 0x06c, BIT(2), 0); +static SUNXI_CCU_GATE(bus_i2c3_clk, "bus-i2c3", "apb2", + 0x06c, BIT(3), 0); +/* + * In datasheet here's "Reserved", however the gate exists in BSP soucre + * code. + */ +static SUNXI_CCU_GATE(bus_can_clk, "bus-can", "apb2", + 0x06c, BIT(4), 0); +static SUNXI_CCU_GATE(bus_scr_clk, "bus-scr", "apb2", + 0x06c, BIT(5), 0); +static SUNXI_CCU_GATE(bus_ps20_clk, "bus-ps20", "apb2", + 0x06c, BIT(6), 0); +static SUNXI_CCU_GATE(bus_ps21_clk, "bus-ps21", "apb2", + 0x06c, BIT(7), 0); +static SUNXI_CCU_GATE(bus_i2c4_clk, "bus-i2c4", "apb2", + 0x06c, BIT(15), 0); +static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2", + 0x06c, BIT(16), 0); +static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2", + 0x06c, BIT(17), 0); +static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2", + 0x06c, BIT(18), 0); +static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb2", + 0x06c, BIT(19), 0); +static SUNXI_CCU_GATE(bus_uart4_clk, "bus-uart4", "apb2", + 0x06c, BIT(20), 0); +static SUNXI_CCU_GATE(bus_uart5_clk, "bus-uart5", "apb2", + 0x06c, BIT(21), 0); +static SUNXI_CCU_GATE(bus_uart6_clk, "bus-uart6", "apb2", + 0x06c, BIT(22), 0); +static SUNXI_CCU_GATE(bus_uart7_clk, "bus-uart7", "apb2", + 0x06c, BIT(23), 0); + +static SUNXI_CCU_GATE(bus_dbg_clk, "bus-dbg", "ahb1", + 0x070, BIT(7), 0); + +static const char * const ths_parents[] = { "osc24M" }; +static struct ccu_div ths_clk = { + .enable = BIT(31), + .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = _SUNXI_CCU_MUX(24, 2), + .common = { + .reg = 0x074, + .hw.init = CLK_HW_INIT_PARENTS("ths", + ths_parents, + &ccu_div_ops, + 0), + }, +}; + +static const char * const mod0_default_parents[] = { "osc24M", "pll-periph0", + "pll-periph1" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc3_clk, "mmc3", mod0_default_parents, 0x094, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const ts_parents[] = { "osc24M", "pll-periph0", }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", ts_parents, 0x098, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const ce_parents[] = { "osc24M", "pll-periph0-2x", + "pll-periph1-2x" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ce_clk, "ce", ce_parents, 0x09c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi2_clk, "spi2", mod0_default_parents, 0x0a8, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi3_clk, "spi3", mod0_default_parents, 0x0ac, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x", + "pll-audio-2x", "pll-audio" }; +static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents, + 0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents, + 0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(i2s2_clk, "i2s2", i2s_parents, + 0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(ac97_clk, "ac97", i2s_parents, + 0x0bc, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(spdif_clk, "spdif", i2s_parents, + 0x0c0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const keypad_parents[] = { "osc24M", "osc32k" }; +static const u8 keypad_table[] = { 0, 2 }; +static struct ccu_mp keypad_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(0, 5), + .p = _SUNXI_CCU_DIV(16, 2), + .mux = _SUNXI_CCU_MUX_TABLE(24, 2, keypad_table), + .common = { + .reg = 0x0c4, + .hw.init = CLK_HW_INIT_PARENTS("keypad", + keypad_parents, + &ccu_mp_ops, + 0), + } +}; + +static const char * const sata_parents[] = { "pll-sata-out", "sata-ext" }; +static SUNXI_CCU_MUX_WITH_GATE(sata_clk, "sata", sata_parents, + 0x0c8, 24, 1, BIT(31), CLK_SET_RATE_PARENT); + +/* + * There are 3 OHCI 12M clock source selection bits in this register. + * We will force them to 0 (12M divided from 48M). + */ +#define SUN8I_R40_USB_CLK_REG 0x0cc + +static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", + 0x0cc, BIT(8), 0); +static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M", + 0x0cc, BIT(9), 0); +static SUNXI_CCU_GATE(usb_phy2_clk, "usb-phy2", "osc24M", + 0x0cc, BIT(10), 0); +static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc12M", + 0x0cc, BIT(16), 0); +static SUNXI_CCU_GATE(usb_ohci1_clk, "usb-ohci1", "osc12M", + 0x0cc, BIT(17), 0); +static SUNXI_CCU_GATE(usb_ohci2_clk, "usb-ohci2", "osc12M", + 0x0cc, BIT(18), 0); + +static const char * const ir_parents[] = { "osc24M", "pll-periph0", + "pll-periph1", "osc32k" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ir0_clk, "ir0", ir_parents, 0x0d0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ir1_clk, "ir1", ir_parents, 0x0d4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const dram_parents[] = { "pll-ddr0", "pll-ddr1" }; +static SUNXI_CCU_M_WITH_MUX(dram_clk, "dram", dram_parents, + 0x0f4, 0, 2, 20, 2, CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "dram", + 0x100, BIT(0), 0); +static SUNXI_CCU_GATE(dram_csi0_clk, "dram-csi0", "dram", + 0x100, BIT(1), 0); +static SUNXI_CCU_GATE(dram_csi1_clk, "dram-csi1", "dram", + 0x100, BIT(2), 0); +static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "dram", + 0x100, BIT(3), 0); +static SUNXI_CCU_GATE(dram_tvd_clk, "dram-tvd", "dram", + 0x100, BIT(4), 0); +static SUNXI_CCU_GATE(dram_mp_clk, "dram-mp", "dram", + 0x100, BIT(5), 0); +static SUNXI_CCU_GATE(dram_deinterlace_clk, "dram-deinterlace", "dram", + 0x100, BIT(6), 0); + +static const char * const de_parents[] = { "pll-periph0-2x", "pll-de" }; +static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents, + 0x104, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(mp_clk, "mp", de_parents, + 0x108, 0, 4, 24, 3, BIT(31), 0); + +static const char * const tcon_parents[] = { "pll-video0", "pll-video1", + "pll-video0-2x", "pll-video1-2x", + "pll-mipi" }; +static SUNXI_CCU_MUX_WITH_GATE(tcon_lcd0_clk, "tcon-lcd0", tcon_parents, + 0x110, 24, 3, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_MUX_WITH_GATE(tcon_lcd1_clk, "tcon-lcd1", tcon_parents, + 0x114, 24, 3, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_MUX_GATE(tcon_tv0_clk, "tcon-tv0", tcon_parents, + 0x118, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(tcon_tv1_clk, "tcon-tv1", tcon_parents, + 0x11c, 0, 4, 24, 3, BIT(31), 0); + +static const char * const deinterlace_parents[] = { "pll-periph0", + "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", + deinterlace_parents, 0x124, 0, 4, 24, 3, + BIT(31), 0); + +static const char * const csi_mclk_parents[] = { "osc24M", "pll-video1", + "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(csi1_mclk_clk, "csi1-mclk", csi_mclk_parents, + 0x130, 0, 5, 8, 3, BIT(15), 0); + +static const char * const csi_sclk_parents[] = { "pll-periph0", "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(csi_sclk_clk, "csi-sclk", csi_sclk_parents, + 0x134, 16, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(csi0_mclk_clk, "csi0-mclk", csi_mclk_parents, + 0x134, 0, 5, 8, 3, BIT(15), 0); + +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", + 0x13c, 16, 3, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(codec_clk, "codec", "pll-audio", + 0x140, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", + 0x144, BIT(31), 0); + +static const char * const hdmi_parents[] = { "pll-video0", "pll-video1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents, + 0x150, 0, 4, 24, 2, BIT(31), 0); + +static SUNXI_CCU_GATE(hdmi_slow_clk, "hdmi-slow", "osc24M", + 0x154, BIT(31), 0); + +/* + * In the SoC's user manual, the P factor is mentioned, but not used in + * the frequency formula. + * + * Here the factor is included, according to the BSP kernel source, + * which contains the P factor of this clock. + */ +static const char * const mbus_parents[] = { "osc24M", "pll-periph0-2x", + "pll-ddr0" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, 0x15c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + CLK_IS_CRITICAL); + +static const char * const dsi_dphy_parents[] = { "pll-video0", "pll-video1", + "pll-periph0" }; +static SUNXI_CCU_M_WITH_MUX_GATE(dsi_dphy_clk, "dsi-dphy", dsi_dphy_parents, + 0x168, 0, 4, 8, 2, BIT(15), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(tve0_clk, "tve0", tcon_parents, + 0x180, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(tve1_clk, "tve1", tcon_parents, + 0x184, 0, 4, 24, 3, BIT(31), 0); + +static const char * const tvd_parents[] = { "pll-video0", "pll-video1", + "pll-video0-2x", "pll-video1-2x" }; +static SUNXI_CCU_M_WITH_MUX_GATE(tvd0_clk, "tvd0", tvd_parents, + 0x188, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(tvd1_clk, "tvd1", tvd_parents, + 0x18c, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(tvd2_clk, "tvd2", tvd_parents, + 0x190, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(tvd3_clk, "tvd3", tvd_parents, + 0x194, 0, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu", + 0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const out_parents[] = { "osc24M", "osc32k", "osc24M" }; +static const struct ccu_mux_fixed_prediv out_predivs[] = { + { .index = 0, .div = 750, }, +}; + +static struct ccu_mp outa_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = out_predivs, + .n_predivs = ARRAY_SIZE(out_predivs), + }, + .common = { + .reg = 0x1f0, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("outa", out_parents, + &ccu_mp_ops, 0), + } +}; + +static struct ccu_mp outb_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = out_predivs, + .n_predivs = ARRAY_SIZE(out_predivs), + }, + .common = { + .reg = 0x1f4, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("outb", out_parents, + &ccu_mp_ops, 0), + } +}; + +static struct ccu_common *sun8i_r40_ccu_clks[] = { + &pll_cpu_clk.common, + &pll_audio_base_clk.common, + &pll_video0_clk.common, + &pll_ve_clk.common, + &pll_ddr0_clk.common, + &pll_periph0_clk.common, + &pll_periph0_sata_clk.common, + &pll_periph1_clk.common, + &pll_video1_clk.common, + &pll_sata_clk.common, + &pll_sata_out_clk.common, + &pll_gpu_clk.common, + &pll_mipi_clk.common, + &pll_de_clk.common, + &pll_ddr1_clk.common, + &cpu_clk.common, + &axi_clk.common, + &ahb1_clk.common, + &apb1_clk.common, + &apb2_clk.common, + &bus_mipi_dsi_clk.common, + &bus_ce_clk.common, + &bus_dma_clk.common, + &bus_mmc0_clk.common, + &bus_mmc1_clk.common, + &bus_mmc2_clk.common, + &bus_mmc3_clk.common, + &bus_nand_clk.common, + &bus_dram_clk.common, + &bus_emac_clk.common, + &bus_ts_clk.common, + &bus_hstimer_clk.common, + &bus_spi0_clk.common, + &bus_spi1_clk.common, + &bus_spi2_clk.common, + &bus_spi3_clk.common, + &bus_sata_clk.common, + &bus_otg_clk.common, + &bus_ehci0_clk.common, + &bus_ehci1_clk.common, + &bus_ehci2_clk.common, + &bus_ohci0_clk.common, + &bus_ohci1_clk.common, + &bus_ohci2_clk.common, + &bus_ve_clk.common, + &bus_mp_clk.common, + &bus_deinterlace_clk.common, + &bus_csi0_clk.common, + &bus_csi1_clk.common, + &bus_hdmi0_clk.common, + &bus_hdmi1_clk.common, + &bus_de_clk.common, + &bus_tve0_clk.common, + &bus_tve1_clk.common, + &bus_tve_top_clk.common, + &bus_gmac_clk.common, + &bus_gpu_clk.common, + &bus_tvd0_clk.common, + &bus_tvd1_clk.common, + &bus_tvd2_clk.common, + &bus_tvd3_clk.common, + &bus_tvd_top_clk.common, + &bus_tcon_lcd0_clk.common, + &bus_tcon_lcd1_clk.common, + &bus_tcon_tv0_clk.common, + &bus_tcon_tv1_clk.common, + &bus_tcon_top_clk.common, + &bus_codec_clk.common, + &bus_spdif_clk.common, + &bus_ac97_clk.common, + &bus_pio_clk.common, + &bus_ir0_clk.common, + &bus_ir1_clk.common, + &bus_ths_clk.common, + &bus_keypad_clk.common, + &bus_i2s0_clk.common, + &bus_i2s1_clk.common, + &bus_i2s2_clk.common, + &bus_i2c0_clk.common, + &bus_i2c1_clk.common, + &bus_i2c2_clk.common, + &bus_i2c3_clk.common, + &bus_can_clk.common, + &bus_scr_clk.common, + &bus_ps20_clk.common, + &bus_ps21_clk.common, + &bus_i2c4_clk.common, + &bus_uart0_clk.common, + &bus_uart1_clk.common, + &bus_uart2_clk.common, + &bus_uart3_clk.common, + &bus_uart4_clk.common, + &bus_uart5_clk.common, + &bus_uart6_clk.common, + &bus_uart7_clk.common, + &bus_dbg_clk.common, + &ths_clk.common, + &nand_clk.common, + &mmc0_clk.common, + &mmc1_clk.common, + &mmc2_clk.common, + &mmc3_clk.common, + &ts_clk.common, + &ce_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &spi2_clk.common, + &spi3_clk.common, + &i2s0_clk.common, + &i2s1_clk.common, + &i2s2_clk.common, + &ac97_clk.common, + &spdif_clk.common, + &keypad_clk.common, + &sata_clk.common, + &usb_phy0_clk.common, + &usb_phy1_clk.common, + &usb_phy2_clk.common, + &usb_ohci0_clk.common, + &usb_ohci1_clk.common, + &usb_ohci2_clk.common, + &ir0_clk.common, + &ir1_clk.common, + &dram_clk.common, + &dram_ve_clk.common, + &dram_csi0_clk.common, + &dram_csi1_clk.common, + &dram_ts_clk.common, + &dram_tvd_clk.common, + &dram_mp_clk.common, + &dram_deinterlace_clk.common, + &de_clk.common, + &mp_clk.common, + &tcon_lcd0_clk.common, + &tcon_lcd1_clk.common, + &tcon_tv0_clk.common, + &tcon_tv1_clk.common, + &deinterlace_clk.common, + &csi1_mclk_clk.common, + &csi_sclk_clk.common, + &csi0_mclk_clk.common, + &ve_clk.common, + &codec_clk.common, + &avs_clk.common, + &hdmi_clk.common, + &hdmi_slow_clk.common, + &mbus_clk.common, + &dsi_dphy_clk.common, + &tve0_clk.common, + &tve1_clk.common, + &tvd0_clk.common, + &tvd1_clk.common, + &tvd2_clk.common, + &tvd3_clk.common, + &gpu_clk.common, + &outa_clk.common, + &outb_clk.common, +}; + +/* Fixed Factor clocks */ +static CLK_FIXED_FACTOR(osc12M_clk, "osc12M", "osc24M", 2, 1, 0); + +/* We hardcode the divider to 4 for now */ +static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", + "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", + "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", + "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_periph0_2x_clk, "pll-periph0-2x", + "pll-periph0", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_periph1_2x_clk, "pll-periph1-2x", + "pll-periph1", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x", + "pll-video0", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_video1_2x_clk, "pll-video1-2x", + "pll-video1", 1, 2, 0); + +static struct clk_hw_onecell_data sun8i_r40_hw_clks = { + .hws = { + [CLK_OSC_12M] = &osc12M_clk.hw, + [CLK_PLL_CPU] = &pll_cpu_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw, + [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw, + [CLK_PLL_PERIPH0_SATA] = &pll_periph0_sata_clk.common.hw, + [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.hw, + [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw, + [CLK_PLL_PERIPH1_2X] = &pll_periph1_2x_clk.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_PLL_SATA] = &pll_sata_clk.common.hw, + [CLK_PLL_SATA_OUT] = &pll_sata_out_clk.common.hw, + [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, + [CLK_PLL_MIPI] = &pll_mipi_clk.common.hw, + [CLK_PLL_DE] = &pll_de_clk.common.hw, + [CLK_PLL_DDR1] = &pll_ddr1_clk.common.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB1] = &ahb1_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_APB2] = &apb2_clk.common.hw, + [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw, + [CLK_BUS_CE] = &bus_ce_clk.common.hw, + [CLK_BUS_DMA] = &bus_dma_clk.common.hw, + [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw, + [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw, + [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw, + [CLK_BUS_MMC3] = &bus_mmc3_clk.common.hw, + [CLK_BUS_NAND] = &bus_nand_clk.common.hw, + [CLK_BUS_DRAM] = &bus_dram_clk.common.hw, + [CLK_BUS_EMAC] = &bus_emac_clk.common.hw, + [CLK_BUS_TS] = &bus_ts_clk.common.hw, + [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw, + [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw, + [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw, + [CLK_BUS_SPI2] = &bus_spi2_clk.common.hw, + [CLK_BUS_SPI3] = &bus_spi3_clk.common.hw, + [CLK_BUS_SATA] = &bus_sata_clk.common.hw, + [CLK_BUS_OTG] = &bus_otg_clk.common.hw, + [CLK_BUS_EHCI0] = &bus_ehci0_clk.common.hw, + [CLK_BUS_EHCI1] = &bus_ehci1_clk.common.hw, + [CLK_BUS_EHCI2] = &bus_ehci2_clk.common.hw, + [CLK_BUS_OHCI0] = &bus_ohci0_clk.common.hw, + [CLK_BUS_OHCI1] = &bus_ohci1_clk.common.hw, + [CLK_BUS_OHCI2] = &bus_ohci2_clk.common.hw, + [CLK_BUS_VE] = &bus_ve_clk.common.hw, + [CLK_BUS_MP] = &bus_mp_clk.common.hw, + [CLK_BUS_DEINTERLACE] = &bus_deinterlace_clk.common.hw, + [CLK_BUS_CSI0] = &bus_csi0_clk.common.hw, + [CLK_BUS_CSI1] = &bus_csi1_clk.common.hw, + [CLK_BUS_HDMI0] = &bus_hdmi0_clk.common.hw, + [CLK_BUS_HDMI1] = &bus_hdmi1_clk.common.hw, + [CLK_BUS_DE] = &bus_de_clk.common.hw, + [CLK_BUS_TVE0] = &bus_tve0_clk.common.hw, + [CLK_BUS_TVE1] = &bus_tve1_clk.common.hw, + [CLK_BUS_TVE_TOP] = &bus_tve_top_clk.common.hw, + [CLK_BUS_GMAC] = &bus_gmac_clk.common.hw, + [CLK_BUS_GPU] = &bus_gpu_clk.common.hw, + [CLK_BUS_TVD0] = &bus_tvd0_clk.common.hw, + [CLK_BUS_TVD1] = &bus_tvd1_clk.common.hw, + [CLK_BUS_TVD2] = &bus_tvd2_clk.common.hw, + [CLK_BUS_TVD3] = &bus_tvd3_clk.common.hw, + [CLK_BUS_TVD_TOP] = &bus_tvd_top_clk.common.hw, + [CLK_BUS_TCON_LCD0] = &bus_tcon_lcd0_clk.common.hw, + [CLK_BUS_TCON_LCD1] = &bus_tcon_lcd1_clk.common.hw, + [CLK_BUS_TCON_TV0] = &bus_tcon_tv0_clk.common.hw, + [CLK_BUS_TCON_TV1] = &bus_tcon_tv1_clk.common.hw, + [CLK_BUS_TCON_TOP] = &bus_tcon_top_clk.common.hw, + [CLK_BUS_CODEC] = &bus_codec_clk.common.hw, + [CLK_BUS_SPDIF] = &bus_spdif_clk.common.hw, + [CLK_BUS_AC97] = &bus_ac97_clk.common.hw, + [CLK_BUS_PIO] = &bus_pio_clk.common.hw, + [CLK_BUS_IR0] = &bus_ir0_clk.common.hw, + [CLK_BUS_IR1] = &bus_ir1_clk.common.hw, + [CLK_BUS_THS] = &bus_ths_clk.common.hw, + [CLK_BUS_KEYPAD] = &bus_keypad_clk.common.hw, + [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw, + [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw, + [CLK_BUS_I2S2] = &bus_i2s2_clk.common.hw, + [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw, + [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw, + [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw, + [CLK_BUS_I2C3] = &bus_i2c3_clk.common.hw, + [CLK_BUS_CAN] = &bus_can_clk.common.hw, + [CLK_BUS_SCR] = &bus_scr_clk.common.hw, + [CLK_BUS_PS20] = &bus_ps20_clk.common.hw, + [CLK_BUS_PS21] = &bus_ps21_clk.common.hw, + [CLK_BUS_I2C4] = &bus_i2c4_clk.common.hw, + [CLK_BUS_UART0] = &bus_uart0_clk.common.hw, + [CLK_BUS_UART1] = &bus_uart1_clk.common.hw, + [CLK_BUS_UART2] = &bus_uart2_clk.common.hw, + [CLK_BUS_UART3] = &bus_uart3_clk.common.hw, + [CLK_BUS_UART4] = &bus_uart4_clk.common.hw, + [CLK_BUS_UART5] = &bus_uart5_clk.common.hw, + [CLK_BUS_UART6] = &bus_uart6_clk.common.hw, + [CLK_BUS_UART7] = &bus_uart7_clk.common.hw, + [CLK_BUS_DBG] = &bus_dbg_clk.common.hw, + [CLK_THS] = &ths_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC3] = &mmc3_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_CE] = &ce_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_SPI3] = &spi3_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_I2S1] = &i2s1_clk.common.hw, + [CLK_I2S2] = &i2s2_clk.common.hw, + [CLK_AC97] = &ac97_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_KEYPAD] = &keypad_clk.common.hw, + [CLK_SATA] = &sata_clk.common.hw, + [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, + [CLK_USB_PHY1] = &usb_phy1_clk.common.hw, + [CLK_USB_PHY2] = &usb_phy2_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw, + [CLK_USB_OHCI2] = &usb_ohci2_clk.common.hw, + [CLK_IR0] = &ir0_clk.common.hw, + [CLK_IR1] = &ir1_clk.common.hw, + [CLK_DRAM] = &dram_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI0] = &dram_csi0_clk.common.hw, + [CLK_DRAM_CSI1] = &dram_csi1_clk.common.hw, + [CLK_DRAM_TS] = &dram_ts_clk.common.hw, + [CLK_DRAM_TVD] = &dram_tvd_clk.common.hw, + [CLK_DRAM_MP] = &dram_mp_clk.common.hw, + [CLK_DRAM_DEINTERLACE] = &dram_deinterlace_clk.common.hw, + [CLK_DE] = &de_clk.common.hw, + [CLK_MP] = &mp_clk.common.hw, + [CLK_TCON_LCD0] = &tcon_lcd0_clk.common.hw, + [CLK_TCON_LCD1] = &tcon_lcd1_clk.common.hw, + [CLK_TCON_TV0] = &tcon_tv0_clk.common.hw, + [CLK_TCON_TV1] = &tcon_tv1_clk.common.hw, + [CLK_DEINTERLACE] = &deinterlace_clk.common.hw, + [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, + [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_CODEC] = &codec_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_HDMI_SLOW] = &hdmi_slow_clk.common.hw, + [CLK_MBUS] = &mbus_clk.common.hw, + [CLK_DSI_DPHY] = &dsi_dphy_clk.common.hw, + [CLK_TVE0] = &tve0_clk.common.hw, + [CLK_TVE1] = &tve1_clk.common.hw, + [CLK_TVD0] = &tvd0_clk.common.hw, + [CLK_TVD1] = &tvd1_clk.common.hw, + [CLK_TVD2] = &tvd2_clk.common.hw, + [CLK_TVD3] = &tvd3_clk.common.hw, + [CLK_GPU] = &gpu_clk.common.hw, + [CLK_OUTA] = &outa_clk.common.hw, + [CLK_OUTB] = &outb_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun8i_r40_ccu_resets[] = { + [RST_USB_PHY0] = { 0x0cc, BIT(0) }, + [RST_USB_PHY1] = { 0x0cc, BIT(1) }, + [RST_USB_PHY2] = { 0x0cc, BIT(2) }, + + [RST_DRAM] = { 0x0f4, BIT(31) }, + [RST_MBUS] = { 0x0fc, BIT(31) }, + + [RST_BUS_MIPI_DSI] = { 0x2c0, BIT(1) }, + [RST_BUS_CE] = { 0x2c0, BIT(5) }, + [RST_BUS_DMA] = { 0x2c0, BIT(6) }, + [RST_BUS_MMC0] = { 0x2c0, BIT(8) }, + [RST_BUS_MMC1] = { 0x2c0, BIT(9) }, + [RST_BUS_MMC2] = { 0x2c0, BIT(10) }, + [RST_BUS_MMC3] = { 0x2c0, BIT(11) }, + [RST_BUS_NAND] = { 0x2c0, BIT(13) }, + [RST_BUS_DRAM] = { 0x2c0, BIT(14) }, + [RST_BUS_EMAC] = { 0x2c0, BIT(17) }, + [RST_BUS_TS] = { 0x2c0, BIT(18) }, + [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) }, + [RST_BUS_SPI0] = { 0x2c0, BIT(20) }, + [RST_BUS_SPI1] = { 0x2c0, BIT(21) }, + [RST_BUS_SPI2] = { 0x2c0, BIT(22) }, + [RST_BUS_SPI3] = { 0x2c0, BIT(23) }, + [RST_BUS_SATA] = { 0x2c0, BIT(24) }, + [RST_BUS_OTG] = { 0x2c0, BIT(25) }, + [RST_BUS_EHCI0] = { 0x2c0, BIT(26) }, + [RST_BUS_EHCI1] = { 0x2c0, BIT(27) }, + [RST_BUS_EHCI2] = { 0x2c0, BIT(28) }, + [RST_BUS_OHCI0] = { 0x2c0, BIT(29) }, + [RST_BUS_OHCI1] = { 0x2c0, BIT(30) }, + [RST_BUS_OHCI2] = { 0x2c0, BIT(31) }, + + [RST_BUS_VE] = { 0x2c4, BIT(0) }, + [RST_BUS_MP] = { 0x2c4, BIT(2) }, + [RST_BUS_DEINTERLACE] = { 0x2c4, BIT(5) }, + [RST_BUS_CSI0] = { 0x2c4, BIT(8) }, + [RST_BUS_CSI1] = { 0x2c4, BIT(9) }, + [RST_BUS_HDMI0] = { 0x2c4, BIT(10) }, + [RST_BUS_HDMI1] = { 0x2c4, BIT(11) }, + [RST_BUS_DE] = { 0x2c4, BIT(12) }, + [RST_BUS_TVE0] = { 0x2c4, BIT(13) }, + [RST_BUS_TVE1] = { 0x2c4, BIT(14) }, + [RST_BUS_TVE_TOP] = { 0x2c4, BIT(15) }, + [RST_BUS_GMAC] = { 0x2c4, BIT(17) }, + [RST_BUS_GPU] = { 0x2c4, BIT(20) }, + [RST_BUS_TVD0] = { 0x2c4, BIT(21) }, + [RST_BUS_TVD1] = { 0x2c4, BIT(22) }, + [RST_BUS_TVD2] = { 0x2c4, BIT(23) }, + [RST_BUS_TVD3] = { 0x2c4, BIT(24) }, + [RST_BUS_TVD_TOP] = { 0x2c4, BIT(25) }, + [RST_BUS_TCON_LCD0] = { 0x2c4, BIT(26) }, + [RST_BUS_TCON_LCD1] = { 0x2c4, BIT(27) }, + [RST_BUS_TCON_TV0] = { 0x2c4, BIT(28) }, + [RST_BUS_TCON_TV1] = { 0x2c4, BIT(29) }, + [RST_BUS_TCON_TOP] = { 0x2c4, BIT(30) }, + [RST_BUS_DBG] = { 0x2c4, BIT(31) }, + + [RST_BUS_LVDS] = { 0x2c8, BIT(0) }, + + [RST_BUS_CODEC] = { 0x2d0, BIT(0) }, + [RST_BUS_SPDIF] = { 0x2d0, BIT(1) }, + [RST_BUS_AC97] = { 0x2d0, BIT(2) }, + [RST_BUS_IR0] = { 0x2d0, BIT(6) }, + [RST_BUS_IR1] = { 0x2d0, BIT(7) }, + [RST_BUS_THS] = { 0x2d0, BIT(8) }, + [RST_BUS_KEYPAD] = { 0x2d0, BIT(10) }, + [RST_BUS_I2S0] = { 0x2d0, BIT(12) }, + [RST_BUS_I2S1] = { 0x2d0, BIT(13) }, + [RST_BUS_I2S2] = { 0x2d0, BIT(14) }, + + [RST_BUS_I2C0] = { 0x2d8, BIT(0) }, + [RST_BUS_I2C1] = { 0x2d8, BIT(1) }, + [RST_BUS_I2C2] = { 0x2d8, BIT(2) }, + [RST_BUS_I2C3] = { 0x2d8, BIT(3) }, + [RST_BUS_CAN] = { 0x2d8, BIT(4) }, + [RST_BUS_SCR] = { 0x2d8, BIT(5) }, + [RST_BUS_PS20] = { 0x2d8, BIT(6) }, + [RST_BUS_PS21] = { 0x2d8, BIT(7) }, + [RST_BUS_I2C4] = { 0x2d8, BIT(15) }, + [RST_BUS_UART0] = { 0x2d8, BIT(16) }, + [RST_BUS_UART1] = { 0x2d8, BIT(17) }, + [RST_BUS_UART2] = { 0x2d8, BIT(18) }, + [RST_BUS_UART3] = { 0x2d8, BIT(19) }, + [RST_BUS_UART4] = { 0x2d8, BIT(20) }, + [RST_BUS_UART5] = { 0x2d8, BIT(21) }, + [RST_BUS_UART6] = { 0x2d8, BIT(22) }, + [RST_BUS_UART7] = { 0x2d8, BIT(23) }, +}; + +static const struct sunxi_ccu_desc sun8i_r40_ccu_desc = { + .ccu_clks = sun8i_r40_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_r40_ccu_clks), + + .hw_clks = &sun8i_r40_hw_clks, + + .resets = sun8i_r40_ccu_resets, + .num_resets = ARRAY_SIZE(sun8i_r40_ccu_resets), +}; + +static struct ccu_pll_nb sun8i_r40_pll_cpu_nb = { + .common = &pll_cpu_clk.common, + /* copy from pll_cpu_clk */ + .enable = BIT(31), + .lock = BIT(28), +}; + +static struct ccu_mux_nb sun8i_r40_cpu_nb = { + .common = &cpu_clk.common, + .cm = &cpu_clk.mux, + .delay_us = 1, /* > 8 clock cycles at 24 MHz */ + .bypass_index = 1, /* index of 24 MHz oscillator */ +}; + +static void __init sun8i_r40_ccu_setup(struct device_node *node) +{ + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", + of_node_full_name(node)); + return; + } + + /* Force the PLL-Audio-1x divider to 4 */ + val = readl(reg + SUN8I_R40_PLL_AUDIO_REG); + val &= ~GENMASK(19, 16); + writel(val | (3 << 16), reg + SUN8I_R40_PLL_AUDIO_REG); + + /* Force PLL-MIPI to MIPI mode */ + val = readl(reg + SUN8I_R40_PLL_MIPI_REG); + val &= ~BIT(16); + writel(val, reg + SUN8I_R40_PLL_MIPI_REG); + + /* Force OHCI 12M parent to 12M divided from 48M */ + val = readl(reg + SUN8I_R40_USB_CLK_REG); + val &= ~GENMASK(25, 20); + writel(val, reg + SUN8I_R40_USB_CLK_REG); + + sunxi_ccu_probe(node, reg, &sun8i_r40_ccu_desc); + + /* Gate then ungate PLL CPU after any rate changes */ + ccu_pll_notifier_register(&sun8i_r40_pll_cpu_nb); + + /* Reparent CPU during PLL CPU rate changes */ + ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk, + &sun8i_r40_cpu_nb); +} +CLK_OF_DECLARE(sun8i_r40_ccu, "allwinner,sun8i-r40-ccu", + sun8i_r40_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r40.h b/drivers/clk/sunxi-ng/ccu-sun8i-r40.h new file mode 100644 index 000000000000..0db8e1e97af8 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r40.h @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Icenowy Zheng <icenowy@aosc.io> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CCU_SUN8I_R40_H_ +#define _CCU_SUN8I_R40_H_ + +#include <dt-bindings/clock/sun8i-r40-ccu.h> +#include <dt-bindings/reset/sun8i-r40-ccu.h> + +#define CLK_OSC_12M 0 +#define CLK_PLL_CPU 1 +#define CLK_PLL_AUDIO_BASE 2 +#define CLK_PLL_AUDIO 3 +#define CLK_PLL_AUDIO_2X 4 +#define CLK_PLL_AUDIO_4X 5 +#define CLK_PLL_AUDIO_8X 6 +#define CLK_PLL_VIDEO0 7 +#define CLK_PLL_VIDEO0_2X 8 +#define CLK_PLL_VE 9 +#define CLK_PLL_DDR0 10 +#define CLK_PLL_PERIPH0 11 +#define CLK_PLL_PERIPH0_SATA 12 +#define CLK_PLL_PERIPH0_2X 13 +#define CLK_PLL_PERIPH1 14 +#define CLK_PLL_PERIPH1_2X 15 +#define CLK_PLL_VIDEO1 16 +#define CLK_PLL_VIDEO1_2X 17 +#define CLK_PLL_SATA 18 +#define CLK_PLL_SATA_OUT 19 +#define CLK_PLL_GPU 20 +#define CLK_PLL_MIPI 21 +#define CLK_PLL_DE 22 +#define CLK_PLL_DDR1 23 + +/* The CPU clock is exported */ + +#define CLK_AXI 25 +#define CLK_AHB1 26 +#define CLK_APB1 27 +#define CLK_APB2 28 + +/* All the bus gates are exported */ + +/* The first bunch of module clocks are exported */ + +#define CLK_DRAM 132 + +/* All the DRAM gates are exported */ + +/* Some more module clocks are exported */ + +#define CLK_MBUS 155 + +/* Another bunch of module clocks are exported */ + +#define CLK_NUMBER (CLK_OUTB + 1) + +#endif /* _CCU_SUN8I_R40_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c index a34a78d7fb28..621b1cd996db 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c @@ -575,8 +575,7 @@ static void __init sun8i_v3s_ccu_setup(struct device_node *node) reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c index c0e5c10d0091..baa3cf96507b 100644 --- a/drivers/clk/sunxi-ng/ccu_div.c +++ b/drivers/clk/sunxi-ng/ccu_div.c @@ -21,10 +21,18 @@ static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux, { struct ccu_div *cd = data; - return divider_round_rate_parent(&cd->common.hw, parent, + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= cd->fixed_post_div; + + rate = divider_round_rate_parent(&cd->common.hw, parent, rate, parent_rate, cd->div.table, cd->div.width, cd->div.flags); + + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= cd->fixed_post_div; + + return rate; } static void ccu_div_disable(struct clk_hw *hw) @@ -62,8 +70,13 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw, parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1, parent_rate); - return divider_recalc_rate(hw, parent_rate, val, cd->div.table, - cd->div.flags); + val = divider_recalc_rate(hw, parent_rate, val, cd->div.table, + cd->div.flags); + + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + val /= cd->fixed_post_div; + + return val; } static int ccu_div_determine_rate(struct clk_hw *hw, @@ -86,6 +99,9 @@ static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate, parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1, parent_rate); + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= cd->fixed_post_div; + val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width, cd->div.flags); diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h index 08d074451204..f3a5028dcd14 100644 --- a/drivers/clk/sunxi-ng/ccu_div.h +++ b/drivers/clk/sunxi-ng/ccu_div.h @@ -86,9 +86,10 @@ struct ccu_div_internal { struct ccu_div { u32 enable; - struct ccu_div_internal div; + struct ccu_div_internal div; struct ccu_mux_internal mux; struct ccu_common common; + unsigned int fixed_post_div; }; #define SUNXI_CCU_DIV_TABLE_WITH_GATE(_struct, _name, _parent, _reg, \ diff --git a/drivers/clk/sunxi-ng/ccu_frac.c b/drivers/clk/sunxi-ng/ccu_frac.c index 8b5eb7756bf7..d1d168d4c4f0 100644 --- a/drivers/clk/sunxi-ng/ccu_frac.c +++ b/drivers/clk/sunxi-ng/ccu_frac.c @@ -67,25 +67,25 @@ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, { u32 reg; - printk("%s: Read fractional\n", clk_hw_get_name(&common->hw)); + pr_debug("%s: Read fractional\n", clk_hw_get_name(&common->hw)); if (!(common->features & CCU_FEATURE_FRACTIONAL)) return 0; - printk("%s: clock is fractional (rates %lu and %lu)\n", - clk_hw_get_name(&common->hw), cf->rates[0], cf->rates[1]); + pr_debug("%s: clock is fractional (rates %lu and %lu)\n", + clk_hw_get_name(&common->hw), cf->rates[0], cf->rates[1]); reg = readl(common->base + common->reg); - printk("%s: clock reg is 0x%x (select is 0x%x)\n", - clk_hw_get_name(&common->hw), reg, cf->select); + pr_debug("%s: clock reg is 0x%x (select is 0x%x)\n", + clk_hw_get_name(&common->hw), reg, cf->select); return (reg & cf->select) ? cf->rates[1] : cf->rates[0]; } int ccu_frac_helper_set_rate(struct ccu_common *common, struct ccu_frac_internal *cf, - unsigned long rate) + unsigned long rate, u32 lock) { unsigned long flags; u32 reg, sel; @@ -106,5 +106,7 @@ int ccu_frac_helper_set_rate(struct ccu_common *common, writel(reg | sel, common->base + common->reg); spin_unlock_irqrestore(common->lock, flags); + ccu_helper_wait_for_lock(common, lock); + return 0; } diff --git a/drivers/clk/sunxi-ng/ccu_frac.h b/drivers/clk/sunxi-ng/ccu_frac.h index 7b1ee380156f..efe2dd6bac01 100644 --- a/drivers/clk/sunxi-ng/ccu_frac.h +++ b/drivers/clk/sunxi-ng/ccu_frac.h @@ -48,6 +48,6 @@ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, int ccu_frac_helper_set_rate(struct ccu_common *common, struct ccu_frac_internal *cf, - unsigned long rate); + unsigned long rate, u32 lock); #endif /* _CCU_FRAC_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c index 20d0300867f2..12e0783caee6 100644 --- a/drivers/clk/sunxi-ng/ccu_mult.c +++ b/drivers/clk/sunxi-ng/ccu_mult.c @@ -111,10 +111,14 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags; u32 reg; - if (ccu_frac_helper_has_rate(&cm->common, &cm->frac, rate)) - return ccu_frac_helper_set_rate(&cm->common, &cm->frac, rate); - else + if (ccu_frac_helper_has_rate(&cm->common, &cm->frac, rate)) { + ccu_frac_helper_enable(&cm->common, &cm->frac); + + return ccu_frac_helper_set_rate(&cm->common, &cm->frac, + rate, cm->lock); + } else { ccu_frac_helper_disable(&cm->common, &cm->frac); + } parent_rate = ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1, parent_rate); diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c index 44b16dc8fea6..841840e35e61 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.c +++ b/drivers/clk/sunxi-ng/ccu_nkm.c @@ -75,7 +75,7 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); - unsigned long n, m, k; + unsigned long n, m, k, rate; u32 reg; reg = readl(nkm->common.base + nkm->common.reg); @@ -98,7 +98,12 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw, if (!m) m++; - return parent_rate * n * k / m; + rate = parent_rate * n * k / m; + + if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= nkm->fixed_post_div; + + return rate; } static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, @@ -117,9 +122,17 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, _nkm.min_m = 1; _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; + if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= nkm->fixed_post_div; + ccu_nkm_find_best(*parent_rate, rate, &_nkm); - return *parent_rate * _nkm.n * _nkm.k / _nkm.m; + rate = *parent_rate * _nkm.n * _nkm.k / _nkm.m; + + if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= nkm->fixed_post_div; + + return rate; } static int ccu_nkm_determine_rate(struct clk_hw *hw, @@ -139,6 +152,9 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags; u32 reg; + if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= nkm->fixed_post_div; + _nkm.min_n = nkm->n.min ?: 1; _nkm.max_n = nkm->n.max ?: 1 << nkm->n.width; _nkm.min_k = nkm->k.min ?: 1; diff --git a/drivers/clk/sunxi-ng/ccu_nkm.h b/drivers/clk/sunxi-ng/ccu_nkm.h index 34580894f4d1..cc6efb70a102 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.h +++ b/drivers/clk/sunxi-ng/ccu_nkm.h @@ -34,6 +34,8 @@ struct ccu_nkm { struct ccu_div_internal m; struct ccu_mux_internal mux; + unsigned int fixed_post_div; + struct ccu_common common; }; diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c index 5e5e90a4a50c..a32158e8f2e3 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.c +++ b/drivers/clk/sunxi-ng/ccu_nm.c @@ -117,10 +117,23 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags; u32 reg; - if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) - return ccu_frac_helper_set_rate(&nm->common, &nm->frac, rate); - else + if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) { + spin_lock_irqsave(nm->common.lock, flags); + + /* most SoCs require M to be 0 if fractional mode is used */ + reg = readl(nm->common.base + nm->common.reg); + reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift); + writel(reg, nm->common.base + nm->common.reg); + + spin_unlock_irqrestore(nm->common.lock, flags); + + ccu_frac_helper_enable(&nm->common, &nm->frac); + + return ccu_frac_helper_set_rate(&nm->common, &nm->frac, + rate, nm->lock); + } else { ccu_frac_helper_disable(&nm->common, &nm->frac); + } _nm.min_n = nm->n.min ?: 1; _nm.max_n = nm->n.max ?: 1 << nm->n.width; diff --git a/drivers/clk/sunxi/clk-sun8i-bus-gates.c b/drivers/clk/sunxi/clk-sun8i-bus-gates.c index 63fdb790df29..bee305bdddbe 100644 --- a/drivers/clk/sunxi/clk-sun8i-bus-gates.c +++ b/drivers/clk/sunxi/clk-sun8i-bus-gates.c @@ -78,6 +78,10 @@ static void __init sun8i_h3_bus_gates_init(struct device_node *node) clk_parent = APB1; else if (index >= 96 && index <= 127) clk_parent = APB2; + else { + WARN_ON(true); + continue; + } clk_reg = reg + 4 * (index / 32); clk_bit = index % 32; diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index f2c9274b8bd5..aa4add580516 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -666,15 +666,14 @@ static struct clk * __init sunxi_mux_clk_setup(struct device_node *node, reg = of_iomap(node, 0); if (!reg) { - pr_err("Could not map registers for mux-clk: %s\n", - of_node_full_name(node)); + pr_err("Could not map registers for mux-clk: %pOF\n", node); return NULL; } i = of_clk_parent_fill(node, parents, SUNXI_MAX_PARENTS); if (of_property_read_string(node, "clock-output-names", &clk_name)) { - pr_err("%s: could not read clock-output-names from \"%s\"\n", - __func__, of_node_full_name(node)); + pr_err("%s: could not read clock-output-names from \"%pOF\"\n", + __func__, node); goto out_unmap; } @@ -797,16 +796,15 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, reg = of_iomap(node, 0); if (!reg) { - pr_err("Could not map registers for mux-clk: %s\n", - of_node_full_name(node)); + pr_err("Could not map registers for mux-clk: %pOF\n", node); return; } clk_parent = of_clk_get_parent_name(node, 0); if (of_property_read_string(node, "clock-output-names", &clk_name)) { - pr_err("%s: could not read clock-output-names from \"%s\"\n", - __func__, of_node_full_name(node)); + pr_err("%s: could not read clock-output-names from \"%pOF\"\n", + __func__, node); goto out_unmap; } @@ -1010,8 +1008,7 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, reg = of_iomap(node, 0); if (!reg) { - pr_err("Could not map registers for divs-clk: %s\n", - of_node_full_name(node)); + pr_err("Could not map registers for divs-clk: %pOF\n", node); return NULL; } diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 74e7544f861b..11a5066e5c27 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -378,7 +378,7 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, err = of_property_read_u32(node, "clock-frequency", &tmp); if (err) { - pr_err("timing %s: failed to read rate\n", node->full_name); + pr_err("timing %pOF: failed to read rate\n", node); return err; } @@ -386,8 +386,7 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, err = of_property_read_u32(node, "nvidia,parent-clock-frequency", &tmp); if (err) { - pr_err("timing %s: failed to read parent rate\n", - node->full_name); + pr_err("timing %pOF: failed to read parent rate\n", node); return err; } @@ -395,8 +394,7 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, timing->parent = of_clk_get_by_name(node, "emc-parent"); if (IS_ERR(timing->parent)) { - pr_err("timing %s: failed to get parent clock\n", - node->full_name); + pr_err("timing %pOF: failed to get parent clock\n", node); return PTR_ERR(timing->parent); } @@ -409,8 +407,8 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, } } if (timing->parent_index == 0xff) { - pr_err("timing %s: %s is not a valid parent\n", - node->full_name, __clk_get_name(timing->parent)); + pr_err("timing %pOF: %s is not a valid parent\n", + node, __clk_get_name(timing->parent)); clk_put(timing->parent); return -EINVAL; } diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 159a854779e6..7c369e21c91c 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -363,7 +363,7 @@ static void _clk_pll_enable(struct clk_hw *hw) val = pll_readl(pll->params->iddq_reg, pll); val &= ~BIT(pll->params->iddq_bit_idx); pll_writel(val, pll->params->iddq_reg, pll); - udelay(2); + udelay(5); } if (pll->params->reset_reg) { @@ -418,6 +418,26 @@ static void _clk_pll_disable(struct clk_hw *hw) } } +static void pll_clk_start_ss(struct tegra_clk_pll *pll) +{ + if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) { + u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll); + + val |= pll->params->ssc_ctrl_en_mask; + pll_writel(val, pll->params->ssc_ctrl_reg, pll); + } +} + +static void pll_clk_stop_ss(struct tegra_clk_pll *pll) +{ + if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) { + u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll); + + val &= ~pll->params->ssc_ctrl_en_mask; + pll_writel(val, pll->params->ssc_ctrl_reg, pll); + } +} + static int clk_pll_enable(struct clk_hw *hw) { struct tegra_clk_pll *pll = to_clk_pll(hw); @@ -431,6 +451,8 @@ static int clk_pll_enable(struct clk_hw *hw) ret = clk_pll_wait_for_lock(pll); + pll_clk_start_ss(pll); + if (pll->lock) spin_unlock_irqrestore(pll->lock, flags); @@ -445,6 +467,8 @@ static void clk_pll_disable(struct clk_hw *hw) if (pll->lock) spin_lock_irqsave(pll->lock, flags); + pll_clk_stop_ss(pll); + _clk_pll_disable(hw); if (pll->lock) @@ -666,6 +690,8 @@ static void _get_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_params *params = pll->params; struct div_nmp *div_nmp = params->div_nmp; + *cfg = (struct tegra_clk_pll_freq_table) { }; + if ((params->flags & (TEGRA_PLLM | TEGRA_PLLMB)) && (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) { @@ -716,26 +742,6 @@ static void _update_pll_cpcon(struct tegra_clk_pll *pll, pll_writel_misc(val, pll); } -static void pll_clk_start_ss(struct tegra_clk_pll *pll) -{ - if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) { - u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll); - - val |= pll->params->ssc_ctrl_en_mask; - pll_writel(val, pll->params->ssc_ctrl_reg, pll); - } -} - -static void pll_clk_stop_ss(struct tegra_clk_pll *pll) -{ - if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) { - u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll); - - val &= ~pll->params->ssc_ctrl_en_mask; - pll_writel(val, pll->params->ssc_ctrl_reg, pll); - } -} - static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate) { @@ -2251,7 +2257,7 @@ tegra_clk_register_pllu_tegra114(const char *name, const char *parent_name, } #endif -#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) +#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) || defined(CONFIG_ARCH_TEGRA_210_SOC) static const struct clk_ops tegra_clk_pllss_ops = { .is_enabled = clk_pll_is_enabled, .enable = clk_pll_enable, @@ -2349,7 +2355,6 @@ struct clk *tegra_clk_register_pllre_tegra210(const char *name, struct tegra_clk_pll_params *pll_params, spinlock_t *lock, unsigned long parent_rate) { - u32 val; struct tegra_clk_pll *pll; struct clk *clk; @@ -2363,26 +2368,8 @@ struct clk *tegra_clk_register_pllre_tegra210(const char *name, if (IS_ERR(pll)) return ERR_CAST(pll); - /* program minimum rate by default */ - - val = pll_readl_base(pll); - if (val & PLL_BASE_ENABLE) - WARN_ON(readl_relaxed(clk_base + pll_params->iddq_reg) & - BIT(pll_params->iddq_bit_idx)); - else { - val = 0x4 << divm_shift(pll); - val |= 0x41 << divn_shift(pll); - pll_writel_base(val, pll); - } - - /* disable lock override */ - - val = pll_readl_misc(pll); - val &= ~BIT(29); - pll_writel_misc(val, pll); - clk = _tegra_clk_register_pll(pll, name, parent_name, flags, - &tegra_clk_pllre_ops); + &tegra_clk_pll_ops); if (IS_ERR(clk)) kfree(pll); @@ -2604,46 +2591,6 @@ struct clk *tegra_clk_register_pllc_tegra210(const char *name, return clk; } -struct clk *tegra_clk_register_pllxc_tegra210(const char *name, - const char *parent_name, void __iomem *clk_base, - void __iomem *pmc, unsigned long flags, - struct tegra_clk_pll_params *pll_params, - spinlock_t *lock) -{ - struct tegra_clk_pll *pll; - struct clk *clk, *parent; - unsigned long parent_rate; - - parent = __clk_lookup(parent_name); - if (!parent) { - WARN(1, "parent clk %s of %s must be registered first\n", - name, parent_name); - return ERR_PTR(-EINVAL); - } - - if (!pll_params->pdiv_tohw) - return ERR_PTR(-EINVAL); - - parent_rate = clk_get_rate(parent); - - pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate); - - if (pll_params->adjust_vco) - pll_params->vco_min = pll_params->adjust_vco(pll_params, - parent_rate); - - pll = _tegra_init_pll(clk_base, pmc, pll_params, lock); - if (IS_ERR(pll)) - return ERR_CAST(pll); - - clk = _tegra_clk_register_pll(pll, name, parent_name, flags, - &tegra_clk_pll_ops); - if (IS_ERR(clk)) - kfree(pll); - - return clk; -} - struct clk *tegra_clk_register_pllss_tegra210(const char *name, const char *parent_name, void __iomem *clk_base, unsigned long flags, @@ -2652,10 +2599,8 @@ struct clk *tegra_clk_register_pllss_tegra210(const char *name, { struct tegra_clk_pll *pll; struct clk *clk, *parent; - struct tegra_clk_pll_freq_table cfg; unsigned long parent_rate; u32 val; - int i; if (!pll_params->div_nmp) return ERR_PTR(-EINVAL); @@ -2667,13 +2612,11 @@ struct clk *tegra_clk_register_pllss_tegra210(const char *name, return ERR_PTR(-EINVAL); } - pll = _tegra_init_pll(clk_base, NULL, pll_params, lock); - if (IS_ERR(pll)) - return ERR_CAST(pll); - - val = pll_readl_base(pll); - val &= ~PLLSS_REF_SRC_SEL_MASK; - pll_writel_base(val, pll); + val = readl_relaxed(clk_base + pll_params->base_reg); + if (val & PLLSS_REF_SRC_SEL_MASK) { + WARN(1, "not supported reference clock for %s\n", name); + return ERR_PTR(-EINVAL); + } parent_rate = clk_get_rate(parent); @@ -2683,36 +2626,10 @@ struct clk *tegra_clk_register_pllss_tegra210(const char *name, pll_params->vco_min = pll_params->adjust_vco(pll_params, parent_rate); - /* initialize PLL to minimum rate */ - - cfg.m = _pll_fixed_mdiv(pll_params, parent_rate); - cfg.n = cfg.m * pll_params->vco_min / parent_rate; - - for (i = 0; pll_params->pdiv_tohw[i].pdiv; i++) - ; - if (!i) { - kfree(pll); - return ERR_PTR(-EINVAL); - } - - cfg.p = pll_params->pdiv_tohw[i-1].hw_val; - - _update_pll_mnp(pll, &cfg); - - pll_writel_misc(PLLSS_MISC_DEFAULT, pll); - - val = pll_readl_base(pll); - if (val & PLL_BASE_ENABLE) { - if (val & BIT(pll_params->iddq_bit_idx)) { - WARN(1, "%s is on but IDDQ set\n", name); - kfree(pll); - return ERR_PTR(-EINVAL); - } - } else - val |= BIT(pll_params->iddq_bit_idx); - - val &= ~PLLSS_LOCK_OVERRIDE; - pll_writel_base(val, pll); + pll_params->flags |= TEGRA_PLL_BYPASS; + pll = _tegra_init_pll(clk_base, NULL, pll_params, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); clk = _tegra_clk_register_pll(pll, name, parent_name, flags, &tegra_clk_pll_ops); diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index 294bfe40a4f5..848255cc0209 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -216,7 +216,8 @@ _clk_num, _clk_id) \ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\ 30, MASK(2), 0, 0, 16, 0, TEGRA_DIVIDER_ROUND_UP,\ - _clk_num, 0, _clk_id, _parents##_idx, 0, NULL) + _clk_num, TEGRA_PERIPH_ON_APB, _clk_id, \ + _parents##_idx, 0, NULL) #define XUSB(_name, _parents, _offset, \ _clk_num, _gate_flags, _clk_id) \ diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c index 474de0f0c26d..4f6fd307cb70 100644 --- a/drivers/clk/tegra/clk-tegra-super-gen4.c +++ b/drivers/clk/tegra/clk-tegra-super-gen4.c @@ -232,8 +232,15 @@ static void __init tegra_super_clk_init(void __iomem *clk_base, if (!dt_clk) return; - clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, - pmc_base, CLK_IGNORE_UNUSED, params, NULL); +#if defined(CONFIG_ARCH_TEGRA_210_SOC) + if (gen_info->gen == gen5) + clk = tegra_clk_register_pllc_tegra210("pll_x", "pll_ref", + clk_base, pmc_base, CLK_IGNORE_UNUSED, params, NULL); + else +#endif + clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, + pmc_base, CLK_IGNORE_UNUSED, params, NULL); + *dt_clk = clk; /* PLLX_OUT0 */ diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 1024e853ea65..6d7a613f2656 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -146,7 +146,7 @@ #define PLLD_SDM_EN_MASK BIT(16) #define PLLD2_SDM_EN_MASK BIT(31) -#define PLLD2_SSC_EN_MASK BIT(30) +#define PLLD2_SSC_EN_MASK 0 #define PLLDP_SS_CFG 0x598 #define PLLDP_SDM_EN_MASK BIT(31) @@ -241,6 +241,9 @@ #define PLL_SDM_COEFF BIT(13) #define sdin_din_to_data(din) ((u16)((din) ? : 0xFFFFU)) #define sdin_data_to_din(dat) (((dat) == 0xFFFFU) ? 0 : (s16)dat) +/* This macro returns ndiv effective scaled to SDM range */ +#define sdin_get_n_eff(cfg) ((cfg)->n * PLL_SDM_COEFF + ((cfg)->sdm_data ? \ + (PLL_SDM_COEFF/2 + sdin_data_to_din((cfg)->sdm_data)) : 0)) /* Tegra CPU clock and reset control regs */ #define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470 @@ -715,8 +718,6 @@ static void plldss_defaults(const char *pll_name, struct tegra_clk_pll *plldss, plldss->params->defaults_set = true; if (val & PLL_ENABLE) { - pr_warn("%s already enabled. Postponing set full defaults\n", - pll_name); /* * PLL is ON: check if defaults already set, then set those @@ -755,6 +756,10 @@ static void plldss_defaults(const char *pll_name, struct tegra_clk_pll *plldss, (~PLLDSS_MISC1_CFG_EN_SDM)); } + if (!plldss->params->defaults_set) + pr_warn("%s already enabled. Postponing set full defaults\n", + pll_name); + /* Enable lock detect */ if (val & PLLDSS_BASE_LOCK_OVERRIDE) { val &= ~PLLDSS_BASE_LOCK_OVERRIDE; @@ -1288,8 +1293,7 @@ static int tegra210_pll_fixed_mdiv_cfg(struct clk_hw *hw, s -= PLL_SDM_COEFF / 2; cfg->sdm_data = sdin_din_to_data(s); } - cfg->output_rate *= cfg->n * PLL_SDM_COEFF + PLL_SDM_COEFF/2 + - sdin_data_to_din(cfg->sdm_data); + cfg->output_rate *= sdin_get_n_eff(cfg); cfg->output_rate /= p * cfg->m * PLL_SDM_COEFF; } else { cfg->output_rate *= cfg->n; @@ -1314,8 +1318,7 @@ static int tegra210_pll_fixed_mdiv_cfg(struct clk_hw *hw, */ static void tegra210_clk_pll_set_gain(struct tegra_clk_pll_freq_table *cfg) { - cfg->n = cfg->n * PLL_SDM_COEFF + PLL_SDM_COEFF/2 + - sdin_data_to_din(cfg->sdm_data); + cfg->n = sdin_get_n_eff(cfg); cfg->m *= PLL_SDM_COEFF; } @@ -2204,7 +2207,6 @@ static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = { [tegra_clk_gpu] = { .dt_id = TEGRA210_CLK_GPU, .present = true }, [tegra_clk_pll_g_ref] = { .dt_id = TEGRA210_CLK_PLL_G_REF, .present = true, }, [tegra_clk_uartb_8] = { .dt_id = TEGRA210_CLK_UARTB, .present = true }, - [tegra_clk_vfir] = { .dt_id = TEGRA210_CLK_VFIR, .present = true }, [tegra_clk_spdif_in_8] = { .dt_id = TEGRA210_CLK_SPDIF_IN, .present = true }, [tegra_clk_spdif_out] = { .dt_id = TEGRA210_CLK_SPDIF_OUT, .present = true }, [tegra_clk_vi_10] = { .dt_id = TEGRA210_CLK_VI, .present = true }, @@ -2470,15 +2472,14 @@ static void tegra210_utmi_param_configure(void) reg |= UTMIP_PLL_CFG2_STABLE_COUNT(utmi_parameters[i].stable_count); reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); - reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(utmi_parameters[i].active_delay_count); writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2); /* Program UTMIP PLL delay and oscillator frequency counts */ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); - reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); + reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(utmi_parameters[i].enable_delay_count); @@ -2494,7 +2495,8 @@ static void tegra210_utmi_param_configure(void) reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; reg |= UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); - udelay(1); + + udelay(20); /* Enable samplers for SNPS, XUSB_HOST, XUSB_DEV */ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2); @@ -2552,6 +2554,7 @@ static int tegra210_enable_pllu(void) reg = readl_relaxed(clk_base + pllu.params->ext_misc_reg[0]); reg &= ~BIT(pllu.params->iddq_bit_idx); writel_relaxed(reg, clk_base + pllu.params->ext_misc_reg[0]); + udelay(5); reg = readl_relaxed(clk_base + PLLU_BASE); reg &= ~GENMASK(20, 0); @@ -2559,6 +2562,7 @@ static int tegra210_enable_pllu(void) reg |= fentry->n << 8; reg |= fentry->p << 16; writel(reg, clk_base + PLLU_BASE); + udelay(1); reg |= PLL_ENABLE; writel(reg, clk_base + PLLU_BASE); @@ -2699,7 +2703,7 @@ static void __init tegra210_pll_init(void __iomem *clk_base, struct clk *clk; /* PLLC */ - clk = tegra_clk_register_pllxc_tegra210("pll_c", "pll_ref", clk_base, + clk = tegra_clk_register_pllc_tegra210("pll_c", "pll_ref", clk_base, pmc, 0, &pll_c_params, NULL); if (!WARN_ON(IS_ERR(clk))) clk_register_clkdev(clk, "pll_c", NULL); @@ -2798,14 +2802,14 @@ static void __init tegra210_pll_init(void __iomem *clk_base, /* PLLU_60M */ clk = clk_register_gate(NULL, "pll_u_60M", "pll_u_out2", CLK_SET_RATE_PARENT, clk_base + PLLU_BASE, - 23, 0, NULL); + 23, 0, &pll_u_lock); clk_register_clkdev(clk, "pll_u_60M", NULL); clks[TEGRA210_CLK_PLL_U_60M] = clk; /* PLLU_48M */ clk = clk_register_gate(NULL, "pll_u_48M", "pll_u_out1", CLK_SET_RATE_PARENT, clk_base + PLLU_BASE, - 25, 0, NULL); + 25, 0, &pll_u_lock); clk_register_clkdev(clk, "pll_u_48M", NULL); clks[TEGRA210_CLK_PLL_U_48M] = clk; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 945b07093afa..872f1189ad7f 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -362,12 +362,6 @@ struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, struct tegra_clk_pll_params *pll_params, spinlock_t *lock); -struct clk *tegra_clk_register_pllxc_tegra210(const char *name, - const char *parent_name, void __iomem *clk_base, - void __iomem *pmc, unsigned long flags, - struct tegra_clk_pll_params *pll_params, - spinlock_t *lock); - struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c index 255cafb18336..d6036c788fab 100644 --- a/drivers/clk/ti/adpll.c +++ b/drivers/clk/ti/adpll.c @@ -222,7 +222,7 @@ static int ti_adpll_setup_clock(struct ti_adpll_data *d, struct clk *clock, /* Separate con_id in format "pll040dcoclkldo" to fit MAX_CON_ID */ postfix = strrchr(name, '.'); - if (strlen(postfix) > 1) { + if (postfix && strlen(postfix) > 1) { if (strlen(postfix) > ADPLL_MAX_CON_ID) dev_warn(d->dev, "clock %s con_id lookup may fail\n", name); @@ -486,7 +486,7 @@ static u8 ti_adpll_get_parent(struct clk_hw *hw) return 0; } -static struct clk_ops ti_adpll_ops = { +static const struct clk_ops ti_adpll_ops = { .prepare = ti_adpll_prepare, .unprepare = ti_adpll_unprepare, .is_prepared = ti_adpll_is_prepared, diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c index 06f486b3488c..83b148f8037c 100644 --- a/drivers/clk/ti/apll.c +++ b/drivers/clk/ti/apll.c @@ -304,7 +304,7 @@ static void omap2_apll_disable(struct clk_hw *hw) ti_clk_ll_ops->clk_writel(v, &ad->control_reg); } -static struct clk_ops omap2_apll_ops = { +static const struct clk_ops omap2_apll_ops = { .enable = &omap2_apll_enable, .disable = &omap2_apll_disable, .is_enabled = &omap2_apll_is_enabled, diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c index fbedc6a9fed0..07a805125e98 100644 --- a/drivers/clk/ti/clockdomain.c +++ b/drivers/clk/ti/clockdomain.c @@ -138,8 +138,8 @@ static void __init of_ti_clockdomain_setup(struct device_node *node) for (i = 0; i < num_clks; i++) { clk = of_clk_get(node, i); if (IS_ERR(clk)) { - pr_err("%s: Failed get %s' clock nr %d (%ld)\n", - __func__, node->full_name, i, PTR_ERR(clk)); + pr_err("%s: Failed get %pOF' clock nr %d (%ld)\n", + __func__, node, i, PTR_ERR(clk)); continue; } clk_hw = __clk_get_hw(clk); diff --git a/drivers/clk/ti/fapll.c b/drivers/clk/ti/fapll.c index 66a0d0ed8b55..071af44b1ba8 100644 --- a/drivers/clk/ti/fapll.c +++ b/drivers/clk/ti/fapll.c @@ -268,7 +268,7 @@ static int ti_fapll_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static struct clk_ops ti_fapll_ops = { +static const struct clk_ops ti_fapll_ops = { .enable = ti_fapll_enable, .disable = ti_fapll_disable, .is_enabled = ti_fapll_is_enabled, @@ -478,7 +478,7 @@ static int ti_fapll_synth_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static struct clk_ops ti_fapll_synt_ops = { +static const struct clk_ops ti_fapll_synt_ops = { .enable = ti_fapll_synth_enable, .disable = ti_fapll_synth_disable, .is_enabled = ti_fapll_synth_is_enabled, diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c index 2cf386347f0c..e09f3dd46318 100644 --- a/drivers/clk/uniphier/clk-uniphier-core.c +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -111,10 +111,6 @@ static int uniphier_clk_remove(struct platform_device *pdev) static const struct of_device_id uniphier_clk_match[] = { /* System clock */ { - .compatible = "socionext,uniphier-sld3-clock", - .data = uniphier_sld3_sys_clk_data, - }, - { .compatible = "socionext,uniphier-ld4-clock", .data = uniphier_ld4_sys_clk_data, }, @@ -142,22 +138,22 @@ static const struct of_device_id uniphier_clk_match[] = { .compatible = "socionext,uniphier-ld20-clock", .data = uniphier_ld20_sys_clk_data, }, - /* Media I/O clock, SD clock */ { - .compatible = "socionext,uniphier-sld3-mio-clock", - .data = uniphier_sld3_mio_clk_data, + .compatible = "socionext,uniphier-pxs3-clock", + .data = uniphier_pxs3_sys_clk_data, }, + /* Media I/O clock, SD clock */ { .compatible = "socionext,uniphier-ld4-mio-clock", - .data = uniphier_sld3_mio_clk_data, + .data = uniphier_ld4_mio_clk_data, }, { .compatible = "socionext,uniphier-pro4-mio-clock", - .data = uniphier_sld3_mio_clk_data, + .data = uniphier_ld4_mio_clk_data, }, { .compatible = "socionext,uniphier-sld8-mio-clock", - .data = uniphier_sld3_mio_clk_data, + .data = uniphier_ld4_mio_clk_data, }, { .compatible = "socionext,uniphier-pro5-sd-clock", @@ -169,12 +165,16 @@ static const struct of_device_id uniphier_clk_match[] = { }, { .compatible = "socionext,uniphier-ld11-mio-clock", - .data = uniphier_sld3_mio_clk_data, + .data = uniphier_ld4_mio_clk_data, }, { .compatible = "socionext,uniphier-ld20-sd-clock", .data = uniphier_pro5_sd_clk_data, }, + { + .compatible = "socionext,uniphier-pxs3-sd-clock", + .data = uniphier_pro5_sd_clk_data, + }, /* Peripheral clock */ { .compatible = "socionext,uniphier-ld4-peri-clock", @@ -204,6 +204,10 @@ static const struct of_device_id uniphier_clk_match[] = { .compatible = "socionext,uniphier-ld20-peri-clock", .data = uniphier_pro4_peri_clk_data, }, + { + .compatible = "socionext,uniphier-pxs3-peri-clock", + .data = uniphier_pro4_peri_clk_data, + }, { /* sentinel */ } }; diff --git a/drivers/clk/uniphier/clk-uniphier-mio.c b/drivers/clk/uniphier/clk-uniphier-mio.c index 218d20f099ce..16e4d303f535 100644 --- a/drivers/clk/uniphier/clk-uniphier-mio.c +++ b/drivers/clk/uniphier/clk-uniphier-mio.c @@ -76,7 +76,7 @@ #define UNIPHIER_MIO_CLK_DMAC(idx) \ UNIPHIER_CLK_GATE("miodmac", (idx), "stdmac", 0x20, 25) -const struct uniphier_clk_data uniphier_sld3_mio_clk_data[] = { +const struct uniphier_clk_data uniphier_ld4_mio_clk_data[] = { UNIPHIER_MIO_CLK_SD_FIXED, UNIPHIER_MIO_CLK_SD(0, 0), UNIPHIER_MIO_CLK_SD(1, 1), @@ -85,11 +85,9 @@ const struct uniphier_clk_data uniphier_sld3_mio_clk_data[] = { UNIPHIER_MIO_CLK_USB2(8, 0), UNIPHIER_MIO_CLK_USB2(9, 1), UNIPHIER_MIO_CLK_USB2(10, 2), - UNIPHIER_MIO_CLK_USB2(11, 3), UNIPHIER_MIO_CLK_USB2_PHY(12, 0), UNIPHIER_MIO_CLK_USB2_PHY(13, 1), UNIPHIER_MIO_CLK_USB2_PHY(14, 2), - UNIPHIER_MIO_CLK_USB2_PHY(15, 3), { /* sentinel */ } }; diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c index ad0218182a9f..0e396f3da526 100644 --- a/drivers/clk/uniphier/clk-uniphier-sys.c +++ b/drivers/clk/uniphier/clk-uniphier-sys.c @@ -17,7 +17,7 @@ #include "clk-uniphier.h" -#define UNIPHIER_SLD3_SYS_CLK_SD \ +#define UNIPHIER_LD4_SYS_CLK_SD \ UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 8), \ UNIPHIER_CLK_FACTOR("sd-133m", -1, "vpll27a", 1, 2) @@ -30,7 +30,7 @@ UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 15) /* Denali driver requires clk_x rate (clk: 50MHz, clk_x & ecc_clk: 200MHz) */ -#define UNIPHIER_SLD3_SYS_CLK_NAND(idx) \ +#define UNIPHIER_LD4_SYS_CLK_NAND(idx) \ UNIPHIER_CLK_FACTOR("nand-200m", -1, "spll", 1, 8), \ UNIPHIER_CLK_GATE("nand", (idx), "nand-200m", 0x2104, 2) @@ -45,7 +45,7 @@ #define UNIPHIER_LD11_SYS_CLK_EMMC(idx) \ UNIPHIER_CLK_GATE("emmc", (idx), NULL, 0x210c, 2) -#define UNIPHIER_SLD3_SYS_CLK_STDMAC(idx) \ +#define UNIPHIER_LD4_SYS_CLK_STDMAC(idx) \ UNIPHIER_CLK_GATE("stdmac", (idx), NULL, 0x2104, 10) #define UNIPHIER_LD11_SYS_CLK_STDMAC(idx) \ @@ -57,19 +57,23 @@ #define UNIPHIER_PRO4_SYS_CLK_USB3(idx, ch) \ UNIPHIER_CLK_GATE("usb3" #ch, (idx), NULL, 0x2104, 16 + (ch)) -const struct uniphier_clk_data uniphier_sld3_sys_clk_data[] = { - UNIPHIER_CLK_FACTOR("spll", -1, "ref", 65, 1), /* 1597.44 MHz */ - UNIPHIER_CLK_FACTOR("upll", -1, "ref", 6000, 512), /* 288 MHz */ - UNIPHIER_CLK_FACTOR("a2pll", -1, "ref", 24, 1), /* 589.824 MHz */ - UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */ - UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16), - UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), - UNIPHIER_SLD3_SYS_CLK_NAND(2), - UNIPHIER_SLD3_SYS_CLK_SD, - UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), - { /* sentinel */ } -}; +#define UNIPHIER_LD11_SYS_CLK_AIO(idx) \ + UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 10), \ + UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2108, 0) + +#define UNIPHIER_LD11_SYS_CLK_EVEA(idx) \ + UNIPHIER_CLK_FACTOR("evea-io100m", -1, "spll", 1, 20), \ + UNIPHIER_CLK_GATE("evea", (idx), "evea-io100m", 0x2108, 1) + +#define UNIPHIER_LD11_SYS_CLK_EXIV(idx) \ + UNIPHIER_CLK_FACTOR("exiv-io200m", -1, "spll", 1, 10), \ + UNIPHIER_CLK_GATE("exiv", (idx), "exiv-io200m", 0x2110, 2) + +#define UNIPHIER_PRO4_SYS_CLK_ETHER(idx) \ + UNIPHIER_CLK_GATE("ether", (idx), NULL, 0x2104, 12) + +#define UNIPHIER_LD11_SYS_CLK_ETHER(idx) \ + UNIPHIER_CLK_GATE("ether", (idx), NULL, 0x210c, 6) const struct uniphier_clk_data uniphier_ld4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("spll", -1, "ref", 65, 1), /* 1597.44 MHz */ @@ -78,10 +82,10 @@ const struct uniphier_clk_data uniphier_ld4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), - UNIPHIER_SLD3_SYS_CLK_NAND(2), - UNIPHIER_SLD3_SYS_CLK_SD, + UNIPHIER_LD4_SYS_CLK_NAND(2), + UNIPHIER_LD4_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ + UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ { /* sentinel */ } }; @@ -92,10 +96,11 @@ const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 8), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 32), - UNIPHIER_SLD3_SYS_CLK_NAND(2), - UNIPHIER_SLD3_SYS_CLK_SD, + UNIPHIER_LD4_SYS_CLK_NAND(2), + UNIPHIER_LD4_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */ + UNIPHIER_PRO4_SYS_CLK_ETHER(6), + UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */ UNIPHIER_PRO4_SYS_CLK_GIO(12), /* Ether, SATA, USB3 */ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), @@ -108,10 +113,10 @@ const struct uniphier_clk_data uniphier_sld8_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 20), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), - UNIPHIER_SLD3_SYS_CLK_NAND(2), - UNIPHIER_SLD3_SYS_CLK_SD, + UNIPHIER_LD4_SYS_CLK_NAND(2), + UNIPHIER_LD4_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ + UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ { /* sentinel */ } }; @@ -123,7 +128,7 @@ const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), UNIPHIER_PRO5_SYS_CLK_NAND(2), UNIPHIER_PRO5_SYS_CLK_SD, - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC */ + UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC */ UNIPHIER_PRO4_SYS_CLK_GIO(12), /* PCIe, USB3 */ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), @@ -136,7 +141,8 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), UNIPHIER_PRO5_SYS_CLK_NAND(2), UNIPHIER_PRO5_SYS_CLK_SD, - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, RLE */ + UNIPHIER_PRO4_SYS_CLK_ETHER(6), + UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC, RLE */ /* GIO is always clock-enabled: no function for 0x2104 bit6 */ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), @@ -156,8 +162,12 @@ const struct uniphier_clk_data uniphier_ld11_sys_clk_data[] = { UNIPHIER_LD11_SYS_CLK_NAND(2), UNIPHIER_LD11_SYS_CLK_EMMC(4), /* Index 5 reserved for eMMC PHY */ + UNIPHIER_LD11_SYS_CLK_ETHER(6), UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC, MIO */ UNIPHIER_CLK_FACTOR("usb2", -1, "ref", 24, 25), + UNIPHIER_LD11_SYS_CLK_AIO(40), + UNIPHIER_LD11_SYS_CLK_EVEA(41), + UNIPHIER_LD11_SYS_CLK_EXIV(42), /* CPU gears */ UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8), UNIPHIER_CLK_DIV4("mpll", 2, 3, 4, 8), @@ -185,6 +195,7 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { UNIPHIER_LD11_SYS_CLK_EMMC(4), /* Index 5 reserved for eMMC PHY */ UNIPHIER_LD20_SYS_CLK_SD, + UNIPHIER_LD11_SYS_CLK_ETHER(6), UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC */ /* GIO is always clock-enabled: no function for 0x210c bit5 */ /* @@ -194,6 +205,9 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { UNIPHIER_CLK_GATE("usb30", 14, NULL, 0x210c, 14), UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 12), UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 13), + UNIPHIER_LD11_SYS_CLK_AIO(40), + UNIPHIER_LD11_SYS_CLK_EVEA(41), + UNIPHIER_LD11_SYS_CLK_EXIV(42), /* CPU gears */ UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8), UNIPHIER_CLK_DIV4("spll", 2, 3, 4, 8), @@ -209,3 +223,33 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { "spll/4", "spll/8", "s2pll/4", "s2pll/8"), { /* sentinel */ } }; + +const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("cpll", -1, "ref", 104, 1), /* ARM: 2600 MHz */ + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 80, 1), /* 2000 MHz */ + UNIPHIER_CLK_FACTOR("s2pll", -1, "ref", 88, 1), /* IPP: 2400 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), + UNIPHIER_LD20_SYS_CLK_SD, + UNIPHIER_LD11_SYS_CLK_NAND(2), + UNIPHIER_LD11_SYS_CLK_EMMC(4), + UNIPHIER_CLK_GATE("usb30", 12, NULL, 0x2104, 4), /* =GIO0 */ + UNIPHIER_CLK_GATE("usb31-0", 13, NULL, 0x2104, 5), /* =GIO1 */ + UNIPHIER_CLK_GATE("usb31-1", 14, NULL, 0x2104, 6), /* =GIO1-1 */ + UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 16), + UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 18), + UNIPHIER_CLK_GATE("usb30-phy2", 18, NULL, 0x210c, 20), + UNIPHIER_CLK_GATE("usb31-phy0", 20, NULL, 0x210c, 17), + UNIPHIER_CLK_GATE("usb31-phy1", 21, NULL, 0x210c, 19), + /* CPU gears */ + UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8), + UNIPHIER_CLK_DIV4("spll", 2, 3, 4, 8), + UNIPHIER_CLK_DIV4("s2pll", 2, 3, 4, 8), + UNIPHIER_CLK_CPUGEAR("cpu-ca53", 33, 0x8080, 0xf, 8, + "cpll/2", "spll/2", "cpll/3", "spll/3", + "spll/4", "spll/8", "cpll/4", "cpll/8"), + UNIPHIER_CLK_CPUGEAR("cpu-ipp", 34, 0x8100, 0xf, 8, + "s2pll/2", "spll/2", "s2pll/3", "spll/3", + "spll/4", "spll/8", "s2pll/4", "s2pll/8"), + { /* sentinel */ } +}; diff --git a/drivers/clk/uniphier/clk-uniphier.h b/drivers/clk/uniphier/clk-uniphier.h index 01c16ecec48f..d10a009ada96 100644 --- a/drivers/clk/uniphier/clk-uniphier.h +++ b/drivers/clk/uniphier/clk-uniphier.h @@ -147,7 +147,6 @@ struct clk_hw *uniphier_clk_register_mux(struct device *dev, const char *name, const struct uniphier_clk_mux_data *data); -extern const struct uniphier_clk_data uniphier_sld3_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_ld4_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_pro4_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_sld8_sys_clk_data[]; @@ -155,7 +154,8 @@ extern const struct uniphier_clk_data uniphier_pro5_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_ld11_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_ld20_sys_clk_data[]; -extern const struct uniphier_clk_data uniphier_sld3_mio_clk_data[]; +extern const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_ld4_mio_clk_data[]; extern const struct uniphier_clk_data uniphier_pro5_sd_clk_data[]; extern const struct uniphier_clk_data uniphier_ld4_peri_clk_data[]; extern const struct uniphier_clk_data uniphier_pro4_peri_clk_data[]; diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c index 0e950769ed03..f50592775c9d 100644 --- a/drivers/clk/ux500/clk-prcc.c +++ b/drivers/clk/ux500/clk-prcc.c @@ -79,13 +79,13 @@ static int clk_prcc_is_enabled(struct clk_hw *hw) return clk->is_enabled; } -static struct clk_ops clk_prcc_pclk_ops = { +static const struct clk_ops clk_prcc_pclk_ops = { .enable = clk_prcc_pclk_enable, .disable = clk_prcc_pclk_disable, .is_enabled = clk_prcc_is_enabled, }; -static struct clk_ops clk_prcc_kclk_ops = { +static const struct clk_ops clk_prcc_kclk_ops = { .enable = clk_prcc_kclk_enable, .disable = clk_prcc_kclk_disable, .is_enabled = clk_prcc_is_enabled, @@ -96,7 +96,7 @@ static struct clk *clk_reg_prcc(const char *name, resource_size_t phy_base, u32 cg_sel, unsigned long flags, - struct clk_ops *clk_prcc_ops) + const struct clk_ops *clk_prcc_ops) { struct clk_prcc *clk; struct clk_init_data clk_prcc_init; diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 7f343821f4e4..6e3e16b2e5ca 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -186,7 +186,7 @@ static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) clk->is_prepared = 0; } -static struct clk_ops clk_prcmu_scalable_ops = { +static const struct clk_ops clk_prcmu_scalable_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, .is_prepared = clk_prcmu_is_prepared, @@ -198,7 +198,7 @@ static struct clk_ops clk_prcmu_scalable_ops = { .set_rate = clk_prcmu_set_rate, }; -static struct clk_ops clk_prcmu_gate_ops = { +static const struct clk_ops clk_prcmu_gate_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, .is_prepared = clk_prcmu_is_prepared, @@ -208,19 +208,19 @@ static struct clk_ops clk_prcmu_gate_ops = { .recalc_rate = clk_prcmu_recalc_rate, }; -static struct clk_ops clk_prcmu_scalable_rate_ops = { +static const struct clk_ops clk_prcmu_scalable_rate_ops = { .is_enabled = clk_prcmu_is_enabled, .recalc_rate = clk_prcmu_recalc_rate, .round_rate = clk_prcmu_round_rate, .set_rate = clk_prcmu_set_rate, }; -static struct clk_ops clk_prcmu_rate_ops = { +static const struct clk_ops clk_prcmu_rate_ops = { .is_enabled = clk_prcmu_is_enabled, .recalc_rate = clk_prcmu_recalc_rate, }; -static struct clk_ops clk_prcmu_opp_gate_ops = { +static const struct clk_ops clk_prcmu_opp_gate_ops = { .prepare = clk_prcmu_opp_prepare, .unprepare = clk_prcmu_opp_unprepare, .is_prepared = clk_prcmu_is_prepared, @@ -230,7 +230,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = { .recalc_rate = clk_prcmu_recalc_rate, }; -static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { +static const struct clk_ops clk_prcmu_opp_volt_scalable_ops = { .prepare = clk_prcmu_opp_volt_prepare, .unprepare = clk_prcmu_opp_volt_unprepare, .is_prepared = clk_prcmu_is_prepared, @@ -247,7 +247,7 @@ static struct clk *clk_reg_prcmu(const char *name, u8 cg_sel, unsigned long rate, unsigned long flags, - struct clk_ops *clk_prcmu_ops) + const struct clk_ops *clk_prcmu_ops) { struct clk_prcmu *clk; struct clk_init_data clk_prcmu_init; diff --git a/drivers/clk/ux500/clk-sysctrl.c b/drivers/clk/ux500/clk-sysctrl.c index 266ddea630d2..8a4e93ce1e42 100644 --- a/drivers/clk/ux500/clk-sysctrl.c +++ b/drivers/clk/ux500/clk-sysctrl.c @@ -98,18 +98,18 @@ static u8 clk_sysctrl_get_parent(struct clk_hw *hw) return clk->parent_index; } -static struct clk_ops clk_sysctrl_gate_ops = { +static const struct clk_ops clk_sysctrl_gate_ops = { .prepare = clk_sysctrl_prepare, .unprepare = clk_sysctrl_unprepare, }; -static struct clk_ops clk_sysctrl_gate_fixed_rate_ops = { +static const struct clk_ops clk_sysctrl_gate_fixed_rate_ops = { .prepare = clk_sysctrl_prepare, .unprepare = clk_sysctrl_unprepare, .recalc_rate = clk_sysctrl_recalc_rate, }; -static struct clk_ops clk_sysctrl_set_parent_ops = { +static const struct clk_ops clk_sysctrl_set_parent_ops = { .set_parent = clk_sysctrl_set_parent, .get_parent = clk_sysctrl_get_parent, }; @@ -124,7 +124,7 @@ static struct clk *clk_reg_sysctrl(struct device *dev, unsigned long rate, unsigned long enable_delay_us, unsigned long flags, - struct clk_ops *clk_sysctrl_ops) + const struct clk_ops *clk_sysctrl_ops) { struct clk_sysctrl *clk; struct clk_init_data clk_sysctrl_init; diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c index 7e5add7d7752..e7a868b83fe5 100644 --- a/drivers/clk/versatile/clk-vexpress-osc.c +++ b/drivers/clk/versatile/clk-vexpress-osc.c @@ -61,7 +61,7 @@ static int vexpress_osc_set_rate(struct clk_hw *hw, unsigned long rate, return regmap_write(osc->reg, 0, rate); } -static struct clk_ops vexpress_osc_ops = { +static const struct clk_ops vexpress_osc_ops = { .recalc_rate = vexpress_osc_recalc_rate, .round_rate = vexpress_osc_round_rate, .set_rate = vexpress_osc_set_rate, diff --git a/drivers/clk/zte/clk-zx296718.c b/drivers/clk/zte/clk-zx296718.c index 27f853d4c76b..354dd508c516 100644 --- a/drivers/clk/zte/clk-zx296718.c +++ b/drivers/clk/zte/clk-zx296718.c @@ -451,7 +451,7 @@ static struct zx_clk_fixed_factor top_ffactor_clk[] = { FFACTOR(0, "emmc_mux_div2", "emmc_mux", 1, 2, CLK_SET_RATE_PARENT), }; -static struct clk_div_table noc_div_table[] = { +static const struct clk_div_table noc_div_table[] = { { .val = 1, .div = 2, }, { .val = 3, .div = 4, }, }; @@ -644,7 +644,7 @@ static int __init top_clocks_init(struct device_node *np) return 0; } -static struct clk_div_table common_even_div_table[] = { +static const struct clk_div_table common_even_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 3, .div = 4, }, @@ -656,7 +656,7 @@ static struct clk_div_table common_even_div_table[] = { { .val = 15, .div = 16, }, }; -static struct clk_div_table common_div_table[] = { +static const struct clk_div_table common_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 2, .div = 3, }, diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c index 9f013ed42977..80ac313e6c59 100644 --- a/drivers/cpufreq/powernow-k7.c +++ b/drivers/cpufreq/powernow-k7.c @@ -578,7 +578,7 @@ static int acer_cpufreq_pst(const struct dmi_system_id *d) * A BIOS update is all that can save them. * Mention this, and disable cpufreq. */ -static struct dmi_system_id powernow_dmi_table[] = { +static const struct dmi_system_id powernow_dmi_table[] = { { .callback = acer_cpufreq_pst, .ident = "Acer Aspire", diff --git a/drivers/dax/super.c b/drivers/dax/super.c index 938eb4868f7f..3600ff786646 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -46,6 +46,8 @@ void dax_read_unlock(int id) EXPORT_SYMBOL_GPL(dax_read_unlock); #ifdef CONFIG_BLOCK +#include <linux/blkdev.h> + int bdev_dax_pgoff(struct block_device *bdev, sector_t sector, size_t size, pgoff_t *pgoff) { @@ -59,6 +61,16 @@ int bdev_dax_pgoff(struct block_device *bdev, sector_t sector, size_t size, } EXPORT_SYMBOL(bdev_dax_pgoff); +#if IS_ENABLED(CONFIG_FS_DAX) +struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev) +{ + if (!blk_queue_dax(bdev->bd_queue)) + return NULL; + return fs_dax_get_by_host(bdev->bd_disk->disk_name); +} +EXPORT_SYMBOL_GPL(fs_dax_get_by_bdev); +#endif + /** * __bdev_dax_supported() - Check if the device supports dax for filesystem * @sb: The superblock of the device diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 8043e51de897..7da9f1b83ebe 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -357,7 +357,7 @@ struct sensor_value { } __packed; struct dev_pstate_set { - u16 dev_id; + __le16 dev_id; u8 pstate; } __packed; @@ -965,7 +965,7 @@ static int scpi_probe(struct platform_device *pdev) count = of_count_phandle_with_args(np, "mboxes", "#mbox-cells"); if (count < 0) { - dev_err(dev, "no mboxes property in '%s'\n", np->full_name); + dev_err(dev, "no mboxes property in '%pOF'\n", np); return -ENODEV; } diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index bf3672a81e49..d2fcafcea07e 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -534,7 +534,7 @@ static void cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, int sec_no) { - uuid_le *sec_type = (uuid_le *)gdata->section_type; + guid_t *sec_type = (guid_t *)gdata->section_type; __u16 severity; char newpfx[64]; @@ -545,12 +545,12 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata printk("%s""Error %d, type: %s\n", pfx, sec_no, cper_severity_str(severity)); if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) - printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id); + printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id); if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); - if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) { + if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); printk("%s""section_type: general processor error\n", newpfx); @@ -558,7 +558,7 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata cper_print_proc_generic(newpfx, proc_err); else goto err_section_too_small; - } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { + } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); printk("%s""section_type: memory error\n", newpfx); @@ -568,7 +568,7 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata gdata->error_data_length); else goto err_section_too_small; - } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) { + } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata); printk("%s""section_type: PCIe error\n", newpfx); diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index c46387160976..c8f169bf2e27 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -709,7 +709,7 @@ static u32 __init hash_oem_table_id(char s[8]) return local_hash_64(input, 32); } -static struct dmi_system_id gsmi_dmi_table[] __initdata = { +static const struct dmi_system_id gsmi_dmi_table[] __initconst = { { .ident = "Google Board", .matches = { diff --git a/drivers/firmware/google/memconsole-x86-legacy.c b/drivers/firmware/google/memconsole-x86-legacy.c index 8c1bf6dbdaa6..19bcbd10855b 100644 --- a/drivers/firmware/google/memconsole-x86-legacy.c +++ b/drivers/firmware/google/memconsole-x86-legacy.c @@ -126,7 +126,7 @@ static bool memconsole_ebda_init(void) return false; } -static struct dmi_system_id memconsole_dmi_table[] __initdata = { +static const struct dmi_system_id memconsole_dmi_table[] __initconst = { { .ident = "Google Board", .matches = { diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c index 493a56a4cfc4..d687ca3d5049 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -280,8 +280,8 @@ static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) "arm,psci-suspend-param", &state); if (ret) { - pr_warn(" * %s missing arm,psci-suspend-param property\n", - state_node->full_name); + pr_warn(" * %pOF missing arm,psci-suspend-param property\n", + state_node); of_node_put(state_node); goto free_mem; } diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index b25179517cc5..73ca55b7b7ec 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -806,6 +806,8 @@ static int tegra_bpmp_probe(struct platform_device *pdev) dev_info(&pdev->dev, "firmware: %s\n", tag); + platform_set_drvdata(pdev, bpmp); + err = of_platform_default_populate(pdev->dev.of_node, NULL, &pdev->dev); if (err < 0) goto free_mrq; @@ -822,8 +824,6 @@ static int tegra_bpmp_probe(struct platform_device *pdev) if (err < 0) goto free_mrq; - platform_set_drvdata(pdev, bpmp); - return 0; free_mrq: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c index e1cde6b80027..3b0f2ec6eec7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c @@ -51,7 +51,7 @@ struct amdgpu_mn { /* objects protected by lock */ struct mutex lock; - struct rb_root objects; + struct rb_root_cached objects; }; struct amdgpu_mn_node { @@ -76,8 +76,8 @@ static void amdgpu_mn_destroy(struct work_struct *work) mutex_lock(&adev->mn_lock); mutex_lock(&rmn->lock); hash_del(&rmn->node); - rbtree_postorder_for_each_entry_safe(node, next_node, &rmn->objects, - it.rb) { + rbtree_postorder_for_each_entry_safe(node, next_node, + &rmn->objects.rb_root, it.rb) { list_for_each_entry_safe(bo, next_bo, &node->bos, mn_list) { bo->mn = NULL; list_del_init(&bo->mn_list); @@ -221,7 +221,7 @@ static struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev) rmn->mm = mm; rmn->mn.ops = &amdgpu_mn_ops; mutex_init(&rmn->lock); - rmn->objects = RB_ROOT; + rmn->objects = RB_ROOT_CACHED; r = __mmu_notifier_register(&rmn->mn, mm); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 6b1343e5541d..b9a5a77eedaf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2475,7 +2475,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, u64 flags; uint64_t init_pde_value = 0; - vm->va = RB_ROOT; + vm->va = RB_ROOT_CACHED; vm->client_id = atomic64_inc_return(&adev->vm_manager.client_counter); for (i = 0; i < AMDGPU_MAX_VMHUBS; i++) vm->reserved_vmid[i] = NULL; @@ -2596,10 +2596,11 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm) amd_sched_entity_fini(vm->entity.sched, &vm->entity); - if (!RB_EMPTY_ROOT(&vm->va)) { + if (!RB_EMPTY_ROOT(&vm->va.rb_root)) { dev_err(adev->dev, "still active bo inside vm\n"); } - rbtree_postorder_for_each_entry_safe(mapping, tmp, &vm->va, rb) { + rbtree_postorder_for_each_entry_safe(mapping, tmp, + &vm->va.rb_root, rb) { list_del(&mapping->list); amdgpu_vm_it_remove(mapping, &vm->va); kfree(mapping); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index ba6691b58ee7..6716355403ec 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -118,7 +118,7 @@ struct amdgpu_vm_pt { struct amdgpu_vm { /* tree of virtual addresses mapped */ - struct rb_root va; + struct rb_root_cached va; /* protecting invalidated */ spinlock_t status_lock; diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c index db6aeec50b82..2e5e089dd912 100644 --- a/drivers/gpu/drm/drm_blend.c +++ b/drivers/gpu/drm/drm_blend.c @@ -319,7 +319,7 @@ static int drm_atomic_helper_crtc_normalize_zpos(struct drm_crtc *crtc, DRM_DEBUG_ATOMIC("[CRTC:%d:%s] calculating normalized zpos values\n", crtc->base.id, crtc->name); - states = kmalloc_array(total_planes, sizeof(*states), GFP_TEMPORARY); + states = kmalloc_array(total_planes, sizeof(*states), GFP_KERNEL); if (!states) return -ENOMEM; diff --git a/drivers/gpu/drm/drm_dp_dual_mode_helper.c b/drivers/gpu/drm/drm_dp_dual_mode_helper.c index 80e62f669321..0ef9011a1856 100644 --- a/drivers/gpu/drm/drm_dp_dual_mode_helper.c +++ b/drivers/gpu/drm/drm_dp_dual_mode_helper.c @@ -111,7 +111,7 @@ ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter, void *data; int ret; - data = kmalloc(msg.len, GFP_TEMPORARY); + data = kmalloc(msg.len, GFP_KERNEL); if (!data) return -ENOMEM; diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index f794089d30ac..61a1c8ea74bc 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -169,7 +169,7 @@ INTERVAL_TREE_DEFINE(struct drm_mm_node, rb, struct drm_mm_node * __drm_mm_interval_first(const struct drm_mm *mm, u64 start, u64 last) { - return drm_mm_interval_tree_iter_first((struct rb_root *)&mm->interval_tree, + return drm_mm_interval_tree_iter_first((struct rb_root_cached *)&mm->interval_tree, start, last) ?: (struct drm_mm_node *)&mm->head_node; } EXPORT_SYMBOL(__drm_mm_interval_first); @@ -180,6 +180,7 @@ static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, struct drm_mm *mm = hole_node->mm; struct rb_node **link, *rb; struct drm_mm_node *parent; + bool leftmost = true; node->__subtree_last = LAST(node); @@ -196,9 +197,10 @@ static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, rb = &hole_node->rb; link = &hole_node->rb.rb_right; + leftmost = false; } else { rb = NULL; - link = &mm->interval_tree.rb_node; + link = &mm->interval_tree.rb_root.rb_node; } while (*link) { @@ -208,14 +210,15 @@ static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, parent->__subtree_last = node->__subtree_last; if (node->start < parent->start) link = &parent->rb.rb_left; - else + else { link = &parent->rb.rb_right; + leftmost = true; + } } rb_link_node(&node->rb, rb, link); - rb_insert_augmented(&node->rb, - &mm->interval_tree, - &drm_mm_interval_tree_augment); + rb_insert_augmented_cached(&node->rb, &mm->interval_tree, leftmost, + &drm_mm_interval_tree_augment); } #define RB_INSERT(root, member, expr) do { \ @@ -577,7 +580,7 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) *new = *old; list_replace(&old->node_list, &new->node_list); - rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree); + rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree.rb_root); if (drm_mm_hole_follows(old)) { list_replace(&old->hole_stack, &new->hole_stack); @@ -863,7 +866,7 @@ void drm_mm_init(struct drm_mm *mm, u64 start, u64 size) mm->color_adjust = NULL; INIT_LIST_HEAD(&mm->hole_stack); - mm->interval_tree = RB_ROOT; + mm->interval_tree = RB_ROOT_CACHED; mm->holes_size = RB_ROOT; mm->holes_addr = RB_ROOT; diff --git a/drivers/gpu/drm/drm_scdc_helper.c b/drivers/gpu/drm/drm_scdc_helper.c index 7d1b0f011d33..935653eb3616 100644 --- a/drivers/gpu/drm/drm_scdc_helper.c +++ b/drivers/gpu/drm/drm_scdc_helper.c @@ -102,7 +102,7 @@ ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, void *data; int err; - data = kmalloc(1 + size, GFP_TEMPORARY); + data = kmalloc(1 + size, GFP_KERNEL); if (!data) return -ENOMEM; diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index d9100b565198..28f1226576f8 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -147,7 +147,7 @@ struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_m struct rb_node *iter; unsigned long offset; - iter = mgr->vm_addr_space_mm.interval_tree.rb_node; + iter = mgr->vm_addr_space_mm.interval_tree.rb_root.rb_node; best = NULL; while (likely(iter)) { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index a7ff2e4c00d2..026ef4e02f85 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -37,7 +37,7 @@ static struct etnaviv_gem_submit *submit_create(struct drm_device *dev, struct etnaviv_gem_submit *submit; size_t sz = size_vstruct(nr, sizeof(submit->bos[0]), sizeof(*submit)); - submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); + submit = kmalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); if (submit) { submit->dev = dev; submit->gpu = gpu; diff --git a/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c index 771ff66711af..37c997e24b9e 100644 --- a/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c +++ b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c @@ -26,7 +26,7 @@ #include "mdfld_output.h" #include "mdfld_dsi_pkg_sender.h" #include "tc35876x-dsi-lvds.h" -#include <linux/i2c/tc35876x.h> +#include <linux/platform_data/tc35876x.h> #include <linux/kernel.h> #include <linux/module.h> #include <asm/intel_scu_ipc.h> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 57317715977f..19404c96eeb1 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2540,7 +2540,7 @@ static void *i915_gem_object_map(const struct drm_i915_gem_object *obj, if (n_pages > ARRAY_SIZE(stack_pages)) { /* Too big for stack -- allocate temporary array instead */ - pages = kvmalloc_array(n_pages, sizeof(*pages), GFP_TEMPORARY); + pages = kvmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL); if (!pages) return NULL; } diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 50d5e24f91a9..92437f455b43 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -293,7 +293,7 @@ static int eb_create(struct i915_execbuffer *eb) * as possible to perform the allocation and warn * if it fails. */ - flags = GFP_TEMPORARY; + flags = GFP_KERNEL; if (size > 1) flags |= __GFP_NORETRY | __GFP_NOWARN; @@ -1515,7 +1515,7 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb) urelocs = u64_to_user_ptr(eb->exec[i].relocs_ptr); size = nreloc * sizeof(*relocs); - relocs = kvmalloc_array(size, 1, GFP_TEMPORARY); + relocs = kvmalloc_array(size, 1, GFP_KERNEL); if (!relocs) { kvfree(relocs); err = -ENOMEM; @@ -2077,7 +2077,7 @@ get_fence_array(struct drm_i915_gem_execbuffer2 *args, return ERR_PTR(-EFAULT); fences = kvmalloc_array(args->num_cliprects, sizeof(*fences), - __GFP_NOWARN | GFP_TEMPORARY); + __GFP_NOWARN | GFP_KERNEL); if (!fences) return ERR_PTR(-ENOMEM); @@ -2463,9 +2463,9 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, /* Copy in the exec list from userland */ exec_list = kvmalloc_array(args->buffer_count, sizeof(*exec_list), - __GFP_NOWARN | GFP_TEMPORARY); + __GFP_NOWARN | GFP_KERNEL); exec2_list = kvmalloc_array(args->buffer_count + 1, sz, - __GFP_NOWARN | GFP_TEMPORARY); + __GFP_NOWARN | GFP_KERNEL); if (exec_list == NULL || exec2_list == NULL) { DRM_DEBUG("Failed to allocate exec list for %d buffers\n", args->buffer_count); @@ -2543,7 +2543,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, /* Allocate an extra slot for use by the command parser */ exec2_list = kvmalloc_array(args->buffer_count + 1, sz, - __GFP_NOWARN | GFP_TEMPORARY); + __GFP_NOWARN | GFP_KERNEL); if (exec2_list == NULL) { DRM_DEBUG("Failed to allocate exec list for %d buffers\n", args->buffer_count); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 0d5a988b3867..e2410eb5d96e 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -3231,7 +3231,7 @@ intel_rotate_pages(struct intel_rotation_info *rot_info, /* Allocate a temporary list of source pages for random access. */ page_addr_list = kvmalloc_array(n_pages, sizeof(dma_addr_t), - GFP_TEMPORARY); + GFP_KERNEL); if (!page_addr_list) return ERR_PTR(ret); diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index f152a38d7079..709efe2357ea 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -49,7 +49,7 @@ struct i915_mmu_notifier { spinlock_t lock; struct hlist_node node; struct mmu_notifier mn; - struct rb_root objects; + struct rb_root_cached objects; struct workqueue_struct *wq; }; @@ -123,7 +123,7 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn, struct interval_tree_node *it; LIST_HEAD(cancelled); - if (RB_EMPTY_ROOT(&mn->objects)) + if (RB_EMPTY_ROOT(&mn->objects.rb_root)) return; /* interval ranges are inclusive, but invalidate range is exclusive */ @@ -172,7 +172,7 @@ i915_mmu_notifier_create(struct mm_struct *mm) spin_lock_init(&mn->lock); mn->mn.ops = &i915_gem_userptr_notifier; - mn->objects = RB_ROOT; + mn->objects = RB_ROOT_CACHED; mn->wq = alloc_workqueue("i915-userptr-release", WQ_UNBOUND, 0); if (mn->wq == NULL) { kfree(mn); @@ -507,7 +507,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work) ret = -ENOMEM; pinned = 0; - pvec = kvmalloc_array(npages, sizeof(struct page *), GFP_TEMPORARY); + pvec = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); if (pvec != NULL) { struct mm_struct *mm = obj->userptr.mm->mm; unsigned int flags = 0; @@ -643,7 +643,7 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj) if (mm == current->mm) { pvec = kvmalloc_array(num_pages, sizeof(struct page *), - GFP_TEMPORARY | + GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN); if (pvec) /* defer to worker if malloc fails */ diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index ed5a1eb839ad..0c779671fe2d 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -787,16 +787,16 @@ int i915_error_state_buf_init(struct drm_i915_error_state_buf *ebuf, */ ebuf->size = count + 1 > PAGE_SIZE ? count + 1 : PAGE_SIZE; ebuf->buf = kmalloc(ebuf->size, - GFP_TEMPORARY | __GFP_NORETRY | __GFP_NOWARN); + GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN); if (ebuf->buf == NULL) { ebuf->size = PAGE_SIZE; - ebuf->buf = kmalloc(ebuf->size, GFP_TEMPORARY); + ebuf->buf = kmalloc(ebuf->size, GFP_KERNEL); } if (ebuf->buf == NULL) { ebuf->size = 128; - ebuf->buf = kmalloc(ebuf->size, GFP_TEMPORARY); + ebuf->buf = kmalloc(ebuf->size, GFP_KERNEL); } if (ebuf->buf == NULL) diff --git a/drivers/gpu/drm/i915/selftests/i915_random.c b/drivers/gpu/drm/i915/selftests/i915_random.c index d044bf9a6feb..222c511bea49 100644 --- a/drivers/gpu/drm/i915/selftests/i915_random.c +++ b/drivers/gpu/drm/i915/selftests/i915_random.c @@ -62,7 +62,7 @@ unsigned int *i915_random_order(unsigned int count, struct rnd_state *state) { unsigned int *order, i; - order = kmalloc_array(count, sizeof(*order), GFP_TEMPORARY); + order = kmalloc_array(count, sizeof(*order), GFP_KERNEL); if (!order) return order; diff --git a/drivers/gpu/drm/i915/selftests/intel_breadcrumbs.c b/drivers/gpu/drm/i915/selftests/intel_breadcrumbs.c index 7276194c04f7..828904b7d468 100644 --- a/drivers/gpu/drm/i915/selftests/intel_breadcrumbs.c +++ b/drivers/gpu/drm/i915/selftests/intel_breadcrumbs.c @@ -117,12 +117,12 @@ static int igt_random_insert_remove(void *arg) mock_engine_reset(engine); - waiters = kvmalloc_array(count, sizeof(*waiters), GFP_TEMPORARY); + waiters = kvmalloc_array(count, sizeof(*waiters), GFP_KERNEL); if (!waiters) goto out_engines; bitmap = kcalloc(DIV_ROUND_UP(count, BITS_PER_LONG), sizeof(*bitmap), - GFP_TEMPORARY); + GFP_KERNEL); if (!bitmap) goto out_waiters; @@ -187,12 +187,12 @@ static int igt_insert_complete(void *arg) mock_engine_reset(engine); - waiters = kvmalloc_array(count, sizeof(*waiters), GFP_TEMPORARY); + waiters = kvmalloc_array(count, sizeof(*waiters), GFP_KERNEL); if (!waiters) goto out_engines; bitmap = kcalloc(DIV_ROUND_UP(count, BITS_PER_LONG), sizeof(*bitmap), - GFP_TEMPORARY); + GFP_KERNEL); if (!bitmap) goto out_waiters; @@ -368,7 +368,7 @@ static int igt_wakeup(void *arg) mock_engine_reset(engine); - waiters = kvmalloc_array(count, sizeof(*waiters), GFP_TEMPORARY); + waiters = kvmalloc_array(count, sizeof(*waiters), GFP_KERNEL); if (!waiters) goto out_engines; diff --git a/drivers/gpu/drm/i915/selftests/intel_uncore.c b/drivers/gpu/drm/i915/selftests/intel_uncore.c index 2d0fef2cfca6..3cac22eb47ce 100644 --- a/drivers/gpu/drm/i915/selftests/intel_uncore.c +++ b/drivers/gpu/drm/i915/selftests/intel_uncore.c @@ -127,7 +127,7 @@ static int intel_uncore_check_forcewake_domains(struct drm_i915_private *dev_pri return 0; valid = kzalloc(BITS_TO_LONGS(FW_RANGE) * sizeof(*valid), - GFP_TEMPORARY); + GFP_KERNEL); if (!valid) return -ENOMEM; diff --git a/drivers/gpu/drm/lib/drm_random.c b/drivers/gpu/drm/lib/drm_random.c index 7b12a68c3b54..a78c4b483e8d 100644 --- a/drivers/gpu/drm/lib/drm_random.c +++ b/drivers/gpu/drm/lib/drm_random.c @@ -28,7 +28,7 @@ unsigned int *drm_random_order(unsigned int count, struct rnd_state *state) { unsigned int *order, i; - order = kmalloc_array(count, sizeof(*order), GFP_TEMPORARY); + order = kmalloc_array(count, sizeof(*order), GFP_KERNEL); if (!order) return order; diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 8a75c0bd8a78..5d0a75d4b249 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -40,7 +40,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, if (sz > SIZE_MAX) return NULL; - submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); + submit = kmalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); if (!submit) return NULL; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c index 4a57defc99b3..1399d923d446 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c @@ -171,7 +171,7 @@ nvkm_gpio_fini(struct nvkm_subdev *subdev, bool suspend) return 0; } -static struct dmi_system_id gpio_reset_ids[] = { +static const struct dmi_system_id gpio_reset_ids[] = { { .ident = "Apple Macbook 10,1", .matches = { diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index ec63bc5e9de7..8cbaeec090c9 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -924,7 +924,7 @@ struct radeon_vm_id { struct radeon_vm { struct mutex mutex; - struct rb_root va; + struct rb_root_cached va; /* protecting invalidated and freed */ spinlock_t status_lock; diff --git a/drivers/gpu/drm/radeon/radeon_mn.c b/drivers/gpu/drm/radeon/radeon_mn.c index 896f2cf51e4e..1d62288b7ee3 100644 --- a/drivers/gpu/drm/radeon/radeon_mn.c +++ b/drivers/gpu/drm/radeon/radeon_mn.c @@ -50,7 +50,7 @@ struct radeon_mn { /* objects protected by lock */ struct mutex lock; - struct rb_root objects; + struct rb_root_cached objects; }; struct radeon_mn_node { @@ -75,8 +75,8 @@ static void radeon_mn_destroy(struct work_struct *work) mutex_lock(&rdev->mn_lock); mutex_lock(&rmn->lock); hash_del(&rmn->node); - rbtree_postorder_for_each_entry_safe(node, next_node, &rmn->objects, - it.rb) { + rbtree_postorder_for_each_entry_safe(node, next_node, + &rmn->objects.rb_root, it.rb) { interval_tree_remove(&node->it, &rmn->objects); list_for_each_entry_safe(bo, next_bo, &node->bos, mn_list) { @@ -205,7 +205,7 @@ static struct radeon_mn *radeon_mn_get(struct radeon_device *rdev) rmn->mm = mm; rmn->mn.ops = &radeon_mn_ops; mutex_init(&rmn->lock); - rmn->objects = RB_ROOT; + rmn->objects = RB_ROOT_CACHED; r = __mmu_notifier_register(&rmn->mn, mm); if (r) diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c index 5e82b408d522..e5c0e635e371 100644 --- a/drivers/gpu/drm/radeon/radeon_vm.c +++ b/drivers/gpu/drm/radeon/radeon_vm.c @@ -1185,7 +1185,7 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm) vm->ids[i].last_id_use = NULL; } mutex_init(&vm->mutex); - vm->va = RB_ROOT; + vm->va = RB_ROOT_CACHED; spin_lock_init(&vm->status_lock); INIT_LIST_HEAD(&vm->invalidated); INIT_LIST_HEAD(&vm->freed); @@ -1232,10 +1232,11 @@ void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm) struct radeon_bo_va *bo_va, *tmp; int i, r; - if (!RB_EMPTY_ROOT(&vm->va)) { + if (!RB_EMPTY_ROOT(&vm->va.rb_root)) { dev_err(rdev->dev, "still active bo inside vm\n"); } - rbtree_postorder_for_each_entry_safe(bo_va, tmp, &vm->va, it.rb) { + rbtree_postorder_for_each_entry_safe(bo_va, tmp, + &vm->va.rb_root, it.rb) { interval_tree_remove(&bo_va->it, &vm->va); r = radeon_bo_reserve(bo_va->bo, false); if (!r) { diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c index dfdd858eda0a..86eb4c185a28 100644 --- a/drivers/gpu/drm/selftests/test-drm_mm.c +++ b/drivers/gpu/drm/selftests/test-drm_mm.c @@ -1627,7 +1627,7 @@ static int igt_topdown(void *ignored) goto err; bitmap = kzalloc(count / BITS_PER_LONG * sizeof(unsigned long), - GFP_TEMPORARY); + GFP_KERNEL); if (!bitmap) goto err_nodes; @@ -1741,7 +1741,7 @@ static int igt_bottomup(void *ignored) goto err; bitmap = kzalloc(count / BITS_PER_LONG * sizeof(unsigned long), - GFP_TEMPORARY); + GFP_KERNEL); if (!bitmap) goto err_nodes; diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 579bdf93be43..14a94d90c028 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -973,7 +973,7 @@ static int __init enable_cap_knobs(const struct dmi_system_id *d) return 0; } -static struct dmi_system_id __initdata pm_dmi_table[] = { +static const struct dmi_system_id pm_dmi_table[] __initconst = { { enable_cap_knobs, "IBM Active Energy Manager", { diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 76c34f4fde13..5c677ba44014 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -1247,7 +1247,7 @@ static int applesmc_dmi_match(const struct dmi_system_id *id) * Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". * So we need to put "Apple MacBook Pro" before "Apple MacBook". */ -static __initdata struct dmi_system_id applesmc_whitelist[] = { +static const struct dmi_system_id applesmc_whitelist[] __initconst = { { applesmc_dmi_match, "Apple MacBook Air", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 3189246302a6..c7c9e95e58a8 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -890,7 +890,7 @@ static const struct i8k_config_data i8k_config_data[] = { }, }; -static struct dmi_system_id i8k_dmi_table[] __initdata = { +static const struct dmi_system_id i8k_dmi_table[] __initconst = { { .ident = "Dell Inspiron", .matches = { @@ -1013,7 +1013,7 @@ MODULE_DEVICE_TABLE(dmi, i8k_dmi_table); * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call. * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121 */ -static struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initdata = { +static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst = { { .ident = "Dell Studio XPS 8000", .matches = { diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 65fa29591d21..edc0cfb6fc1a 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -189,6 +189,14 @@ config I2C_PIIX4 This driver can also be built as a module. If so, the module will be called i2c-piix4. +config I2C_CHT_WC + tristate "Intel Cherry Trail Whiskey Cove PMIC smbus controller" + depends on INTEL_SOC_PMIC_CHTWC + help + If you say yes to this option, support will be included for the + SMBus controller found in the Intel Cherry Trail Whiskey Cove PMIC + found on some Intel Cherry Trail systems. + config I2C_NFORCE2 tristate "Nvidia nForce2, nForce3 and nForce4" depends on PCI @@ -900,6 +908,13 @@ config I2C_SIRF This driver can also be built as a module. If so, the module will be called i2c-sirf. +config I2C_SPRD + bool "Spreadtrum I2C interface" + depends on I2C=y && ARCH_SPRD + help + If you say yes to this option, support will be included for the + Spreadtrum I2C interface. + config I2C_ST tristate "STMicroelectronics SSC I2C support" depends on ARCH_STI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 1b2fc815a4d8..562daf738048 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o +obj-$(CONFIG_I2C_CHT_WC) += i2c-cht-wc.o obj-$(CONFIG_I2C_I801) += i2c-i801.o obj-$(CONFIG_I2C_ISCH) += i2c-isch.o obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o @@ -89,6 +90,7 @@ obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o +obj-$(CONFIG_I2C_SPRD) += i2c-sprd.o obj-$(CONFIG_I2C_ST) += i2c-st.o obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 6fdf9231c23c..284f8670dbeb 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -53,6 +53,9 @@ #define ASPEED_I2CD_MASTER_EN BIT(0) /* 0x04 : I2CD Clock and AC Timing Control Register #1 */ +#define ASPEED_I2CD_TIME_TBUF_MASK GENMASK(31, 28) +#define ASPEED_I2CD_TIME_THDSTA_MASK GENMASK(27, 24) +#define ASPEED_I2CD_TIME_TACST_MASK GENMASK(23, 20) #define ASPEED_I2CD_TIME_SCL_HIGH_SHIFT 16 #define ASPEED_I2CD_TIME_SCL_HIGH_MASK GENMASK(19, 16) #define ASPEED_I2CD_TIME_SCL_LOW_SHIFT 12 @@ -132,6 +135,7 @@ struct aspeed_i2c_bus { /* Synchronizes I/O mem access to base. */ spinlock_t lock; struct completion cmd_complete; + u32 (*get_clk_reg_val)(u32 divisor); unsigned long parent_clk_frequency; u32 bus_frequency; /* Transaction state. */ @@ -675,7 +679,7 @@ static const struct i2c_algorithm aspeed_i2c_algo = { #endif /* CONFIG_I2C_SLAVE */ }; -static u32 aspeed_i2c_get_clk_reg_val(u32 divisor) +static u32 aspeed_i2c_get_clk_reg_val(u32 clk_high_low_max, u32 divisor) { u32 base_clk, clk_high, clk_low, tmp; @@ -695,16 +699,22 @@ static u32 aspeed_i2c_get_clk_reg_val(u32 divisor) * Thus, * SCL_freq = APB_freq / * ((1 << base_clk) * (clk_high + 1 + clk_low + 1)) - * The documentation recommends clk_high >= 8 and clk_low >= 7 when - * possible; this last constraint gives us the following solution: + * The documentation recommends clk_high >= clk_high_max / 2 and + * clk_low >= clk_low_max / 2 - 1 when possible; this last constraint + * gives us the following solution: */ - base_clk = divisor > 33 ? ilog2((divisor - 1) / 32) + 1 : 0; - tmp = divisor / (1 << base_clk); - clk_high = tmp / 2 + tmp % 2; - clk_low = tmp - clk_high; + base_clk = divisor > clk_high_low_max ? + ilog2((divisor - 1) / clk_high_low_max) + 1 : 0; + tmp = (divisor + (1 << base_clk) - 1) >> base_clk; + clk_low = tmp / 2; + clk_high = tmp - clk_low; + + if (clk_high) + clk_high--; + + if (clk_low) + clk_low--; - clk_high -= 1; - clk_low -= 1; return ((clk_high << ASPEED_I2CD_TIME_SCL_HIGH_SHIFT) & ASPEED_I2CD_TIME_SCL_HIGH_MASK) @@ -713,13 +723,35 @@ static u32 aspeed_i2c_get_clk_reg_val(u32 divisor) | (base_clk & ASPEED_I2CD_TIME_BASE_DIVISOR_MASK); } +static u32 aspeed_i2c_24xx_get_clk_reg_val(u32 divisor) +{ + /* + * clk_high and clk_low are each 3 bits wide, so each can hold a max + * value of 8 giving a clk_high_low_max of 16. + */ + return aspeed_i2c_get_clk_reg_val(16, divisor); +} + +static u32 aspeed_i2c_25xx_get_clk_reg_val(u32 divisor) +{ + /* + * clk_high and clk_low are each 4 bits wide, so each can hold a max + * value of 16 giving a clk_high_low_max of 32. + */ + return aspeed_i2c_get_clk_reg_val(32, divisor); +} + /* precondition: bus.lock has been acquired. */ static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus) { u32 divisor, clk_reg_val; - divisor = bus->parent_clk_frequency / bus->bus_frequency; - clk_reg_val = aspeed_i2c_get_clk_reg_val(divisor); + divisor = DIV_ROUND_UP(bus->parent_clk_frequency, bus->bus_frequency); + clk_reg_val = readl(bus->base + ASPEED_I2C_AC_TIMING_REG1); + clk_reg_val &= (ASPEED_I2CD_TIME_TBUF_MASK | + ASPEED_I2CD_TIME_THDSTA_MASK | + ASPEED_I2CD_TIME_TACST_MASK); + clk_reg_val |= bus->get_clk_reg_val(divisor); writel(clk_reg_val, bus->base + ASPEED_I2C_AC_TIMING_REG1); writel(ASPEED_NO_TIMEOUT_CTRL, bus->base + ASPEED_I2C_AC_TIMING_REG2); @@ -778,8 +810,22 @@ static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus) return ret; } +static const struct of_device_id aspeed_i2c_bus_of_table[] = { + { + .compatible = "aspeed,ast2400-i2c-bus", + .data = aspeed_i2c_24xx_get_clk_reg_val, + }, + { + .compatible = "aspeed,ast2500-i2c-bus", + .data = aspeed_i2c_25xx_get_clk_reg_val, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table); + static int aspeed_i2c_probe_bus(struct platform_device *pdev) { + const struct of_device_id *match; struct aspeed_i2c_bus *bus; struct clk *parent_clk; struct resource *res; @@ -809,6 +855,12 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev) bus->bus_frequency = 100000; } + match = of_match_node(aspeed_i2c_bus_of_table, pdev->dev.of_node); + if (!match) + bus->get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val; + else + bus->get_clk_reg_val = match->data; + /* Initialize the I2C adapter */ spin_lock_init(&bus->lock); init_completion(&bus->cmd_complete); @@ -870,13 +922,6 @@ static int aspeed_i2c_remove_bus(struct platform_device *pdev) return 0; } -static const struct of_device_id aspeed_i2c_bus_of_table[] = { - { .compatible = "aspeed,ast2400-i2c-bus", }, - { .compatible = "aspeed,ast2500-i2c-bus", }, - { }, -}; -MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table); - static struct platform_driver aspeed_i2c_bus_driver = { .probe = aspeed_i2c_probe_bus, .remove = aspeed_i2c_remove_bus, diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 38dd61d621df..bfd1fdff64a9 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -809,7 +809,7 @@ out: * The hardware can handle at most two messages concatenated by a * repeated start via it's internal address feature. */ -static struct i2c_adapter_quirks at91_twi_quirks = { +static const struct i2c_adapter_quirks at91_twi_quirks = { .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR, .max_comb_1st_msg_len = 3, }; diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index 318df559adc5..4c8c3bc4669c 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -510,8 +510,7 @@ static int bcm_iproc_i2c_remove(struct platform_device *pdev) static int bcm_iproc_i2c_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); /* make sure there's no pending interrupt when we go into suspend */ writel(0, iproc_i2c->base + IE_OFFSET); @@ -526,8 +525,7 @@ static int bcm_iproc_i2c_suspend(struct device *dev) static int bcm_iproc_i2c_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); int ret; u32 val; diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 9fe942b8c610..ff3343186a82 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -21,7 +21,6 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/delay.h> -#include <linux/i2c/bfin_twi.h> #include <asm/irq.h> #include <asm/portmux.h> diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 75d80161931f..b13605718291 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -826,8 +826,7 @@ static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long */ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct cdns_i2c *xi2c = platform_get_drvdata(pdev); + struct cdns_i2c *xi2c = dev_get_drvdata(dev); clk_disable(xi2c->clk); @@ -844,8 +843,7 @@ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev) */ static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct cdns_i2c *xi2c = platform_get_drvdata(pdev); + struct cdns_i2c *xi2c = dev_get_drvdata(dev); int ret; ret = clk_enable(xi2c->clk); diff --git a/drivers/i2c/busses/i2c-cht-wc.c b/drivers/i2c/busses/i2c-cht-wc.c new file mode 100644 index 000000000000..190bbbc7bfee --- /dev/null +++ b/drivers/i2c/busses/i2c-cht-wc.c @@ -0,0 +1,363 @@ +/* + * Intel CHT Whiskey Cove PMIC I2C Master driver + * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> + * + * Based on various non upstream patches to support the CHT Whiskey Cove PMIC: + * Copyright (C) 2011 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/mfd/intel_soc_pmic.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define CHT_WC_I2C_CTRL 0x5e24 +#define CHT_WC_I2C_CTRL_WR BIT(0) +#define CHT_WC_I2C_CTRL_RD BIT(1) +#define CHT_WC_I2C_CLIENT_ADDR 0x5e25 +#define CHT_WC_I2C_REG_OFFSET 0x5e26 +#define CHT_WC_I2C_WRDATA 0x5e27 +#define CHT_WC_I2C_RDDATA 0x5e28 + +#define CHT_WC_EXTCHGRIRQ 0x6e0a +#define CHT_WC_EXTCHGRIRQ_CLIENT_IRQ BIT(0) +#define CHT_WC_EXTCHGRIRQ_WRITE_IRQ BIT(1) +#define CHT_WC_EXTCHGRIRQ_READ_IRQ BIT(2) +#define CHT_WC_EXTCHGRIRQ_NACK_IRQ BIT(3) +#define CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK ((u8)GENMASK(3, 1)) +#define CHT_WC_EXTCHGRIRQ_MSK 0x6e17 + +struct cht_wc_i2c_adap { + struct i2c_adapter adapter; + wait_queue_head_t wait; + struct irq_chip irqchip; + struct mutex adap_lock; + struct mutex irqchip_lock; + struct regmap *regmap; + struct irq_domain *irq_domain; + struct i2c_client *client; + int client_irq; + u8 irq_mask; + u8 old_irq_mask; + int read_data; + bool io_error; + bool done; +}; + +static irqreturn_t cht_wc_i2c_adap_thread_handler(int id, void *data) +{ + struct cht_wc_i2c_adap *adap = data; + int ret, reg; + + mutex_lock(&adap->adap_lock); + + /* Read IRQs */ + ret = regmap_read(adap->regmap, CHT_WC_EXTCHGRIRQ, ®); + if (ret) { + dev_err(&adap->adapter.dev, "Error reading extchgrirq reg\n"); + mutex_unlock(&adap->adap_lock); + return IRQ_NONE; + } + + reg &= ~adap->irq_mask; + + /* Reads must be acked after reading the received data. */ + ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, &adap->read_data); + if (ret) + adap->io_error = true; + + /* + * Immediately ack IRQs, so that if new IRQs arrives while we're + * handling the previous ones our irq will re-trigger when we're done. + */ + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, reg); + if (ret) + dev_err(&adap->adapter.dev, "Error writing extchgrirq reg\n"); + + if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK) { + adap->io_error |= !!(reg & CHT_WC_EXTCHGRIRQ_NACK_IRQ); + adap->done = true; + } + + mutex_unlock(&adap->adap_lock); + + if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK) + wake_up(&adap->wait); + + /* + * Do NOT use handle_nested_irq here, the client irq handler will + * likely want to do i2c transfers and the i2c controller uses this + * interrupt handler as well, so running the client irq handler from + * this thread will cause things to lock up. + */ + if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ) { + /* + * generic_handle_irq expects local IRQs to be disabled + * as normally it is called from interrupt context. + */ + local_irq_disable(); + generic_handle_irq(adap->client_irq); + local_irq_enable(); + } + + return IRQ_HANDLED; +} + +static u32 cht_wc_i2c_adap_master_func(struct i2c_adapter *adap) +{ + /* This i2c adapter only supports SMBUS byte transfers */ + return I2C_FUNC_SMBUS_BYTE_DATA; +} + +static int cht_wc_i2c_adap_smbus_xfer(struct i2c_adapter *_adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + struct cht_wc_i2c_adap *adap = i2c_get_adapdata(_adap); + int ret; + + mutex_lock(&adap->adap_lock); + adap->io_error = false; + adap->done = false; + mutex_unlock(&adap->adap_lock); + + ret = regmap_write(adap->regmap, CHT_WC_I2C_CLIENT_ADDR, addr); + if (ret) + return ret; + + if (read_write == I2C_SMBUS_WRITE) { + ret = regmap_write(adap->regmap, CHT_WC_I2C_WRDATA, data->byte); + if (ret) + return ret; + } + + ret = regmap_write(adap->regmap, CHT_WC_I2C_REG_OFFSET, command); + if (ret) + return ret; + + ret = regmap_write(adap->regmap, CHT_WC_I2C_CTRL, + (read_write == I2C_SMBUS_WRITE) ? + CHT_WC_I2C_CTRL_WR : CHT_WC_I2C_CTRL_RD); + if (ret) + return ret; + + ret = wait_event_timeout(adap->wait, adap->done, msecs_to_jiffies(30)); + if (ret == 0) { + /* + * The CHT GPIO controller serializes all IRQs, sometimes + * causing significant delays, check status manually. + */ + cht_wc_i2c_adap_thread_handler(0, adap); + if (!adap->done) + return -ETIMEDOUT; + } + + ret = 0; + mutex_lock(&adap->adap_lock); + if (adap->io_error) + ret = -EIO; + else if (read_write == I2C_SMBUS_READ) + data->byte = adap->read_data; + mutex_unlock(&adap->adap_lock); + + return ret; +} + +static const struct i2c_algorithm cht_wc_i2c_adap_algo = { + .functionality = cht_wc_i2c_adap_master_func, + .smbus_xfer = cht_wc_i2c_adap_smbus_xfer, +}; + +/**** irqchip for the client connected to the extchgr i2c adapter ****/ +static void cht_wc_i2c_irq_lock(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + + mutex_lock(&adap->irqchip_lock); +} + +static void cht_wc_i2c_irq_sync_unlock(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + int ret; + + if (adap->irq_mask != adap->old_irq_mask) { + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK, + adap->irq_mask); + if (ret == 0) + adap->old_irq_mask = adap->irq_mask; + else + dev_err(&adap->adapter.dev, "Error writing EXTCHGRIRQ_MSK\n"); + } + + mutex_unlock(&adap->irqchip_lock); +} + +static void cht_wc_i2c_irq_enable(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + + adap->irq_mask &= ~CHT_WC_EXTCHGRIRQ_CLIENT_IRQ; +} + +static void cht_wc_i2c_irq_disable(struct irq_data *data) +{ + struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); + + adap->irq_mask |= CHT_WC_EXTCHGRIRQ_CLIENT_IRQ; +} + +static const struct irq_chip cht_wc_i2c_irq_chip = { + .irq_bus_lock = cht_wc_i2c_irq_lock, + .irq_bus_sync_unlock = cht_wc_i2c_irq_sync_unlock, + .irq_disable = cht_wc_i2c_irq_disable, + .irq_enable = cht_wc_i2c_irq_enable, + .name = "cht_wc_ext_chrg_irq_chip", +}; + +static const struct property_entry bq24190_props[] = { + PROPERTY_ENTRY_STRING("extcon-name", "cht_wcove_pwrsrc"), + PROPERTY_ENTRY_BOOL("omit-battery-class"), + PROPERTY_ENTRY_BOOL("disable-reset"), + { } +}; + +static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + struct cht_wc_i2c_adap *adap; + struct i2c_board_info board_info = { + .type = "bq24190", + .addr = 0x6b, + .properties = bq24190_props, + }; + int ret, reg, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Error missing irq resource\n"); + return -EINVAL; + } + + adap = devm_kzalloc(&pdev->dev, sizeof(*adap), GFP_KERNEL); + if (!adap) + return -ENOMEM; + + init_waitqueue_head(&adap->wait); + mutex_init(&adap->adap_lock); + mutex_init(&adap->irqchip_lock); + adap->irqchip = cht_wc_i2c_irq_chip; + adap->regmap = pmic->regmap; + adap->adapter.owner = THIS_MODULE; + adap->adapter.class = I2C_CLASS_HWMON; + adap->adapter.algo = &cht_wc_i2c_adap_algo; + strlcpy(adap->adapter.name, "PMIC I2C Adapter", + sizeof(adap->adapter.name)); + adap->adapter.dev.parent = &pdev->dev; + + /* Clear and activate i2c-adapter interrupts, disable client IRQ */ + adap->old_irq_mask = adap->irq_mask = ~CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK; + + ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, ®); + if (ret) + return ret; + + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, ~adap->irq_mask); + if (ret) + return ret; + + ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK, adap->irq_mask); + if (ret) + return ret; + + /* Alloc and register client IRQ */ + adap->irq_domain = irq_domain_add_linear(pdev->dev.of_node, 1, + &irq_domain_simple_ops, NULL); + if (!adap->irq_domain) + return -ENOMEM; + + adap->client_irq = irq_create_mapping(adap->irq_domain, 0); + if (!adap->client_irq) { + ret = -ENOMEM; + goto remove_irq_domain; + } + + irq_set_chip_data(adap->client_irq, adap); + irq_set_chip_and_handler(adap->client_irq, &adap->irqchip, + handle_simple_irq); + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + cht_wc_i2c_adap_thread_handler, + IRQF_ONESHOT, "PMIC I2C Adapter", adap); + if (ret) + goto remove_irq_domain; + + i2c_set_adapdata(&adap->adapter, adap); + ret = i2c_add_adapter(&adap->adapter); + if (ret) + goto remove_irq_domain; + + board_info.irq = adap->client_irq; + adap->client = i2c_new_device(&adap->adapter, &board_info); + if (!adap->client) { + ret = -ENOMEM; + goto del_adapter; + } + + platform_set_drvdata(pdev, adap); + return 0; + +del_adapter: + i2c_del_adapter(&adap->adapter); +remove_irq_domain: + irq_domain_remove(adap->irq_domain); + return ret; +} + +static int cht_wc_i2c_adap_i2c_remove(struct platform_device *pdev) +{ + struct cht_wc_i2c_adap *adap = platform_get_drvdata(pdev); + + i2c_unregister_device(adap->client); + i2c_del_adapter(&adap->adapter); + irq_domain_remove(adap->irq_domain); + + return 0; +} + +static struct platform_device_id cht_wc_i2c_adap_id_table[] = { + { .name = "cht_wcove_ext_chgr" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, cht_wc_i2c_adap_id_table); + +static struct platform_driver cht_wc_i2c_adap_driver = { + .probe = cht_wc_i2c_adap_i2c_probe, + .remove = cht_wc_i2c_adap_i2c_remove, + .driver = { + .name = "cht_wcove_ext_chgr", + }, + .id_table = cht_wc_i2c_adap_id_table, +}; +module_platform_driver(cht_wc_i2c_adap_driver); + +MODULE_DESCRIPTION("Intel CHT Whiskey Cove PMIC I2C Master driver"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index d89bde2c5da2..8a8ca945561b 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -413,7 +413,7 @@ static const struct i2c_algorithm cpm_i2c_algo = { }; /* CPM_MAX_READ is also limiting writes according to the code! */ -static struct i2c_adapter_quirks cpm_i2c_quirks = { +static const struct i2c_adapter_quirks cpm_i2c_quirks = { .max_num_msgs = CPM_MAXBD, .max_read_len = CPM_MAX_READ, .max_write_len = CPM_MAX_READ, diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 9e7ef5cf5d49..b8c43535f16c 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -733,7 +733,7 @@ static inline void i2c_davinci_cpufreq_deregister(struct davinci_i2c_dev *dev) } #endif -static struct i2c_algorithm i2c_davinci_algo = { +static const struct i2c_algorithm i2c_davinci_algo = { .master_xfer = i2c_davinci_xfer, .functionality = i2c_davinci_func, }; @@ -801,7 +801,7 @@ static int davinci_i2c_probe(struct platform_device *pdev) dev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) - return -ENODEV; + return PTR_ERR(dev->clk); clk_prepare_enable(dev->clk); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -876,8 +876,7 @@ static int davinci_i2c_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int davinci_i2c_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct davinci_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct davinci_i2c_dev *i2c_dev = dev_get_drvdata(dev); /* put I2C into reset */ davinci_i2c_reset_ctrl(i2c_dev, 0); @@ -888,8 +887,7 @@ static int davinci_i2c_suspend(struct device *dev) static int davinci_i2c_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct davinci_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct davinci_i2c_dev *i2c_dev = dev_get_drvdata(dev); clk_prepare_enable(i2c_dev->clk); /* take I2C out of reset */ diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 2b98a173136f..0e65b97842b4 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -439,8 +439,7 @@ static void dw_i2c_plat_complete(struct device *dev) #ifdef CONFIG_PM static int dw_i2c_plat_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); + struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); i_dev->disable(i_dev); i2c_dw_plat_prepare_clk(i_dev, false); @@ -450,8 +449,7 @@ static int dw_i2c_plat_runtime_suspend(struct device *dev) static int dw_i2c_plat_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); + struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); i2c_dw_plat_prepare_clk(i_dev, true); i_dev->init(i_dev); diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c index 78d8fb73927d..ea9578ab19a1 100644 --- a/drivers/i2c/busses/i2c-designware-slave.c +++ b/drivers/i2c/busses/i2c-designware-slave.c @@ -346,7 +346,7 @@ static irqreturn_t i2c_dw_isr_slave(int this_irq, void *dev_id) return IRQ_RETVAL(ret); } -static struct i2c_algorithm i2c_dw_algo = { +static const struct i2c_algorithm i2c_dw_algo = { .functionality = i2c_dw_func, .reg_slave = i2c_dw_reg_slave, .unreg_slave = i2c_dw_unreg_slave, diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index 23ed4d67ecad..3855e0b11877 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -803,8 +803,7 @@ static int exynos5_i2c_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int exynos5_i2c_suspend_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct exynos5_i2c *i2c = platform_get_drvdata(pdev); + struct exynos5_i2c *i2c = dev_get_drvdata(dev); i2c->suspended = 1; @@ -815,8 +814,7 @@ static int exynos5_i2c_suspend_noirq(struct device *dev) static int exynos5_i2c_resume_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct exynos5_i2c *i2c = platform_get_drvdata(pdev); + struct exynos5_i2c *i2c = dev_get_drvdata(dev); int ret = 0; ret = clk_prepare_enable(i2c->clk); diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 34cfc0ebdcb9..0ef8fcc6ac3a 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -98,8 +98,8 @@ static int of_i2c_gpio_get_pins(struct device_node *np, return -EPROBE_DEFER; if (!gpio_is_valid(*sda_pin) || !gpio_is_valid(*scl_pin)) { - pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n", - np->full_name, *sda_pin, *scl_pin); + pr_err("%pOF: invalid GPIO pins, sda=%d/scl=%d\n", + np, *sda_pin, *scl_pin); return -ENODEV; } diff --git a/drivers/i2c/busses/i2c-hix5hd2.c b/drivers/i2c/busses/i2c-hix5hd2.c index ae7f3180f7e8..bb68957d3da5 100644 --- a/drivers/i2c/busses/i2c-hix5hd2.c +++ b/drivers/i2c/busses/i2c-hix5hd2.c @@ -505,8 +505,7 @@ static int hix5hd2_i2c_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int hix5hd2_i2c_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct hix5hd2_i2c_priv *priv = platform_get_drvdata(pdev); + struct hix5hd2_i2c_priv *priv = dev_get_drvdata(dev); clk_disable_unprepare(priv->clk); @@ -515,8 +514,7 @@ static int hix5hd2_i2c_runtime_suspend(struct device *dev) static int hix5hd2_i2c_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct hix5hd2_i2c_priv *priv = platform_get_drvdata(pdev); + struct hix5hd2_i2c_priv *priv = dev_get_drvdata(dev); clk_prepare_enable(priv->clk); hix5hd2_i2c_init(priv); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index c9536e17d6ff..e114e4e00d29 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1332,6 +1332,7 @@ static void i801_add_tco(struct i801_priv *priv) u32 tco_base, tco_ctl; u32 base_addr, ctrl_val; u64 base64_addr; + u8 hidden; if (!(priv->features & FEATURE_TCO)) return; @@ -1376,8 +1377,10 @@ static void i801_add_tco(struct i801_priv *priv) devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1); - /* Unhide the P2SB device */ - pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0); + /* Unhide the P2SB device, if it is hidden */ + pci_bus_read_config_byte(pci_dev->bus, devfn, 0xe1, &hidden); + if (hidden) + pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0); pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR, &base_addr); base64_addr = base_addr & 0xfffffff0; @@ -1385,8 +1388,9 @@ static void i801_add_tco(struct i801_priv *priv) pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR + 0x4, &base_addr); base64_addr |= (u64)base_addr << 32; - /* Hide the P2SB device */ - pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x1); + /* Hide the P2SB device, if it was hidden before */ + if (hidden) + pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, hidden); spin_unlock(&p2sb_spinlock); res = &tco_res[ICH_RES_MEM_OFF]; diff --git a/drivers/i2c/busses/i2c-kempld.c b/drivers/i2c/busses/i2c-kempld.c index 25993d2e64bf..e879190b5d1d 100644 --- a/drivers/i2c/busses/i2c-kempld.c +++ b/drivers/i2c/busses/i2c-kempld.c @@ -289,7 +289,7 @@ static const struct i2c_algorithm kempld_i2c_algorithm = { .functionality = kempld_i2c_func, }; -static struct i2c_adapter kempld_i2c_adapter = { +static const struct i2c_adapter kempld_i2c_adapter = { .owner = THIS_MODULE, .name = "i2c-kempld", .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, diff --git a/drivers/i2c/busses/i2c-lpc2k.c b/drivers/i2c/busses/i2c-lpc2k.c index 9b1fef455a89..59167c018ae7 100644 --- a/drivers/i2c/busses/i2c-lpc2k.c +++ b/drivers/i2c/busses/i2c-lpc2k.c @@ -457,8 +457,7 @@ static int i2c_lpc2k_remove(struct platform_device *dev) #ifdef CONFIG_PM static int i2c_lpc2k_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct lpc2k_i2c *i2c = platform_get_drvdata(pdev); + struct lpc2k_i2c *i2c = dev_get_drvdata(dev); clk_disable(i2c->clk); @@ -467,8 +466,7 @@ static int i2c_lpc2k_suspend(struct device *dev) static int i2c_lpc2k_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct lpc2k_i2c *i2c = platform_get_drvdata(pdev); + struct lpc2k_i2c *i2c = dev_get_drvdata(dev); clk_enable(i2c->clk); i2c_lpc2k_reset(i2c); diff --git a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c index d271e6a0954c..4c28fa28ce76 100644 --- a/drivers/i2c/busses/i2c-mlxcpld.c +++ b/drivers/i2c/busses/i2c-mlxcpld.c @@ -433,7 +433,7 @@ static const struct i2c_algorithm mlxcpld_i2c_algo = { .functionality = mlxcpld_i2c_func }; -static struct i2c_adapter_quirks mlxcpld_i2c_quirks = { +static const struct i2c_adapter_quirks mlxcpld_i2c_quirks = { .flags = I2C_AQ_COMB_WRITE_THEN_READ, .max_read_len = MLXCPLD_I2C_DATA_REG_SZ - MLXCPLD_I2C_MAX_ADDR_LEN, .max_write_len = MLXCPLD_I2C_DATA_REG_SZ, diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 45d61714c81b..09d288ce0ddb 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -50,7 +50,6 @@ #define I2C_FS_START_CON 0x1800 #define I2C_TIME_CLR_VALUE 0x0000 #define I2C_TIME_DEFAULT_VALUE 0x0003 -#define I2C_FS_TIME_INIT_VALUE 0x1303 #define I2C_WRRD_TRANAC_VALUE 0x0002 #define I2C_RD_TRANAC_VALUE 0x0001 @@ -154,6 +153,7 @@ struct mtk_i2c { bool use_push_pull; /* IO config push-pull mode */ u16 irq_stat; /* interrupt status */ + unsigned int clk_src_div; unsigned int speed_hz; /* The speed in transfer */ enum mtk_trans_op op; u16 timing_reg; @@ -172,6 +172,10 @@ static const struct i2c_adapter_quirks mt6577_i2c_quirks = { .max_comb_2nd_msg_len = 31, }; +static const struct i2c_adapter_quirks mt7622_i2c_quirks = { + .max_num_msgs = 255, +}; + static const struct mtk_i2c_compatible mt6577_compat = { .quirks = &mt6577_i2c_quirks, .pmic_i2c = 0, @@ -190,6 +194,15 @@ static const struct mtk_i2c_compatible mt6589_compat = { .support_33bits = 0, }; +static const struct mtk_i2c_compatible mt7622_compat = { + .quirks = &mt7622_i2c_quirks, + .pmic_i2c = 0, + .dcm = 1, + .auto_restart = 1, + .aux_len_reg = 1, + .support_33bits = 0, +}; + static const struct mtk_i2c_compatible mt8173_compat = { .pmic_i2c = 0, .dcm = 1, @@ -201,6 +214,7 @@ static const struct mtk_i2c_compatible mt8173_compat = { static const struct of_device_id mtk_i2c_of_match[] = { { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, + { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, {} }; @@ -285,23 +299,20 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c) * less than or equal to i2c->speed_hz. The calculation try to get * sample_cnt and step_cn */ -static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk, - unsigned int clock_div) +static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src, + unsigned int target_speed, + unsigned int *timing_step_cnt, + unsigned int *timing_sample_cnt) { - unsigned int clk_src; unsigned int step_cnt; unsigned int sample_cnt; unsigned int max_step_cnt; - unsigned int target_speed; unsigned int base_sample_cnt = MAX_SAMPLE_CNT_DIV; unsigned int base_step_cnt; unsigned int opt_div; unsigned int best_mul; unsigned int cnt_mul; - clk_src = parent_clk / clock_div; - target_speed = i2c->speed_hz; - if (target_speed > MAX_HS_MODE_SPEED) target_speed = MAX_HS_MODE_SPEED; @@ -347,16 +358,48 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk, return -EINVAL; } - step_cnt--; - sample_cnt--; + *timing_step_cnt = step_cnt - 1; + *timing_sample_cnt = sample_cnt - 1; + + return 0; +} + +static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk) +{ + unsigned int clk_src; + unsigned int step_cnt; + unsigned int sample_cnt; + unsigned int target_speed; + int ret; + + clk_src = parent_clk / i2c->clk_src_div; + target_speed = i2c->speed_hz; if (target_speed > MAX_FS_MODE_SPEED) { + /* Set master code speed register */ + ret = mtk_i2c_calculate_speed(i2c, clk_src, MAX_FS_MODE_SPEED, + &step_cnt, &sample_cnt); + if (ret < 0) + return ret; + + i2c->timing_reg = (sample_cnt << 8) | step_cnt; + /* Set the high speed mode register */ - i2c->timing_reg = I2C_FS_TIME_INIT_VALUE; + ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed, + &step_cnt, &sample_cnt); + if (ret < 0) + return ret; + i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE | (sample_cnt << 12) | (step_cnt << 8); } else { - i2c->timing_reg = (sample_cnt << 8) | (step_cnt << 0); + ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed, + &step_cnt, &sample_cnt); + if (ret < 0) + return ret; + + i2c->timing_reg = (sample_cnt << 8) | step_cnt; + /* Disable the high speed transaction */ i2c->high_speed_reg = I2C_TIME_CLR_VALUE; } @@ -647,8 +690,7 @@ static const struct i2c_algorithm mtk_i2c_algorithm = { .functionality = mtk_i2c_functionality, }; -static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c, - unsigned int *clk_src_div) +static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c) { int ret; @@ -656,11 +698,11 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c, if (ret < 0) i2c->speed_hz = I2C_DEFAULT_SPEED; - ret = of_property_read_u32(np, "clock-div", clk_src_div); + ret = of_property_read_u32(np, "clock-div", &i2c->clk_src_div); if (ret < 0) return ret; - if (*clk_src_div == 0) + if (i2c->clk_src_div == 0) return -EINVAL; i2c->have_pmic = of_property_read_bool(np, "mediatek,have-pmic"); @@ -676,7 +718,6 @@ static int mtk_i2c_probe(struct platform_device *pdev) int ret = 0; struct mtk_i2c *i2c; struct clk *clk; - unsigned int clk_src_div; struct resource *res; int irq; @@ -684,7 +725,7 @@ static int mtk_i2c_probe(struct platform_device *pdev) if (!i2c) return -ENOMEM; - ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c, &clk_src_div); + ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c); if (ret) return -EINVAL; @@ -745,7 +786,7 @@ static int mtk_i2c_probe(struct platform_device *pdev) strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); - ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk), clk_src_div); + ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk)); if (ret) { dev_err(&pdev->dev, "Failed to set the speed.\n"); return -EINVAL; diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 5c4db65c5019..a832c45276a4 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -820,7 +820,7 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, goto out; } - drv_data->rstc = devm_reset_control_get_optional(dev, NULL); + drv_data->rstc = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(drv_data->rstc)) { rc = PTR_ERR(drv_data->rstc); goto out; @@ -975,8 +975,7 @@ mv64xxx_i2c_remove(struct platform_device *dev) #ifdef CONFIG_PM static int mv64xxx_i2c_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(pdev); + struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev); mv64xxx_i2c_hw_init(drv_data); diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index da6609d62848..49c7c0c91486 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -1088,7 +1088,7 @@ static struct i2c_vendor_data vendor_db8500 = { .fifodepth = 32, /* Guessed from TFTR/RFTR = 15 */ }; -static struct amba_id nmk_i2c_ids[] = { +static const struct amba_id nmk_i2c_ids[] = { { .id = 0x00180024, .mask = 0x00ffffff, diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 34f1889a4073..8c42ca7107b2 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -276,7 +276,7 @@ static const struct i2c_algorithm ocores_algorithm = { .functionality = ocores_func, }; -static struct i2c_adapter ocores_adapter = { +static const struct i2c_adapter ocores_adapter = { .owner = THIS_MODULE, .name = "i2c-ocores", .class = I2C_CLASS_DEPRECATED, diff --git a/drivers/i2c/busses/i2c-octeon-platdrv.c b/drivers/i2c/busses/i2c-octeon-platdrv.c index 917524ce6890..64bda83e65ac 100644 --- a/drivers/i2c/busses/i2c-octeon-platdrv.c +++ b/drivers/i2c/busses/i2c-octeon-platdrv.c @@ -126,7 +126,7 @@ static const struct i2c_algorithm octeon_i2c_algo = { .functionality = octeon_i2c_functionality, }; -static struct i2c_adapter octeon_i2c_ops = { +static const struct i2c_adapter octeon_i2c_ops = { .owner = THIS_MODULE, .name = "OCTEON adapter", .algo = &octeon_i2c_algo, diff --git a/drivers/i2c/busses/i2c-opal.c b/drivers/i2c/busses/i2c-opal.c index 11e2a1fc10e9..0aabb7eca0c5 100644 --- a/drivers/i2c/busses/i2c-opal.c +++ b/drivers/i2c/busses/i2c-opal.c @@ -204,7 +204,7 @@ static const struct i2c_algorithm i2c_opal_algo = { * For two messages, we basically support simple smbus transactions of a * write-then-anything. */ -static struct i2c_adapter_quirks i2c_opal_quirks = { +static const struct i2c_adapter_quirks i2c_opal_quirks = { .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR, .max_comb_1st_msg_len = 4, }; diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c index 217c78711d65..2aa0e83174c5 100644 --- a/drivers/i2c/busses/i2c-pmcmsp.c +++ b/drivers/i2c/busses/i2c-pmcmsp.c @@ -577,7 +577,7 @@ static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL; } -static struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = { +static const struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = { .flags = I2C_AQ_COMB_WRITE_THEN_READ, .max_write_len = MSP_MAX_BYTES_PER_RW, .max_read_len = MSP_MAX_BYTES_PER_RW, @@ -587,7 +587,7 @@ static struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = { /* -- Initialization -- */ -static struct i2c_algorithm pmcmsptwi_algo = { +static const struct i2c_algorithm pmcmsptwi_algo = { .master_xfer = pmcmsptwi_master_xfer, .functionality = pmcmsptwi_i2c_func, }; diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index fd5f9d2bf6d9..42d6b3a226f8 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -590,7 +590,7 @@ static u32 i2c_pnx_func(struct i2c_adapter *adapter) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm pnx_algorithm = { +static const struct i2c_algorithm pnx_algorithm = { .master_xfer = i2c_pnx_xfer, .functionality = i2c_pnx_func, }; diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index b0d9dee14a7e..f2a2067525ef 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -197,7 +197,7 @@ static const struct i2c_algorithm i2c_powermac_algorithm = { .functionality = i2c_powermac_func, }; -static struct i2c_adapter_quirks i2c_powermac_quirks = { +static const struct i2c_adapter_quirks i2c_powermac_quirks = { .max_num_msgs = 1, }; @@ -234,7 +234,7 @@ static u32 i2c_powermac_get_addr(struct i2c_adapter *adap, else if (!strcmp(node->name, "deq")) return 0x34; - dev_warn(&adap->dev, "No i2c address for %s\n", node->full_name); + dev_warn(&adap->dev, "No i2c address for %pOF\n", node); return 0xffffffff; } @@ -315,8 +315,7 @@ static bool i2c_powermac_get_type(struct i2c_adapter *adap, } } - dev_err(&adap->dev, "i2c-powermac: modalias failure" - " on %s\n", node->full_name); + dev_err(&adap->dev, "i2c-powermac: modalias failure on %pOF\n", node); return false; } @@ -348,8 +347,7 @@ static void i2c_powermac_register_devices(struct i2c_adapter *adap, if (!pmac_i2c_match_adapter(node, adap)) continue; - dev_dbg(&adap->dev, "i2c-powermac: register %s\n", - node->full_name); + dev_dbg(&adap->dev, "i2c-powermac: register %pOF\n", node); /* * Keep track of some device existence to handle @@ -372,7 +370,7 @@ static void i2c_powermac_register_devices(struct i2c_adapter *adap, newdev = i2c_new_device(adap, &info); if (!newdev) { dev_err(&adap->dev, "i2c-powermac: Failure to register" - " %s\n", node->full_name); + " %pOF\n", node); of_node_put(node); /* We do not dispose of the interrupt mapping on * purpose. It's not necessary (interrupt cannot be diff --git a/drivers/i2c/busses/i2c-puv3.c b/drivers/i2c/busses/i2c-puv3.c index 0c8b1571886d..287088b8c4c8 100644 --- a/drivers/i2c/busses/i2c-puv3.c +++ b/drivers/i2c/busses/i2c-puv3.c @@ -175,7 +175,7 @@ static u32 puv3_i2c_func(struct i2c_adapter *adapter) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm puv3_i2c_algorithm = { +static const struct i2c_algorithm puv3_i2c_algorithm = { .master_xfer = puv3_i2c_xfer, .functionality = puv3_i2c_func, }; diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 6cf333ecc8b8..600d264e080c 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -1346,8 +1346,7 @@ static int i2c_pxa_remove(struct platform_device *dev) #ifdef CONFIG_PM static int i2c_pxa_suspend_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct pxa_i2c *i2c = platform_get_drvdata(pdev); + struct pxa_i2c *i2c = dev_get_drvdata(dev); clk_disable(i2c->clk); @@ -1356,8 +1355,7 @@ static int i2c_pxa_suspend_noirq(struct device *dev) static int i2c_pxa_resume_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct pxa_i2c *i2c = platform_get_drvdata(pdev); + struct pxa_i2c *i2c = dev_get_drvdata(dev); clk_enable(i2c->clk); i2c_pxa_reset(i2c); diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 1902d8ac9753..08f8e0107642 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -1396,7 +1396,7 @@ static const struct i2c_algorithm qup_i2c_algo_v2 = { * the end of the read, the length of the read is specified as one byte * which limits the possible read to 256 (QUP_READ_LIMIT) bytes. */ -static struct i2c_adapter_quirks qup_i2c_quirks = { +static const struct i2c_adapter_quirks qup_i2c_quirks = { .max_read_len = QUP_READ_LIMIT, }; diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 93c1a54981df..15d764afec3b 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -625,9 +625,8 @@ static struct dma_chan *rcar_i2c_request_dma_chan(struct device *dev, chan = dma_request_chan(dev, chan_name); if (IS_ERR(chan)) { - ret = PTR_ERR(chan); - dev_dbg(dev, "request_channel failed for %s (%d)\n", - chan_name, ret); + dev_dbg(dev, "request_channel failed for %s (%ld)\n", + chan_name, PTR_ERR(chan)); return chan; } diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index df220666d627..fe234578380a 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -1131,6 +1131,11 @@ static const struct i2c_algorithm rk3x_i2c_algorithm = { .functionality = rk3x_i2c_func, }; +static const struct rk3x_i2c_soc_data rv1108_soc_data = { + .grf_offset = -1, + .calc_timings = rk3x_i2c_v1_calc_timings, +}; + static const struct rk3x_i2c_soc_data rk3066_soc_data = { .grf_offset = 0x154, .calc_timings = rk3x_i2c_v0_calc_timings, @@ -1158,6 +1163,10 @@ static const struct rk3x_i2c_soc_data rk3399_soc_data = { static const struct of_device_id rk3x_i2c_match[] = { { + .compatible = "rockchip,rv1108-i2c", + .data = (void *)&rv1108_soc_data + }, + { .compatible = "rockchip,rk3066-i2c", .data = (void *)&rk3066_soc_data }, diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 499af26e736e..5d97510ee48b 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -1246,8 +1246,7 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int s3c24xx_i2c_suspend_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); i2c->suspended = 1; @@ -1259,8 +1258,7 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev) static int s3c24xx_i2c_resume_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); int ret; if (!IS_ERR(i2c->sysreg)) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 2e097d97d258..6f2aaeb7c4fa 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -561,8 +561,8 @@ static struct dma_chan *sh_mobile_i2c_request_dma_chan(struct device *dev, chan = dma_request_slave_channel_reason(dev, chan_name); if (IS_ERR(chan)) { - ret = PTR_ERR(chan); - dev_dbg(dev, "request_channel failed for %s (%d)\n", chan_name, ret); + dev_dbg(dev, "request_channel failed for %s (%ld)\n", chan_name, + PTR_ERR(chan)); return chan; } diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c index 95e81d0f72b4..2fd8b6d00391 100644 --- a/drivers/i2c/busses/i2c-sirf.c +++ b/drivers/i2c/busses/i2c-sirf.c @@ -421,8 +421,7 @@ static int i2c_sirfsoc_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int i2c_sirfsoc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct i2c_adapter *adapter = dev_get_drvdata(dev); struct sirfsoc_i2c *siic = adapter->algo_data; clk_enable(siic->clk); @@ -434,8 +433,7 @@ static int i2c_sirfsoc_suspend(struct device *dev) static int i2c_sirfsoc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct i2c_adapter *adapter = dev_get_drvdata(dev); struct sirfsoc_i2c *siic = adapter->algo_data; clk_enable(siic->clk); diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c new file mode 100644 index 000000000000..22e08ae1704f --- /dev/null +++ b/drivers/i2c/busses/i2c-sprd.c @@ -0,0 +1,646 @@ +/* + * Copyright (C) 2017 Spreadtrum Communications Inc. + * + * SPDX-License-Identifier: (GPL-2.0+ OR MIT) + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#define I2C_CTL 0x00 +#define I2C_ADDR_CFG 0x04 +#define I2C_COUNT 0x08 +#define I2C_RX 0x0c +#define I2C_TX 0x10 +#define I2C_STATUS 0x14 +#define I2C_HSMODE_CFG 0x18 +#define I2C_VERSION 0x1c +#define ADDR_DVD0 0x20 +#define ADDR_DVD1 0x24 +#define ADDR_STA0_DVD 0x28 +#define ADDR_RST 0x2c + +/* I2C_CTL */ +#define STP_EN BIT(20) +#define FIFO_AF_LVL_MASK GENMASK(19, 16) +#define FIFO_AF_LVL 16 +#define FIFO_AE_LVL_MASK GENMASK(15, 12) +#define FIFO_AE_LVL 12 +#define I2C_DMA_EN BIT(11) +#define FULL_INTEN BIT(10) +#define EMPTY_INTEN BIT(9) +#define I2C_DVD_OPT BIT(8) +#define I2C_OUT_OPT BIT(7) +#define I2C_TRIM_OPT BIT(6) +#define I2C_HS_MODE BIT(4) +#define I2C_MODE BIT(3) +#define I2C_EN BIT(2) +#define I2C_INT_EN BIT(1) +#define I2C_START BIT(0) + +/* I2C_STATUS */ +#define SDA_IN BIT(21) +#define SCL_IN BIT(20) +#define FIFO_FULL BIT(4) +#define FIFO_EMPTY BIT(3) +#define I2C_INT BIT(2) +#define I2C_RX_ACK BIT(1) +#define I2C_BUSY BIT(0) + +/* ADDR_RST */ +#define I2C_RST BIT(0) + +#define I2C_FIFO_DEEP 12 +#define I2C_FIFO_FULL_THLD 15 +#define I2C_FIFO_EMPTY_THLD 4 +#define I2C_DATA_STEP 8 +#define I2C_ADDR_DVD0_CALC(high, low) \ + ((((high) & GENMASK(15, 0)) << 16) | ((low) & GENMASK(15, 0))) +#define I2C_ADDR_DVD1_CALC(high, low) \ + (((high) & GENMASK(31, 16)) | (((low) & GENMASK(31, 16)) >> 16)) + +/* timeout (ms) for pm runtime autosuspend */ +#define SPRD_I2C_PM_TIMEOUT 1000 + +/* SPRD i2c data structure */ +struct sprd_i2c { + struct i2c_adapter adap; + struct device *dev; + void __iomem *base; + struct i2c_msg *msg; + struct clk *clk; + u32 src_clk; + u32 bus_freq; + struct completion complete; + u8 *buf; + u32 count; + int irq; + int err; +}; + +static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count) +{ + writel(count, i2c_dev->base + I2C_COUNT); +} + +static void sprd_i2c_send_stop(struct sprd_i2c *i2c_dev, int stop) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + if (stop) + writel(tmp & ~STP_EN, i2c_dev->base + I2C_CTL); + else + writel(tmp | STP_EN, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_clear_start(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + writel(tmp & ~I2C_START, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_clear_ack(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_STATUS); + + writel(tmp & ~I2C_RX_ACK, i2c_dev->base + I2C_STATUS); +} + +static void sprd_i2c_clear_irq(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_STATUS); + + writel(tmp & ~I2C_INT, i2c_dev->base + I2C_STATUS); +} + +static void sprd_i2c_reset_fifo(struct sprd_i2c *i2c_dev) +{ + writel(I2C_RST, i2c_dev->base + ADDR_RST); +} + +static void sprd_i2c_set_devaddr(struct sprd_i2c *i2c_dev, struct i2c_msg *m) +{ + writel(m->addr << 1, i2c_dev->base + I2C_ADDR_CFG); +} + +static void sprd_i2c_write_bytes(struct sprd_i2c *i2c_dev, u8 *buf, u32 len) +{ + u32 i; + + for (i = 0; i < len; i++) + writeb(buf[i], i2c_dev->base + I2C_TX); +} + +static void sprd_i2c_read_bytes(struct sprd_i2c *i2c_dev, u8 *buf, u32 len) +{ + u32 i; + + for (i = 0; i < len; i++) + buf[i] = readb(i2c_dev->base + I2C_RX); +} + +static void sprd_i2c_set_full_thld(struct sprd_i2c *i2c_dev, u32 full_thld) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + tmp &= ~FIFO_AF_LVL_MASK; + tmp |= full_thld << FIFO_AF_LVL; + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_set_empty_thld(struct sprd_i2c *i2c_dev, u32 empty_thld) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + tmp &= ~FIFO_AE_LVL_MASK; + tmp |= empty_thld << FIFO_AE_LVL; + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_set_fifo_full_int(struct sprd_i2c *i2c_dev, int enable) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + if (enable) + tmp |= FULL_INTEN; + else + tmp &= ~FULL_INTEN; + + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_set_fifo_empty_int(struct sprd_i2c *i2c_dev, int enable) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + if (enable) + tmp |= EMPTY_INTEN; + else + tmp &= ~EMPTY_INTEN; + + writel(tmp, i2c_dev->base + I2C_CTL); +}; + +static void sprd_i2c_opt_start(struct sprd_i2c *i2c_dev) +{ + u32 tmp = readl(i2c_dev->base + I2C_CTL); + + writel(tmp | I2C_START, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_opt_mode(struct sprd_i2c *i2c_dev, int rw) +{ + u32 cmd = readl(i2c_dev->base + I2C_CTL) & ~I2C_MODE; + + writel(cmd | rw << 3, i2c_dev->base + I2C_CTL); +} + +static void sprd_i2c_data_transfer(struct sprd_i2c *i2c_dev) +{ + u32 i2c_count = i2c_dev->count; + u32 need_tran = i2c_count <= I2C_FIFO_DEEP ? i2c_count : I2C_FIFO_DEEP; + struct i2c_msg *msg = i2c_dev->msg; + + if (msg->flags & I2C_M_RD) { + sprd_i2c_read_bytes(i2c_dev, i2c_dev->buf, I2C_FIFO_FULL_THLD); + i2c_dev->count -= I2C_FIFO_FULL_THLD; + i2c_dev->buf += I2C_FIFO_FULL_THLD; + + /* + * If the read data count is larger than rx fifo full threshold, + * we should enable the rx fifo full interrupt to read data + * again. + */ + if (i2c_dev->count >= I2C_FIFO_FULL_THLD) + sprd_i2c_set_fifo_full_int(i2c_dev, 1); + } else { + sprd_i2c_write_bytes(i2c_dev, i2c_dev->buf, need_tran); + i2c_dev->buf += need_tran; + i2c_dev->count -= need_tran; + + /* + * If the write data count is arger than tx fifo depth which + * means we can not write all data in one time, then we should + * enable the tx fifo empty interrupt to write again. + */ + if (i2c_count > I2C_FIFO_DEEP) + sprd_i2c_set_fifo_empty_int(i2c_dev, 1); + } +} + +static int sprd_i2c_handle_msg(struct i2c_adapter *i2c_adap, + struct i2c_msg *msg, bool is_last_msg) +{ + struct sprd_i2c *i2c_dev = i2c_adap->algo_data; + + i2c_dev->msg = msg; + i2c_dev->buf = msg->buf; + i2c_dev->count = msg->len; + + reinit_completion(&i2c_dev->complete); + sprd_i2c_reset_fifo(i2c_dev); + sprd_i2c_set_devaddr(i2c_dev, msg); + sprd_i2c_set_count(i2c_dev, msg->len); + + if (msg->flags & I2C_M_RD) { + sprd_i2c_opt_mode(i2c_dev, 1); + sprd_i2c_send_stop(i2c_dev, 1); + } else { + sprd_i2c_opt_mode(i2c_dev, 0); + sprd_i2c_send_stop(i2c_dev, !!is_last_msg); + } + + /* + * We should enable rx fifo full interrupt to get data when receiving + * full data. + */ + if (msg->flags & I2C_M_RD) + sprd_i2c_set_fifo_full_int(i2c_dev, 1); + else + sprd_i2c_data_transfer(i2c_dev); + + sprd_i2c_opt_start(i2c_dev); + + wait_for_completion(&i2c_dev->complete); + + return i2c_dev->err; +} + +static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + struct sprd_i2c *i2c_dev = i2c_adap->algo_data; + int im, ret; + + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) + return ret; + + for (im = 0; im < num - 1; im++) { + ret = sprd_i2c_handle_msg(i2c_adap, &msgs[im], 0); + if (ret) + goto err_msg; + } + + ret = sprd_i2c_handle_msg(i2c_adap, &msgs[im++], 1); + +err_msg: + pm_runtime_mark_last_busy(i2c_dev->dev); + pm_runtime_put_autosuspend(i2c_dev->dev); + + return ret < 0 ? ret : im; +} + +static u32 sprd_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm sprd_i2c_algo = { + .master_xfer = sprd_i2c_master_xfer, + .functionality = sprd_i2c_func, +}; + +static void sprd_i2c_set_clk(struct sprd_i2c *i2c_dev, u32 freq) +{ + u32 apb_clk = i2c_dev->src_clk; + /* + * From I2C databook, the prescale calculation formula: + * prescale = freq_i2c / (4 * freq_scl) - 1; + */ + u32 i2c_dvd = apb_clk / (4 * freq) - 1; + /* + * From I2C databook, the high period of SCL clock is recommended as + * 40% (2/5), and the low period of SCL clock is recommended as 60% + * (3/5), then the formula should be: + * high = (prescale * 2 * 2) / 5 + * low = (prescale * 2 * 3) / 5 + */ + u32 high = ((i2c_dvd << 1) * 2) / 5; + u32 low = ((i2c_dvd << 1) * 3) / 5; + u32 div0 = I2C_ADDR_DVD0_CALC(high, low); + u32 div1 = I2C_ADDR_DVD1_CALC(high, low); + + writel(div0, i2c_dev->base + ADDR_DVD0); + writel(div1, i2c_dev->base + ADDR_DVD1); + + /* Start hold timing = hold time(us) * source clock */ + if (freq == 400000) + writel((6 * apb_clk) / 10000000, i2c_dev->base + ADDR_STA0_DVD); + else if (freq == 100000) + writel((4 * apb_clk) / 1000000, i2c_dev->base + ADDR_STA0_DVD); +} + +static void sprd_i2c_enable(struct sprd_i2c *i2c_dev) +{ + u32 tmp = I2C_DVD_OPT; + + writel(tmp, i2c_dev->base + I2C_CTL); + + sprd_i2c_set_full_thld(i2c_dev, I2C_FIFO_FULL_THLD); + sprd_i2c_set_empty_thld(i2c_dev, I2C_FIFO_EMPTY_THLD); + + sprd_i2c_set_clk(i2c_dev, i2c_dev->bus_freq); + sprd_i2c_reset_fifo(i2c_dev); + sprd_i2c_clear_irq(i2c_dev); + + tmp = readl(i2c_dev->base + I2C_CTL); + writel(tmp | I2C_EN | I2C_INT_EN, i2c_dev->base + I2C_CTL); +} + +static irqreturn_t sprd_i2c_isr_thread(int irq, void *dev_id) +{ + struct sprd_i2c *i2c_dev = dev_id; + struct i2c_msg *msg = i2c_dev->msg; + bool ack = !(readl(i2c_dev->base + I2C_STATUS) & I2C_RX_ACK); + u32 i2c_count = readl(i2c_dev->base + I2C_COUNT); + u32 i2c_tran; + + if (msg->flags & I2C_M_RD) + i2c_tran = i2c_dev->count >= I2C_FIFO_FULL_THLD; + else + i2c_tran = i2c_count; + + /* + * If we got one ACK from slave when writing data, and we did not + * finish this transmission (i2c_tran is not zero), then we should + * continue to write data. + * + * For reading data, ack is always true, if i2c_tran is not 0 which + * means we still need to contine to read data from slave. + */ + if (i2c_tran && ack) { + sprd_i2c_data_transfer(i2c_dev); + return IRQ_HANDLED; + } + + i2c_dev->err = 0; + + /* + * If we did not get one ACK from slave when writing data, we should + * return -EIO to notify users. + */ + if (!ack) + i2c_dev->err = -EIO; + else if (msg->flags & I2C_M_RD && i2c_dev->count) + sprd_i2c_read_bytes(i2c_dev, i2c_dev->buf, i2c_dev->count); + + /* Transmission is done and clear ack and start operation */ + sprd_i2c_clear_ack(i2c_dev); + sprd_i2c_clear_start(i2c_dev); + complete(&i2c_dev->complete); + + return IRQ_HANDLED; +} + +static irqreturn_t sprd_i2c_isr(int irq, void *dev_id) +{ + struct sprd_i2c *i2c_dev = dev_id; + struct i2c_msg *msg = i2c_dev->msg; + u32 i2c_count = readl(i2c_dev->base + I2C_COUNT); + bool ack = !(readl(i2c_dev->base + I2C_STATUS) & I2C_RX_ACK); + u32 i2c_tran; + + if (msg->flags & I2C_M_RD) + i2c_tran = i2c_dev->count >= I2C_FIFO_FULL_THLD; + else + i2c_tran = i2c_count; + + /* + * If we did not get one ACK from slave when writing data, then we + * should finish this transmission since we got some errors. + * + * When writing data, if i2c_tran == 0 which means we have writen + * done all data, then we can finish this transmission. + * + * When reading data, if conut < rx fifo full threshold, which + * means we can read all data in one time, then we can finish this + * transmission too. + */ + if (!i2c_tran || !ack) { + sprd_i2c_clear_start(i2c_dev); + sprd_i2c_clear_irq(i2c_dev); + } + + sprd_i2c_set_fifo_empty_int(i2c_dev, 0); + sprd_i2c_set_fifo_full_int(i2c_dev, 0); + + return IRQ_WAKE_THREAD; +} + +static int sprd_i2c_clk_init(struct sprd_i2c *i2c_dev) +{ + struct clk *clk_i2c, *clk_parent; + + clk_i2c = devm_clk_get(i2c_dev->dev, "i2c"); + if (IS_ERR(clk_i2c)) { + dev_warn(i2c_dev->dev, "i2c%d can't get the i2c clock\n", + i2c_dev->adap.nr); + clk_i2c = NULL; + } + + clk_parent = devm_clk_get(i2c_dev->dev, "source"); + if (IS_ERR(clk_parent)) { + dev_warn(i2c_dev->dev, "i2c%d can't get the source clock\n", + i2c_dev->adap.nr); + clk_parent = NULL; + } + + if (clk_set_parent(clk_i2c, clk_parent)) + i2c_dev->src_clk = clk_get_rate(clk_i2c); + else + i2c_dev->src_clk = 26000000; + + dev_dbg(i2c_dev->dev, "i2c%d set source clock is %d\n", + i2c_dev->adap.nr, i2c_dev->src_clk); + + i2c_dev->clk = devm_clk_get(i2c_dev->dev, "enable"); + if (IS_ERR(i2c_dev->clk)) { + dev_warn(i2c_dev->dev, "i2c%d can't get the enable clock\n", + i2c_dev->adap.nr); + i2c_dev->clk = NULL; + } + + return 0; +} + +static int sprd_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sprd_i2c *i2c_dev; + struct resource *res; + u32 prop; + int ret; + + pdev->id = of_alias_get_id(dev->of_node, "i2c"); + + i2c_dev = devm_kzalloc(dev, sizeof(struct sprd_i2c), GFP_KERNEL); + if (!i2c_dev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c_dev->base = devm_ioremap_resource(dev, res); + if (IS_ERR(i2c_dev->base)) + return PTR_ERR(i2c_dev->base); + + i2c_dev->irq = platform_get_irq(pdev, 0); + if (i2c_dev->irq < 0) { + dev_err(&pdev->dev, "failed to get irq resource\n"); + return i2c_dev->irq; + } + + i2c_set_adapdata(&i2c_dev->adap, i2c_dev); + init_completion(&i2c_dev->complete); + snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name), + "%s", "sprd-i2c"); + + i2c_dev->bus_freq = 100000; + i2c_dev->adap.owner = THIS_MODULE; + i2c_dev->dev = dev; + i2c_dev->adap.retries = 3; + i2c_dev->adap.algo = &sprd_i2c_algo; + i2c_dev->adap.algo_data = i2c_dev; + i2c_dev->adap.dev.parent = dev; + i2c_dev->adap.nr = pdev->id; + i2c_dev->adap.dev.of_node = dev->of_node; + + if (!of_property_read_u32(dev->of_node, "clock-frequency", &prop)) + i2c_dev->bus_freq = prop; + + /* We only support 100k and 400k now, otherwise will return error. */ + if (i2c_dev->bus_freq != 100000 && i2c_dev->bus_freq != 400000) + return -EINVAL; + + sprd_i2c_clk_init(i2c_dev); + platform_set_drvdata(pdev, i2c_dev); + + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) + return ret; + + sprd_i2c_enable(i2c_dev); + + pm_runtime_set_autosuspend_delay(i2c_dev->dev, SPRD_I2C_PM_TIMEOUT); + pm_runtime_use_autosuspend(i2c_dev->dev); + pm_runtime_set_active(i2c_dev->dev); + pm_runtime_enable(i2c_dev->dev); + + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) + goto err_rpm_put; + + ret = devm_request_threaded_irq(dev, i2c_dev->irq, + sprd_i2c_isr, sprd_i2c_isr_thread, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + pdev->name, i2c_dev); + if (ret) { + dev_err(&pdev->dev, "failed to request irq %d\n", i2c_dev->irq); + goto err_rpm_put; + } + + ret = i2c_add_numbered_adapter(&i2c_dev->adap); + if (ret) { + dev_err(&pdev->dev, "add adapter failed\n"); + goto err_rpm_put; + } + + pm_runtime_mark_last_busy(i2c_dev->dev); + pm_runtime_put_autosuspend(i2c_dev->dev); + return 0; + +err_rpm_put: + pm_runtime_put_noidle(i2c_dev->dev); + pm_runtime_disable(i2c_dev->dev); + clk_disable_unprepare(i2c_dev->clk); + return ret; +} + +static int sprd_i2c_remove(struct platform_device *pdev) +{ + struct sprd_i2c *i2c_dev = platform_get_drvdata(pdev); + int ret; + + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) + return ret; + + i2c_del_adapter(&i2c_dev->adap); + clk_disable_unprepare(i2c_dev->clk); + + pm_runtime_put_noidle(i2c_dev->dev); + pm_runtime_disable(i2c_dev->dev); + + return 0; +} + +static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev) +{ + return pm_runtime_force_suspend(pdev); +} + +static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev) +{ + return pm_runtime_force_resume(pdev); +} + +static int __maybe_unused sprd_i2c_runtime_suspend(struct device *pdev) +{ + struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + + clk_disable_unprepare(i2c_dev->clk); + + return 0; +} + +static int __maybe_unused sprd_i2c_runtime_resume(struct device *pdev) +{ + struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev); + int ret; + + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) + return ret; + + sprd_i2c_enable(i2c_dev); + + return 0; +} + +static const struct dev_pm_ops sprd_i2c_pm_ops = { + SET_RUNTIME_PM_OPS(sprd_i2c_runtime_suspend, + sprd_i2c_runtime_resume, NULL) + + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sprd_i2c_suspend_noirq, + sprd_i2c_resume_noirq) +}; + +static const struct of_device_id sprd_i2c_of_match[] = { + { .compatible = "sprd,sc9860-i2c", }, +}; + +static struct platform_driver sprd_i2c_driver = { + .probe = sprd_i2c_probe, + .remove = sprd_i2c_remove, + .driver = { + .name = "sprd-i2c", + .of_match_table = sprd_i2c_of_match, + .pm = &sprd_i2c_pm_ops, + }, +}; + +static int sprd_i2c_init(void) +{ + return platform_driver_register(&sprd_i2c_driver); +} +arch_initcall_sync(sprd_i2c_init); diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c index 1eb9fa82dcfd..9e62f893958a 100644 --- a/drivers/i2c/busses/i2c-st.c +++ b/drivers/i2c/busses/i2c-st.c @@ -745,8 +745,7 @@ static int st_i2c_xfer(struct i2c_adapter *i2c_adap, #ifdef CONFIG_PM_SLEEP static int st_i2c_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct st_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + struct st_i2c_dev *i2c_dev = dev_get_drvdata(dev); if (i2c_dev->busy) return -EBUSY; diff --git a/drivers/i2c/busses/i2c-stm32f4.c b/drivers/i2c/busses/i2c-stm32f4.c index f9dd7e86b861..aceb6f788564 100644 --- a/drivers/i2c/busses/i2c-stm32f4.c +++ b/drivers/i2c/busses/i2c-stm32f4.c @@ -751,7 +751,7 @@ static u32 stm32f4_i2c_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm stm32f4_i2c_algo = { +static const struct i2c_algorithm stm32f4_i2c_algo = { .master_xfer = stm32f4_i2c_xfer, .functionality = stm32f4_i2c_func, }; @@ -798,7 +798,7 @@ static int stm32f4_i2c_probe(struct platform_device *pdev) return ret; } - rst = devm_reset_control_get(&pdev->dev, NULL); + rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(rst)) { dev_err(&pdev->dev, "Error: Missing controller reset\n"); ret = PTR_ERR(rst); diff --git a/drivers/i2c/busses/i2c-sun6i-p2wi.c b/drivers/i2c/busses/i2c-sun6i-p2wi.c index 7668e2e9b8fd..7c07ce116e38 100644 --- a/drivers/i2c/busses/i2c-sun6i-p2wi.c +++ b/drivers/i2c/busses/i2c-sun6i-p2wi.c @@ -223,8 +223,8 @@ static int p2wi_probe(struct platform_device *pdev) if (childnp) { ret = of_property_read_u32(childnp, "reg", &slave_addr); if (ret) { - dev_err(dev, "invalid slave address on node %s\n", - childnp->full_name); + dev_err(dev, "invalid slave address on node %pOF\n", + childnp); return -EINVAL; } @@ -258,7 +258,7 @@ static int p2wi_probe(struct platform_device *pdev) parent_clk_freq = clk_get_rate(p2wi->clk); - p2wi->rstc = devm_reset_control_get(dev, NULL); + p2wi->rstc = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(p2wi->rstc)) { ret = PTR_ERR(p2wi->rstc); dev_err(dev, "failed to retrieve reset controller: %d\n", ret); diff --git a/drivers/i2c/busses/i2c-taos-evm.c b/drivers/i2c/busses/i2c-taos-evm.c index 210ca82f8aa0..addd90a8cb59 100644 --- a/drivers/i2c/busses/i2c-taos-evm.c +++ b/drivers/i2c/busses/i2c-taos-evm.c @@ -291,7 +291,7 @@ static void taos_disconnect(struct serio *serio) dev_info(&serio->dev, "Disconnected from TAOS EVM\n"); } -static struct serio_device_id taos_serio_ids[] = { +static const struct serio_device_id taos_serio_ids[] = { { .type = SERIO_RS232, .proto = SERIO_TAOSEVM, diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 4af9bbae20df..60292d243e24 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -793,7 +793,7 @@ static const struct i2c_algorithm tegra_i2c_algo = { }; /* payload size is only 12 bit */ -static struct i2c_adapter_quirks tegra_i2c_quirks = { +static const struct i2c_adapter_quirks tegra_i2c_quirks = { .max_read_len = 4096, .max_write_len = 4096, }; @@ -911,7 +911,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; - i2c_dev->rst = devm_reset_control_get(&pdev->dev, "i2c"); + i2c_dev->rst = devm_reset_control_get_exclusive(&pdev->dev, "i2c"); if (IS_ERR(i2c_dev->rst)) { dev_err(&pdev->dev, "missing controller reset\n"); return PTR_ERR(i2c_dev->rst); diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c index ea35a895b568..df0976f4432a 100644 --- a/drivers/i2c/busses/i2c-thunderx-pcidrv.c +++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c @@ -75,7 +75,7 @@ static const struct i2c_algorithm thunderx_i2c_algo = { .functionality = thunderx_i2c_functionality, }; -static struct i2c_adapter thunderx_i2c_ops = { +static const struct i2c_adapter thunderx_i2c_ops = { .owner = THIS_MODULE, .name = "ThunderX adapter", .algo = &thunderx_i2c_algo, diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c index beee31892295..9918bdd81619 100644 --- a/drivers/i2c/busses/i2c-uniphier-f.c +++ b/drivers/i2c/busses/i2c-uniphier-f.c @@ -97,6 +97,7 @@ struct uniphier_fi2c_priv { int error; unsigned int flags; unsigned int busy_cnt; + unsigned int clk_cycle; }; static void uniphier_fi2c_fill_txfifo(struct uniphier_fi2c_priv *priv, @@ -461,9 +462,9 @@ static struct i2c_bus_recovery_info uniphier_fi2c_bus_recovery_info = { .unprepare_recovery = uniphier_fi2c_unprepare_recovery, }; -static void uniphier_fi2c_hw_init(struct uniphier_fi2c_priv *priv, - u32 bus_speed, unsigned long clk_rate) +static void uniphier_fi2c_hw_init(struct uniphier_fi2c_priv *priv) { + unsigned int cyc = priv->clk_cycle; u32 tmp; tmp = readl(priv->membase + UNIPHIER_FI2C_CR); @@ -472,12 +473,10 @@ static void uniphier_fi2c_hw_init(struct uniphier_fi2c_priv *priv, uniphier_fi2c_reset(priv); - tmp = clk_rate / bus_speed; - - writel(tmp, priv->membase + UNIPHIER_FI2C_CYC); - writel(tmp / 2, priv->membase + UNIPHIER_FI2C_LCTL); - writel(tmp / 2, priv->membase + UNIPHIER_FI2C_SSUT); - writel(tmp / 16, priv->membase + UNIPHIER_FI2C_DSUT); + writel(cyc, priv->membase + UNIPHIER_FI2C_CYC); + writel(cyc / 2, priv->membase + UNIPHIER_FI2C_LCTL); + writel(cyc / 2, priv->membase + UNIPHIER_FI2C_SSUT); + writel(cyc / 16, priv->membase + UNIPHIER_FI2C_DSUT); uniphier_fi2c_prepare_operation(priv); } @@ -531,6 +530,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev) goto disable_clk; } + priv->clk_cycle = clk_rate / bus_speed; init_completion(&priv->comp); priv->adap.owner = THIS_MODULE; priv->adap.algo = &uniphier_fi2c_algo; @@ -541,7 +541,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev) i2c_set_adapdata(&priv->adap, priv); platform_set_drvdata(pdev, priv); - uniphier_fi2c_hw_init(priv, bus_speed, clk_rate); + uniphier_fi2c_hw_init(priv); ret = devm_request_irq(dev, irq, uniphier_fi2c_interrupt, 0, pdev->name, priv); @@ -568,6 +568,33 @@ static int uniphier_fi2c_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused uniphier_fi2c_suspend(struct device *dev) +{ + struct uniphier_fi2c_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int __maybe_unused uniphier_fi2c_resume(struct device *dev) +{ + struct uniphier_fi2c_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + uniphier_fi2c_hw_init(priv); + + return 0; +} + +static const struct dev_pm_ops uniphier_fi2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(uniphier_fi2c_suspend, uniphier_fi2c_resume) +}; + static const struct of_device_id uniphier_fi2c_match[] = { { .compatible = "socionext,uniphier-fi2c" }, { /* sentinel */ } @@ -580,6 +607,7 @@ static struct platform_driver uniphier_fi2c_drv = { .driver = { .name = "uniphier-fi2c", .of_match_table = uniphier_fi2c_match, + .pm = &uniphier_fi2c_pm_ops, }, }; module_platform_driver(uniphier_fi2c_drv); diff --git a/drivers/i2c/busses/i2c-uniphier.c b/drivers/i2c/busses/i2c-uniphier.c index 777c0fe93653..bb181b088291 100644 --- a/drivers/i2c/busses/i2c-uniphier.c +++ b/drivers/i2c/busses/i2c-uniphier.c @@ -53,6 +53,7 @@ struct uniphier_i2c_priv { void __iomem *membase; struct clk *clk; unsigned int busy_cnt; + unsigned int clk_cycle; }; static irqreturn_t uniphier_i2c_interrupt(int irq, void *dev_id) @@ -316,13 +317,13 @@ static struct i2c_bus_recovery_info uniphier_i2c_bus_recovery_info = { .unprepare_recovery = uniphier_i2c_unprepare_recovery, }; -static void uniphier_i2c_hw_init(struct uniphier_i2c_priv *priv, - u32 bus_speed, unsigned long clk_rate) +static void uniphier_i2c_hw_init(struct uniphier_i2c_priv *priv) { + unsigned int cyc = priv->clk_cycle; + uniphier_i2c_reset(priv, true); - writel((clk_rate / bus_speed / 2 << 16) | (clk_rate / bus_speed), - priv->membase + UNIPHIER_I2C_CLK); + writel((cyc / 2 << 16) | cyc, priv->membase + UNIPHIER_I2C_CLK); uniphier_i2c_reset(priv, false); } @@ -376,6 +377,7 @@ static int uniphier_i2c_probe(struct platform_device *pdev) goto disable_clk; } + priv->clk_cycle = clk_rate / bus_speed; init_completion(&priv->comp); priv->adap.owner = THIS_MODULE; priv->adap.algo = &uniphier_i2c_algo; @@ -386,7 +388,7 @@ static int uniphier_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(&priv->adap, priv); platform_set_drvdata(pdev, priv); - uniphier_i2c_hw_init(priv, bus_speed, clk_rate); + uniphier_i2c_hw_init(priv); ret = devm_request_irq(dev, irq, uniphier_i2c_interrupt, 0, pdev->name, priv); @@ -413,6 +415,33 @@ static int uniphier_i2c_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused uniphier_i2c_suspend(struct device *dev) +{ + struct uniphier_i2c_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int __maybe_unused uniphier_i2c_resume(struct device *dev) +{ + struct uniphier_i2c_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + uniphier_i2c_hw_init(priv); + + return 0; +} + +static const struct dev_pm_ops uniphier_i2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(uniphier_i2c_suspend, uniphier_i2c_resume) +}; + static const struct of_device_id uniphier_i2c_match[] = { { .compatible = "socionext,uniphier-i2c" }, { /* sentinel */ } @@ -425,6 +454,7 @@ static struct platform_driver uniphier_i2c_drv = { .driver = { .name = "uniphier-i2c", .of_match_table = uniphier_i2c_match, + .pm = &uniphier_i2c_pm_ops, }, }; module_platform_driver(uniphier_i2c_drv); diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c index c73d2d22009e..f1ab2a637ec0 100644 --- a/drivers/i2c/busses/i2c-versatile.c +++ b/drivers/i2c/busses/i2c-versatile.c @@ -55,7 +55,7 @@ static int i2c_versatile_getscl(void *data) return !!(readl(i2c->base + I2C_CONTROL) & SCL); } -static struct i2c_algo_bit_data i2c_versatile_algo = { +static const struct i2c_algo_bit_data i2c_versatile_algo = { .setsda = i2c_versatile_setsda, .setscl = i2c_versatile_setscl, .getsda = i2c_versatile_getsda, diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 66bce3b311a1..ae6ed254e01d 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -721,7 +721,7 @@ static const struct i2c_algorithm xiic_algorithm = { .functionality = xiic_func, }; -static struct i2c_adapter xiic_adapter = { +static const struct i2c_adapter xiic_adapter = { .owner = THIS_MODULE, .name = DRIVER_NAME, .class = I2C_CLASS_DEPRECATED, @@ -853,8 +853,7 @@ MODULE_DEVICE_TABLE(of, xiic_of_match); static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct xiic_i2c *i2c = platform_get_drvdata(pdev); + struct xiic_i2c *i2c = dev_get_drvdata(dev); clk_disable(i2c->clk); @@ -863,8 +862,7 @@ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev) static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct xiic_i2c *i2c = platform_get_drvdata(pdev); + struct xiic_i2c *i2c = dev_get_drvdata(dev); int ret; ret = clk_enable(i2c->clk); diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c index ccf82fdbcd8e..8d474bb1dc15 100644 --- a/drivers/i2c/i2c-core-of.c +++ b/drivers/i2c/i2c-core-of.c @@ -32,18 +32,17 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, u32 addr; int len; - dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name); + dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node); if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { - dev_err(&adap->dev, "of_i2c: modalias failure on %s\n", - node->full_name); + dev_err(&adap->dev, "of_i2c: modalias failure on %pOF\n", + node); return ERR_PTR(-EINVAL); } addr_be = of_get_property(node, "reg", &len); if (!addr_be || (len < sizeof(*addr_be))) { - dev_err(&adap->dev, "of_i2c: invalid reg on %s\n", - node->full_name); + dev_err(&adap->dev, "of_i2c: invalid reg on %pOF\n", node); return ERR_PTR(-EINVAL); } @@ -59,8 +58,8 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, } if (i2c_check_addr_validity(addr, info.flags)) { - dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n", - addr, node->full_name); + dev_err(&adap->dev, "of_i2c: invalid addr=%x on %pOF\n", + addr, node); return ERR_PTR(-EINVAL); } @@ -76,8 +75,7 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, result = i2c_new_device(adap, &info); if (result == NULL) { - dev_err(&adap->dev, "of_i2c: Failure registering %s\n", - node->full_name); + dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node); of_node_put(node); return ERR_PTR(-EINVAL); } @@ -106,8 +104,8 @@ void of_i2c_register_devices(struct i2c_adapter *adap) client = of_i2c_register_device(adap, node); if (IS_ERR(client)) { dev_warn(&adap->dev, - "Failed to create I2C device for %s\n", - node->full_name); + "Failed to create I2C device for %pOF\n", + node); of_node_clear_flag(node, OF_POPULATED); } } @@ -243,8 +241,8 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action, put_device(&adap->dev); if (IS_ERR(client)) { - dev_err(&adap->dev, "failed to create client for '%s'\n", - rd->dn->full_name); + dev_err(&adap->dev, "failed to create client for '%pOF'\n", + rd->dn); of_node_clear_flag(rd->dn, OF_POPULATED); return notifier_from_errno(PTR_ERR(client)); } diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 17121329bb79..0f5c8fc36625 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -8,7 +8,7 @@ menu "Multiplexer I2C Chip support" config I2C_ARB_GPIO_CHALLENGE tristate "GPIO-based I2C arbitration" depends on GPIOLIB || COMPILE_TEST - depends on OF + depends on OF || COMPILE_TEST help If you say yes to this option, support will be included for an I2C multimaster arbitration scheme using GPIOs and a challenge & @@ -76,6 +76,7 @@ config I2C_MUX_PCA954x config I2C_MUX_PINCTRL tristate "pinctrl-based I2C multiplexer" depends on PINCTRL + depends on OF || COMPILE_TEST help If you say yes to this option, support will be included for an I2C multiplexer that uses the pinctrl subsystem, i.e. pin multiplexing. diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c index 3e6fe1760d82..33ce032cb701 100644 --- a/drivers/i2c/muxes/i2c-demux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c @@ -167,8 +167,8 @@ static ssize_t available_masters_show(struct device *dev, int count = 0, i; for (i = 0; i < priv->num_chan && count < PAGE_SIZE; i++) - count += scnprintf(buf + count, PAGE_SIZE - count, "%d:%s%c", - i, priv->chan[i].parent_np->full_name, + count += scnprintf(buf + count, PAGE_SIZE - count, "%d:%pOF%c", + i, priv->chan[i].parent_np, i == priv->num_chan - 1 ? '\n' : ' '); return count; diff --git a/drivers/i2c/muxes/i2c-mux-mlxcpld.c b/drivers/i2c/muxes/i2c-mux-mlxcpld.c index e53f2abd1350..12ad8d65faf6 100644 --- a/drivers/i2c/muxes/i2c-mux-mlxcpld.c +++ b/drivers/i2c/muxes/i2c-mux-mlxcpld.c @@ -38,9 +38,9 @@ #include <linux/io.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/platform_data/x86/mlxcpld.h> #include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/i2c/mlxcpld.h> #define CPLD_MUX_MAX_NCHANS 8 diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index 9e318c9516c7..6a39adaf433f 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -16,15 +16,14 @@ * warranty of any kind, whether express or implied. */ -#include <linux/module.h> -#include <linux/jiffies.h> #include <linux/delay.h> -#include <linux/slab.h> #include <linux/device.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> - -#include <linux/i2c/pca954x.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/platform_data/pca954x.h> +#include <linux/slab.h> /* * The PCA9541 is a bus master selector. It supports two I2C masters connected diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index f1751c290af6..7b992db38021 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -39,13 +39,13 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> -#include <linux/i2c/pca954x.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_irq.h> +#include <linux/platform_data/pca954x.h> #include <linux/pm.h> #include <linux/slab.h> #include <linux/spinlock.h> diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index 7c0c264b07bc..cc6818aabab5 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -20,17 +20,14 @@ #include <linux/i2c-mux.h> #include <linux/module.h> #include <linux/pinctrl/consumer.h> -#include <linux/i2c-mux-pinctrl.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/of.h> #include "../../pinctrl/core.h" struct i2c_mux_pinctrl { - struct i2c_mux_pinctrl_platform_data *pdata; struct pinctrl *pinctrl; struct pinctrl_state **states; - struct pinctrl_state *state_idle; }; static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) @@ -42,85 +39,9 @@ static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); - - return pinctrl_select_state(mux->pinctrl, mux->state_idle); + return i2c_mux_pinctrl_select(muxc, muxc->num_adapters); } -#ifdef CONFIG_OF -static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, - struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - int num_names, i, ret; - struct device_node *adapter_np; - struct i2c_adapter *adapter; - - if (!np) - return 0; - - mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); - if (!mux->pdata) - return -ENOMEM; - - num_names = of_property_count_strings(np, "pinctrl-names"); - if (num_names < 0) { - dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n", - num_names); - return num_names; - } - - mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, - sizeof(*mux->pdata->pinctrl_states) * num_names, - GFP_KERNEL); - if (!mux->pdata->pinctrl_states) - return -ENOMEM; - - for (i = 0; i < num_names; i++) { - ret = of_property_read_string_index(np, "pinctrl-names", i, - &mux->pdata->pinctrl_states[mux->pdata->bus_count]); - if (ret < 0) { - dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n", - ret); - return ret; - } - if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], - "idle")) { - if (i != num_names - 1) { - dev_err(&pdev->dev, - "idle state must be last\n"); - return -EINVAL; - } - mux->pdata->pinctrl_state_idle = "idle"; - } else { - mux->pdata->bus_count++; - } - } - - adapter_np = of_parse_phandle(np, "i2c-parent", 0); - if (!adapter_np) { - dev_err(&pdev->dev, "Cannot parse i2c-parent\n"); - return -ENODEV; - } - adapter = of_find_i2c_adapter_by_node(adapter_np); - of_node_put(adapter_np); - if (!adapter) { - dev_err(&pdev->dev, "Cannot find parent bus\n"); - return -EPROBE_DEFER; - } - mux->pdata->parent_bus_num = i2c_adapter_id(adapter); - put_device(&adapter->dev); - - return 0; -} -#else -static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, - struct platform_device *pdev) -{ - return 0; -} -#endif - static struct i2c_adapter *i2c_mux_pinctrl_root_adapter( struct pinctrl_state *state) { @@ -141,110 +62,108 @@ static struct i2c_adapter *i2c_mux_pinctrl_root_adapter( return root; } +static struct i2c_adapter *i2c_mux_pinctrl_parent_adapter(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *parent_np; + struct i2c_adapter *parent; + + parent_np = of_parse_phandle(np, "i2c-parent", 0); + if (!parent_np) { + dev_err(dev, "Cannot parse i2c-parent\n"); + return ERR_PTR(-ENODEV); + } + parent = of_find_i2c_adapter_by_node(parent_np); + of_node_put(parent_np); + if (!parent) + return ERR_PTR(-EPROBE_DEFER); + + return parent; +} + static int i2c_mux_pinctrl_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct i2c_mux_core *muxc; struct i2c_mux_pinctrl *mux; + struct i2c_adapter *parent; struct i2c_adapter *root; - int i, ret; - - mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); - if (!mux) { - ret = -ENOMEM; - goto err; - } + int num_names, i, ret; + const char *name; - mux->pdata = dev_get_platdata(&pdev->dev); - if (!mux->pdata) { - ret = i2c_mux_pinctrl_parse_dt(mux, pdev); - if (ret < 0) - goto err; - } - if (!mux->pdata) { - dev_err(&pdev->dev, "Missing platform data\n"); - ret = -ENODEV; - goto err; + num_names = of_property_count_strings(np, "pinctrl-names"); + if (num_names < 0) { + dev_err(dev, "Cannot parse pinctrl-names: %d\n", + num_names); + return num_names; } - mux->states = devm_kzalloc(&pdev->dev, - sizeof(*mux->states) * mux->pdata->bus_count, - GFP_KERNEL); - if (!mux->states) { - dev_err(&pdev->dev, "Cannot allocate states\n"); - ret = -ENOMEM; - goto err; - } + parent = i2c_mux_pinctrl_parent_adapter(dev); + if (IS_ERR(parent)) + return PTR_ERR(parent); - muxc = i2c_mux_alloc(NULL, &pdev->dev, mux->pdata->bus_count, 0, 0, - i2c_mux_pinctrl_select, NULL); + muxc = i2c_mux_alloc(parent, dev, num_names, + sizeof(*mux) + num_names * sizeof(*mux->states), + 0, i2c_mux_pinctrl_select, NULL); if (!muxc) { ret = -ENOMEM; - goto err; + goto err_put_parent; } - muxc->priv = mux; + mux = i2c_mux_priv(muxc); + mux->states = (struct pinctrl_state **)(mux + 1); platform_set_drvdata(pdev, muxc); - mux->pinctrl = devm_pinctrl_get(&pdev->dev); + mux->pinctrl = devm_pinctrl_get(dev); if (IS_ERR(mux->pinctrl)) { ret = PTR_ERR(mux->pinctrl); - dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret); - goto err; + dev_err(dev, "Cannot get pinctrl: %d\n", ret); + goto err_put_parent; } - for (i = 0; i < mux->pdata->bus_count; i++) { - mux->states[i] = pinctrl_lookup_state(mux->pinctrl, - mux->pdata->pinctrl_states[i]); + + for (i = 0; i < num_names; i++) { + ret = of_property_read_string_index(np, "pinctrl-names", i, + &name); + if (ret < 0) { + dev_err(dev, "Cannot parse pinctrl-names: %d\n", ret); + goto err_put_parent; + } + + mux->states[i] = pinctrl_lookup_state(mux->pinctrl, name); if (IS_ERR(mux->states[i])) { ret = PTR_ERR(mux->states[i]); - dev_err(&pdev->dev, - "Cannot look up pinctrl state %s: %d\n", - mux->pdata->pinctrl_states[i], ret); - goto err; - } - } - if (mux->pdata->pinctrl_state_idle) { - mux->state_idle = pinctrl_lookup_state(mux->pinctrl, - mux->pdata->pinctrl_state_idle); - if (IS_ERR(mux->state_idle)) { - ret = PTR_ERR(mux->state_idle); - dev_err(&pdev->dev, - "Cannot look up pinctrl state %s: %d\n", - mux->pdata->pinctrl_state_idle, ret); - goto err; + dev_err(dev, "Cannot look up pinctrl state %s: %d\n", + name, ret); + goto err_put_parent; } - muxc->deselect = i2c_mux_pinctrl_deselect; - } + if (strcmp(name, "idle")) + continue; - muxc->parent = i2c_get_adapter(mux->pdata->parent_bus_num); - if (!muxc->parent) { - dev_err(&pdev->dev, "Parent adapter (%d) not found\n", - mux->pdata->parent_bus_num); - ret = -EPROBE_DEFER; - goto err; + if (i != num_names - 1) { + dev_err(dev, "idle state must be last\n"); + ret = -EINVAL; + goto err_put_parent; + } + muxc->deselect = i2c_mux_pinctrl_deselect; } root = i2c_root_adapter(&muxc->parent->dev); muxc->mux_locked = true; - for (i = 0; i < mux->pdata->bus_count; i++) { + for (i = 0; i < num_names; i++) { if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) { muxc->mux_locked = false; break; } } - if (muxc->mux_locked && mux->pdata->pinctrl_state_idle && - root != i2c_mux_pinctrl_root_adapter(mux->state_idle)) - muxc->mux_locked = false; - if (muxc->mux_locked) - dev_info(&pdev->dev, "mux-locked i2c mux\n"); + dev_info(dev, "mux-locked i2c mux\n"); - for (i = 0; i < mux->pdata->bus_count; i++) { - u32 bus = mux->pdata->base_bus_num ? - (mux->pdata->base_bus_num + i) : 0; - - ret = i2c_mux_add_adapter(muxc, bus, i, 0); + /* Do not add any adapter for the idle state (if it's there at all). */ + for (i = 0; i < num_names - !!muxc->deselect; i++) { + ret = i2c_mux_add_adapter(muxc, 0, i, 0); if (ret) goto err_del_adapter; } @@ -253,8 +172,9 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) err_del_adapter: i2c_mux_del_adapters(muxc); - i2c_put_adapter(muxc->parent); -err: +err_put_parent: + i2c_put_adapter(parent); + return ret; } @@ -264,16 +184,15 @@ static int i2c_mux_pinctrl_remove(struct platform_device *pdev) i2c_mux_del_adapters(muxc); i2c_put_adapter(muxc->parent); + return 0; } -#ifdef CONFIG_OF static const struct of_device_id i2c_mux_pinctrl_of_match[] = { { .compatible = "i2c-mux-pinctrl", }, {}, }; MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match); -#endif static struct platform_driver i2c_mux_pinctrl_driver = { .driver = { diff --git a/drivers/infiniband/core/rw.c b/drivers/infiniband/core/rw.c index dbfd854c32c9..6ca607e8e293 100644 --- a/drivers/infiniband/core/rw.c +++ b/drivers/infiniband/core/rw.c @@ -643,6 +643,30 @@ void rdma_rw_ctx_destroy_signature(struct rdma_rw_ctx *ctx, struct ib_qp *qp, } EXPORT_SYMBOL(rdma_rw_ctx_destroy_signature); +/** + * rdma_rw_mr_factor - return number of MRs required for a payload + * @device: device handling the connection + * @port_num: port num to which the connection is bound + * @maxpages: maximum payload pages per rdma_rw_ctx + * + * Returns the number of MRs the device requires to move @maxpayload + * bytes. The returned value is used during transport creation to + * compute max_rdma_ctxts and the size of the transport's Send and + * Send Completion Queues. + */ +unsigned int rdma_rw_mr_factor(struct ib_device *device, u8 port_num, + unsigned int maxpages) +{ + unsigned int mr_pages; + + if (rdma_rw_can_use_mr(device, port_num)) + mr_pages = rdma_rw_fr_page_list_len(device); + else + mr_pages = device->attrs.max_sge_rd; + return DIV_ROUND_UP(maxpages, mr_pages); +} +EXPORT_SYMBOL(rdma_rw_mr_factor); + void rdma_rw_init_qp(struct ib_device *dev, struct ib_qp_init_attr *attr) { u32 factor; diff --git a/drivers/infiniband/core/umem_rbtree.c b/drivers/infiniband/core/umem_rbtree.c index d176597b4d78..fc801920e341 100644 --- a/drivers/infiniband/core/umem_rbtree.c +++ b/drivers/infiniband/core/umem_rbtree.c @@ -72,7 +72,7 @@ INTERVAL_TREE_DEFINE(struct umem_odp_node, rb, u64, __subtree_last, /* @last is not a part of the interval. See comment for function * node_last. */ -int rbt_ib_umem_for_each_in_range(struct rb_root *root, +int rbt_ib_umem_for_each_in_range(struct rb_root_cached *root, u64 start, u64 last, umem_call_back cb, void *cookie) @@ -95,7 +95,7 @@ int rbt_ib_umem_for_each_in_range(struct rb_root *root, } EXPORT_SYMBOL(rbt_ib_umem_for_each_in_range); -struct ib_umem_odp *rbt_ib_umem_lookup(struct rb_root *root, +struct ib_umem_odp *rbt_ib_umem_lookup(struct rb_root_cached *root, u64 addr, u64 length) { struct umem_odp_node *node; diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index e0cb99860934..4ab30d832ac5 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -118,7 +118,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file, ucontext->closing = 0; #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - ucontext->umem_tree = RB_ROOT; + ucontext->umem_tree = RB_ROOT_CACHED; init_rwsem(&ucontext->umem_rwsem); ucontext->odp_mrs_count = 0; INIT_LIST_HEAD(&ucontext->no_private_counters); diff --git a/drivers/infiniband/hw/hfi1/mmu_rb.c b/drivers/infiniband/hw/hfi1/mmu_rb.c index 2f0d285dc278..175002c046ed 100644 --- a/drivers/infiniband/hw/hfi1/mmu_rb.c +++ b/drivers/infiniband/hw/hfi1/mmu_rb.c @@ -54,7 +54,7 @@ struct mmu_rb_handler { struct mmu_notifier mn; - struct rb_root root; + struct rb_root_cached root; void *ops_arg; spinlock_t lock; /* protect the RB tree */ struct mmu_rb_ops *ops; @@ -108,7 +108,7 @@ int hfi1_mmu_rb_register(void *ops_arg, struct mm_struct *mm, if (!handlr) return -ENOMEM; - handlr->root = RB_ROOT; + handlr->root = RB_ROOT_CACHED; handlr->ops = ops; handlr->ops_arg = ops_arg; INIT_HLIST_NODE(&handlr->mn.hlist); @@ -149,9 +149,9 @@ void hfi1_mmu_rb_unregister(struct mmu_rb_handler *handler) INIT_LIST_HEAD(&del_list); spin_lock_irqsave(&handler->lock, flags); - while ((node = rb_first(&handler->root))) { + while ((node = rb_first_cached(&handler->root))) { rbnode = rb_entry(node, struct mmu_rb_node, node); - rb_erase(node, &handler->root); + rb_erase_cached(node, &handler->root); /* move from LRU list to delete list */ list_move(&rbnode->list, &del_list); } @@ -300,7 +300,7 @@ static void mmu_notifier_mem_invalidate(struct mmu_notifier *mn, { struct mmu_rb_handler *handler = container_of(mn, struct mmu_rb_handler, mn); - struct rb_root *root = &handler->root; + struct rb_root_cached *root = &handler->root; struct mmu_rb_node *node, *ptr = NULL; unsigned long flags; bool added = false; diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c index 0ba5ba7540c8..e219093d2764 100644 --- a/drivers/infiniband/hw/mlx4/sysfs.c +++ b/drivers/infiniband/hw/mlx4/sysfs.c @@ -221,7 +221,7 @@ void del_sysfs_port_mcg_attr(struct mlx4_ib_dev *device, int port_num, static int add_port_entries(struct mlx4_ib_dev *device, int port_num) { int i; - char buff[10]; + char buff[11]; struct mlx4_ib_iov_port *port = NULL; int ret = 0 ; struct ib_port_attr attr; diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c index c49db7c33979..4381c0a9a873 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.c +++ b/drivers/infiniband/hw/usnic/usnic_uiom.c @@ -227,7 +227,7 @@ static void __usnic_uiom_reg_release(struct usnic_uiom_pd *pd, vpn_last = vpn_start + npages - 1; spin_lock(&pd->lock); - usnic_uiom_remove_interval(&pd->rb_root, vpn_start, + usnic_uiom_remove_interval(&pd->root, vpn_start, vpn_last, &rm_intervals); usnic_uiom_unmap_sorted_intervals(&rm_intervals, pd); @@ -379,7 +379,7 @@ struct usnic_uiom_reg *usnic_uiom_reg_get(struct usnic_uiom_pd *pd, err = usnic_uiom_get_intervals_diff(vpn_start, vpn_last, (writable) ? IOMMU_WRITE : 0, IOMMU_WRITE, - &pd->rb_root, + &pd->root, &sorted_diff_intervals); if (err) { usnic_err("Failed disjoint interval vpn [0x%lx,0x%lx] err %d\n", @@ -395,7 +395,7 @@ struct usnic_uiom_reg *usnic_uiom_reg_get(struct usnic_uiom_pd *pd, } - err = usnic_uiom_insert_interval(&pd->rb_root, vpn_start, vpn_last, + err = usnic_uiom_insert_interval(&pd->root, vpn_start, vpn_last, (writable) ? IOMMU_WRITE : 0); if (err) { usnic_err("Failed insert interval vpn [0x%lx,0x%lx] err %d\n", diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.h b/drivers/infiniband/hw/usnic/usnic_uiom.h index 45ca7c1613a7..431efe4143f4 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.h +++ b/drivers/infiniband/hw/usnic/usnic_uiom.h @@ -55,7 +55,7 @@ struct usnic_uiom_dev { struct usnic_uiom_pd { struct iommu_domain *domain; spinlock_t lock; - struct rb_root rb_root; + struct rb_root_cached root; struct list_head devs; int dev_cnt; }; diff --git a/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.c b/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.c index 42b4b4c4e452..d399523206c7 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.c +++ b/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.c @@ -100,9 +100,9 @@ static int interval_cmp(void *priv, struct list_head *a, struct list_head *b) } static void -find_intervals_intersection_sorted(struct rb_root *root, unsigned long start, - unsigned long last, - struct list_head *list) +find_intervals_intersection_sorted(struct rb_root_cached *root, + unsigned long start, unsigned long last, + struct list_head *list) { struct usnic_uiom_interval_node *node; @@ -118,7 +118,7 @@ find_intervals_intersection_sorted(struct rb_root *root, unsigned long start, int usnic_uiom_get_intervals_diff(unsigned long start, unsigned long last, int flags, int flag_mask, - struct rb_root *root, + struct rb_root_cached *root, struct list_head *diff_set) { struct usnic_uiom_interval_node *interval, *tmp; @@ -175,7 +175,7 @@ void usnic_uiom_put_interval_set(struct list_head *intervals) kfree(interval); } -int usnic_uiom_insert_interval(struct rb_root *root, unsigned long start, +int usnic_uiom_insert_interval(struct rb_root_cached *root, unsigned long start, unsigned long last, int flags) { struct usnic_uiom_interval_node *interval, *tmp; @@ -246,8 +246,9 @@ err_out: return err; } -void usnic_uiom_remove_interval(struct rb_root *root, unsigned long start, - unsigned long last, struct list_head *removed) +void usnic_uiom_remove_interval(struct rb_root_cached *root, + unsigned long start, unsigned long last, + struct list_head *removed) { struct usnic_uiom_interval_node *interval; diff --git a/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.h b/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.h index c0b0b876ab90..1d7fc3226bca 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.h +++ b/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.h @@ -48,12 +48,12 @@ struct usnic_uiom_interval_node { extern void usnic_uiom_interval_tree_insert(struct usnic_uiom_interval_node *node, - struct rb_root *root); + struct rb_root_cached *root); extern void usnic_uiom_interval_tree_remove(struct usnic_uiom_interval_node *node, - struct rb_root *root); + struct rb_root_cached *root); extern struct usnic_uiom_interval_node * -usnic_uiom_interval_tree_iter_first(struct rb_root *root, +usnic_uiom_interval_tree_iter_first(struct rb_root_cached *root, unsigned long start, unsigned long last); extern struct usnic_uiom_interval_node * @@ -63,7 +63,7 @@ usnic_uiom_interval_tree_iter_next(struct usnic_uiom_interval_node *node, * Inserts {start...last} into {root}. If there are overlaps, * nodes will be broken up and merged */ -int usnic_uiom_insert_interval(struct rb_root *root, +int usnic_uiom_insert_interval(struct rb_root_cached *root, unsigned long start, unsigned long last, int flags); /* @@ -71,7 +71,7 @@ int usnic_uiom_insert_interval(struct rb_root *root, * 'removed.' The caller is responsibile for freeing memory of nodes in * 'removed.' */ -void usnic_uiom_remove_interval(struct rb_root *root, +void usnic_uiom_remove_interval(struct rb_root_cached *root, unsigned long start, unsigned long last, struct list_head *removed); /* @@ -81,7 +81,7 @@ void usnic_uiom_remove_interval(struct rb_root *root, int usnic_uiom_get_intervals_diff(unsigned long start, unsigned long last, int flags, int flag_mask, - struct rb_root *root, + struct rb_root_cached *root, struct list_head *diff_set); /* Call this to free diff_set returned by usnic_uiom_get_intervals_diff */ void usnic_uiom_put_interval_set(struct list_head *intervals); diff --git a/drivers/input/touchscreen/htcpen.c b/drivers/input/touchscreen/htcpen.c index 92e2243fb77d..8fd909285877 100644 --- a/drivers/input/touchscreen/htcpen.c +++ b/drivers/input/touchscreen/htcpen.c @@ -219,7 +219,7 @@ static struct isa_driver htcpen_isa_driver = { } }; -static struct dmi_system_id htcshift_dmi_table[] __initdata = { +static const struct dmi_system_id htcshift_dmi_table[] __initconst = { { .ident = "Shift", .matches = { diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index f73ff28f77e2..49bd2ab8c507 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -76,6 +76,8 @@ config IOMMU_DMA config FSL_PAMU bool "Freescale IOMMU support" + depends on PCI + depends on PHYS_64BIT depends on PPC_E500MC || (COMPILE_TEST && PPC) select IOMMU_API select GENERIC_ALLOCATOR @@ -253,6 +255,7 @@ config TEGRA_IOMMU_SMMU config EXYNOS_IOMMU bool "Exynos IOMMU Support" depends on ARCH_EXYNOS && MMU + depends on !CPU_BIG_ENDIAN # revisit driver if we can enable big-endian ptes select IOMMU_API select ARM_DMA_USE_IOMMU help @@ -367,4 +370,14 @@ config MTK_IOMMU_V1 if unsure, say N here. +config QCOM_IOMMU + # Note: iommu drivers cannot (yet?) be built as modules + bool "Qualcomm IOMMU Support" + depends on ARCH_QCOM || COMPILE_TEST + select IOMMU_API + select IOMMU_IO_PGTABLE_LPAE + select ARM_DMA_USE_IOMMU + help + Support for IOMMU on certain Qualcomm SoCs. + endif # IOMMU_SUPPORT diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 195f7b997d8e..b910aea813a1 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o obj-$(CONFIG_S390_IOMMU) += s390-iommu.o +obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 4ad7e5e31943..51f8215877f5 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -103,29 +103,6 @@ int amd_iommu_max_glx_val = -1; static const struct dma_map_ops amd_iommu_dma_ops; /* - * This struct contains device specific data for the IOMMU - */ -struct iommu_dev_data { - struct list_head list; /* For domain->dev_list */ - struct list_head dev_data_list; /* For global dev_data_list */ - struct protection_domain *domain; /* Domain the device is bound to */ - u16 devid; /* PCI Device ID */ - u16 alias; /* Alias Device ID */ - bool iommu_v2; /* Device can make use of IOMMUv2 */ - bool passthrough; /* Device is identity mapped */ - struct { - bool enabled; - int qdep; - } ats; /* ATS state */ - bool pri_tlp; /* PASID TLB required for - PPR completions */ - u32 errata; /* Bitmap for errata to apply */ - bool use_vapic; /* Enable device to use vapic mode */ - - struct ratelimit_state rs; /* Ratelimit IOPF messages */ -}; - -/* * general struct to manage commands send to an IOMMU */ struct iommu_cmd { @@ -137,20 +114,7 @@ struct kmem_cache *amd_iommu_irq_cache; static void update_domain(struct protection_domain *domain); static int protection_domain_init(struct protection_domain *domain); static void detach_device(struct device *dev); - -#define FLUSH_QUEUE_SIZE 256 - -struct flush_queue_entry { - unsigned long iova_pfn; - unsigned long pages; - u64 counter; /* Flush counter when this entry was added to the queue */ -}; - -struct flush_queue { - struct flush_queue_entry *entries; - unsigned head, tail; - spinlock_t lock; -}; +static void iova_domain_flush_tlb(struct iova_domain *iovad); /* * Data container for a dma_ops specific protection domain @@ -161,36 +125,6 @@ struct dma_ops_domain { /* IOVA RB-Tree */ struct iova_domain iovad; - - struct flush_queue __percpu *flush_queue; - - /* - * We need two counter here to be race-free wrt. IOTLB flushing and - * adding entries to the flush queue. - * - * The flush_start_cnt is incremented _before_ the IOTLB flush starts. - * New entries added to the flush ring-buffer get their 'counter' value - * from here. This way we can make sure that entries added to the queue - * (or other per-cpu queues of the same domain) while the TLB is about - * to be flushed are not considered to be flushed already. - */ - atomic64_t flush_start_cnt; - - /* - * The flush_finish_cnt is incremented when an IOTLB flush is complete. - * This value is always smaller than flush_start_cnt. The queue_add - * function frees all IOVAs that have a counter value smaller than - * flush_finish_cnt. This makes sure that we only free IOVAs that are - * flushed out of the IOTLB of the domain. - */ - atomic64_t flush_finish_cnt; - - /* - * Timer to make sure we don't keep IOVAs around unflushed - * for too long - */ - struct timer_list flush_timer; - atomic_t flush_timer_on; }; static struct iova_domain reserved_iova_ranges; @@ -371,19 +305,25 @@ static u16 get_alias(struct device *dev) static struct iommu_dev_data *find_dev_data(u16 devid) { struct iommu_dev_data *dev_data; + struct amd_iommu *iommu = amd_iommu_rlookup_table[devid]; dev_data = search_dev_data(devid); - if (dev_data == NULL) + if (dev_data == NULL) { dev_data = alloc_dev_data(devid); + if (translation_pre_enabled(iommu)) + dev_data->defer_attach = true; + } + return dev_data; } -static struct iommu_dev_data *get_dev_data(struct device *dev) +struct iommu_dev_data *get_dev_data(struct device *dev) { return dev->archdata.iommu; } +EXPORT_SYMBOL(get_dev_data); /* * Find or create an IOMMU group for a acpihid device. @@ -1167,7 +1107,7 @@ static int iommu_flush_dte(struct amd_iommu *iommu, u16 devid) return iommu_queue_command(iommu, &cmd); } -static void iommu_flush_dte_all(struct amd_iommu *iommu) +static void amd_iommu_flush_dte_all(struct amd_iommu *iommu) { u32 devid; @@ -1181,7 +1121,7 @@ static void iommu_flush_dte_all(struct amd_iommu *iommu) * This function uses heavy locking and may disable irqs for some time. But * this is no issue because it is only called during resume. */ -static void iommu_flush_tlb_all(struct amd_iommu *iommu) +static void amd_iommu_flush_tlb_all(struct amd_iommu *iommu) { u32 dom_id; @@ -1195,7 +1135,7 @@ static void iommu_flush_tlb_all(struct amd_iommu *iommu) iommu_completion_wait(iommu); } -static void iommu_flush_all(struct amd_iommu *iommu) +static void amd_iommu_flush_all(struct amd_iommu *iommu) { struct iommu_cmd cmd; @@ -1214,7 +1154,7 @@ static void iommu_flush_irt(struct amd_iommu *iommu, u16 devid) iommu_queue_command(iommu, &cmd); } -static void iommu_flush_irt_all(struct amd_iommu *iommu) +static void amd_iommu_flush_irt_all(struct amd_iommu *iommu) { u32 devid; @@ -1227,11 +1167,11 @@ static void iommu_flush_irt_all(struct amd_iommu *iommu) void iommu_flush_all_caches(struct amd_iommu *iommu) { if (iommu_feature(iommu, FEATURE_IA)) { - iommu_flush_all(iommu); + amd_iommu_flush_all(iommu); } else { - iommu_flush_dte_all(iommu); - iommu_flush_irt_all(iommu); - iommu_flush_tlb_all(iommu); + amd_iommu_flush_dte_all(iommu); + amd_iommu_flush_irt_all(iommu); + amd_iommu_flush_tlb_all(iommu); } } @@ -1539,9 +1479,9 @@ static int iommu_map_page(struct protection_domain *dom, if (count > 1) { __pte = PAGE_SIZE_PTE(__sme_set(phys_addr), page_size); - __pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_P | IOMMU_PTE_FC; + __pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_PR | IOMMU_PTE_FC; } else - __pte = __sme_set(phys_addr) | IOMMU_PTE_P | IOMMU_PTE_FC; + __pte = __sme_set(phys_addr) | IOMMU_PTE_PR | IOMMU_PTE_FC; if (prot & IOMMU_PROT_IR) __pte |= IOMMU_PTE_IR; @@ -1790,178 +1730,19 @@ static void free_gcr3_table(struct protection_domain *domain) free_page((unsigned long)domain->gcr3_tbl); } -static void dma_ops_domain_free_flush_queue(struct dma_ops_domain *dom) -{ - int cpu; - - for_each_possible_cpu(cpu) { - struct flush_queue *queue; - - queue = per_cpu_ptr(dom->flush_queue, cpu); - kfree(queue->entries); - } - - free_percpu(dom->flush_queue); - - dom->flush_queue = NULL; -} - -static int dma_ops_domain_alloc_flush_queue(struct dma_ops_domain *dom) -{ - int cpu; - - atomic64_set(&dom->flush_start_cnt, 0); - atomic64_set(&dom->flush_finish_cnt, 0); - - dom->flush_queue = alloc_percpu(struct flush_queue); - if (!dom->flush_queue) - return -ENOMEM; - - /* First make sure everything is cleared */ - for_each_possible_cpu(cpu) { - struct flush_queue *queue; - - queue = per_cpu_ptr(dom->flush_queue, cpu); - queue->head = 0; - queue->tail = 0; - queue->entries = NULL; - } - - /* Now start doing the allocation */ - for_each_possible_cpu(cpu) { - struct flush_queue *queue; - - queue = per_cpu_ptr(dom->flush_queue, cpu); - queue->entries = kzalloc(FLUSH_QUEUE_SIZE * sizeof(*queue->entries), - GFP_KERNEL); - if (!queue->entries) { - dma_ops_domain_free_flush_queue(dom); - return -ENOMEM; - } - - spin_lock_init(&queue->lock); - } - - return 0; -} - static void dma_ops_domain_flush_tlb(struct dma_ops_domain *dom) { - atomic64_inc(&dom->flush_start_cnt); domain_flush_tlb(&dom->domain); domain_flush_complete(&dom->domain); - atomic64_inc(&dom->flush_finish_cnt); -} - -static inline bool queue_ring_full(struct flush_queue *queue) -{ - assert_spin_locked(&queue->lock); - - return (((queue->tail + 1) % FLUSH_QUEUE_SIZE) == queue->head); -} - -#define queue_ring_for_each(i, q) \ - for (i = (q)->head; i != (q)->tail; i = (i + 1) % FLUSH_QUEUE_SIZE) - -static inline unsigned queue_ring_add(struct flush_queue *queue) -{ - unsigned idx = queue->tail; - - assert_spin_locked(&queue->lock); - queue->tail = (idx + 1) % FLUSH_QUEUE_SIZE; - - return idx; -} - -static inline void queue_ring_remove_head(struct flush_queue *queue) -{ - assert_spin_locked(&queue->lock); - queue->head = (queue->head + 1) % FLUSH_QUEUE_SIZE; -} - -static void queue_ring_free_flushed(struct dma_ops_domain *dom, - struct flush_queue *queue) -{ - u64 counter = atomic64_read(&dom->flush_finish_cnt); - int idx; - - queue_ring_for_each(idx, queue) { - /* - * This assumes that counter values in the ring-buffer are - * monotonously rising. - */ - if (queue->entries[idx].counter >= counter) - break; - - free_iova_fast(&dom->iovad, - queue->entries[idx].iova_pfn, - queue->entries[idx].pages); - - queue_ring_remove_head(queue); - } -} - -static void queue_add(struct dma_ops_domain *dom, - unsigned long address, unsigned long pages) -{ - struct flush_queue *queue; - unsigned long flags; - int idx; - - pages = __roundup_pow_of_two(pages); - address >>= PAGE_SHIFT; - - queue = get_cpu_ptr(dom->flush_queue); - spin_lock_irqsave(&queue->lock, flags); - - /* - * First remove the enries from the ring-buffer that are already - * flushed to make the below queue_ring_full() check less likely - */ - queue_ring_free_flushed(dom, queue); - - /* - * When ring-queue is full, flush the entries from the IOTLB so - * that we can free all entries with queue_ring_free_flushed() - * below. - */ - if (queue_ring_full(queue)) { - dma_ops_domain_flush_tlb(dom); - queue_ring_free_flushed(dom, queue); - } - - idx = queue_ring_add(queue); - - queue->entries[idx].iova_pfn = address; - queue->entries[idx].pages = pages; - queue->entries[idx].counter = atomic64_read(&dom->flush_start_cnt); - - spin_unlock_irqrestore(&queue->lock, flags); - - if (atomic_cmpxchg(&dom->flush_timer_on, 0, 1) == 0) - mod_timer(&dom->flush_timer, jiffies + msecs_to_jiffies(10)); - - put_cpu_ptr(dom->flush_queue); } -static void queue_flush_timeout(unsigned long data) +static void iova_domain_flush_tlb(struct iova_domain *iovad) { - struct dma_ops_domain *dom = (struct dma_ops_domain *)data; - int cpu; + struct dma_ops_domain *dom; - atomic_set(&dom->flush_timer_on, 0); + dom = container_of(iovad, struct dma_ops_domain, iovad); dma_ops_domain_flush_tlb(dom); - - for_each_possible_cpu(cpu) { - struct flush_queue *queue; - unsigned long flags; - - queue = per_cpu_ptr(dom->flush_queue, cpu); - spin_lock_irqsave(&queue->lock, flags); - queue_ring_free_flushed(dom, queue); - spin_unlock_irqrestore(&queue->lock, flags); - } } /* @@ -1975,11 +1756,6 @@ static void dma_ops_domain_free(struct dma_ops_domain *dom) del_domain_from_list(&dom->domain); - if (timer_pending(&dom->flush_timer)) - del_timer(&dom->flush_timer); - - dma_ops_domain_free_flush_queue(dom); - put_iova_domain(&dom->iovad); free_pagetable(&dom->domain); @@ -2015,16 +1791,11 @@ static struct dma_ops_domain *dma_ops_domain_alloc(void) init_iova_domain(&dma_dom->iovad, PAGE_SIZE, IOVA_START_PFN, DMA_32BIT_PFN); - /* Initialize reserved ranges */ - copy_reserved_iova(&reserved_iova_ranges, &dma_dom->iovad); - - if (dma_ops_domain_alloc_flush_queue(dma_dom)) + if (init_iova_flush_queue(&dma_dom->iovad, iova_domain_flush_tlb, NULL)) goto free_dma_dom; - setup_timer(&dma_dom->flush_timer, queue_flush_timeout, - (unsigned long)dma_dom); - - atomic_set(&dma_dom->flush_timer_on, 0); + /* Initialize reserved ranges */ + copy_reserved_iova(&reserved_iova_ranges, &dma_dom->iovad); add_domain_to_list(&dma_dom->domain); @@ -2055,7 +1826,7 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats) pte_root |= (domain->mode & DEV_ENTRY_MODE_MASK) << DEV_ENTRY_MODE_SHIFT; - pte_root |= IOMMU_PTE_IR | IOMMU_PTE_IW | IOMMU_PTE_P | IOMMU_PTE_TV; + pte_root |= DTE_FLAG_IR | DTE_FLAG_IW | DTE_FLAG_V | DTE_FLAG_TV; flags = amd_iommu_dev_table[devid].data[1]; @@ -2088,8 +1859,7 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats) flags |= tmp; } - - flags &= ~(DTE_FLAG_SA | 0xffffULL); + flags &= ~DEV_DOMID_MASK; flags |= domain->id; amd_iommu_dev_table[devid].data[1] = flags; @@ -2099,7 +1869,7 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats) static void clear_dte_entry(u16 devid) { /* remove entry from the device table seen by the hardware */ - amd_iommu_dev_table[devid].data[0] = IOMMU_PTE_P | IOMMU_PTE_TV; + amd_iommu_dev_table[devid].data[0] = DTE_FLAG_V | DTE_FLAG_TV; amd_iommu_dev_table[devid].data[1] &= DTE_FLAG_MASK; amd_iommu_apply_erratum_63(devid); @@ -2480,11 +2250,21 @@ static struct iommu_group *amd_iommu_device_group(struct device *dev) static struct protection_domain *get_domain(struct device *dev) { struct protection_domain *domain; + struct iommu_domain *io_domain; if (!check_device(dev)) return ERR_PTR(-EINVAL); domain = get_dev_data(dev)->domain; + if (domain == NULL && get_dev_data(dev)->defer_attach) { + get_dev_data(dev)->defer_attach = false; + io_domain = iommu_get_domain_for_dev(dev); + domain = to_pdomain(io_domain); + attach_device(dev, domain); + } + if (domain == NULL) + return ERR_PTR(-EBUSY); + if (!dma_ops_domain(domain)) return ERR_PTR(-EBUSY); @@ -2530,6 +2310,7 @@ static int dir2prot(enum dma_data_direction direction) else return 0; } + /* * This function contains common code for mapping of a physically * contiguous memory region into DMA address space. It is used by all @@ -2621,7 +2402,8 @@ static void __unmap_single(struct dma_ops_domain *dma_dom, domain_flush_tlb(&dma_dom->domain); domain_flush_complete(&dma_dom->domain); } else { - queue_add(dma_dom, dma_addr, pages); + pages = __roundup_pow_of_two(pages); + queue_iova(&dma_dom->iovad, dma_addr >> PAGE_SHIFT, pages, 0); } } @@ -3375,6 +3157,13 @@ static void amd_iommu_apply_resv_region(struct device *dev, WARN_ON_ONCE(reserve_iova(&dma_dom->iovad, start, end) == NULL); } +static bool amd_iommu_is_attach_deferred(struct iommu_domain *domain, + struct device *dev) +{ + struct iommu_dev_data *dev_data = dev->archdata.iommu; + return dev_data->defer_attach; +} + const struct iommu_ops amd_iommu_ops = { .capable = amd_iommu_capable, .domain_alloc = amd_iommu_domain_alloc, @@ -3391,6 +3180,7 @@ const struct iommu_ops amd_iommu_ops = { .get_resv_regions = amd_iommu_get_resv_regions, .put_resv_regions = amd_iommu_put_resv_regions, .apply_resv_region = amd_iommu_apply_resv_region, + .is_attach_deferred = amd_iommu_is_attach_deferred, .pgsize_bitmap = AMD_IOMMU_PGSIZES, }; @@ -3779,11 +3569,6 @@ EXPORT_SYMBOL(amd_iommu_device_info); static struct irq_chip amd_ir_chip; -#define DTE_IRQ_PHYS_ADDR_MASK (((1ULL << 45)-1) << 6) -#define DTE_IRQ_REMAP_INTCTL (2ULL << 60) -#define DTE_IRQ_TABLE_LEN (8ULL << 1) -#define DTE_IRQ_REMAP_ENABLE 1ULL - static void set_dte_irq_entry(u16 devid, struct irq_remap_table *table) { u64 dte; diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 2292a6cece76..382de42b8359 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -29,7 +29,6 @@ #include <linux/export.h> #include <linux/iommu.h> #include <linux/kmemleak.h> -#include <linux/crash_dump.h> #include <linux/mem_encrypt.h> #include <asm/pci-direct.h> #include <asm/iommu.h> @@ -39,6 +38,7 @@ #include <asm/io_apic.h> #include <asm/irq_remapping.h> +#include <linux/crash_dump.h> #include "amd_iommu_proto.h" #include "amd_iommu_types.h" #include "irq_remapping.h" @@ -197,6 +197,11 @@ spinlock_t amd_iommu_pd_lock; * page table root pointer. */ struct dev_table_entry *amd_iommu_dev_table; +/* + * Pointer to a device table which the content of old device table + * will be copied to. It's only be used in kdump kernel. + */ +static struct dev_table_entry *old_dev_tbl_cpy; /* * The alias table is a driver specific data structure which contains the @@ -210,6 +215,7 @@ u16 *amd_iommu_alias_table; * for a specific device. It is also indexed by the PCI device id. */ struct amd_iommu **amd_iommu_rlookup_table; +EXPORT_SYMBOL(amd_iommu_rlookup_table); /* * This table is used to find the irq remapping table for a given device id @@ -259,6 +265,28 @@ static int amd_iommu_enable_interrupts(void); static int __init iommu_go_to_state(enum iommu_init_state state); static void init_device_table_dma(void); +static bool amd_iommu_pre_enabled = true; + +bool translation_pre_enabled(struct amd_iommu *iommu) +{ + return (iommu->flags & AMD_IOMMU_FLAG_TRANS_PRE_ENABLED); +} +EXPORT_SYMBOL(translation_pre_enabled); + +static void clear_translation_pre_enabled(struct amd_iommu *iommu) +{ + iommu->flags &= ~AMD_IOMMU_FLAG_TRANS_PRE_ENABLED; +} + +static void init_translation_status(struct amd_iommu *iommu) +{ + u32 ctrl; + + ctrl = readl(iommu->mmio_base + MMIO_CONTROL_OFFSET); + if (ctrl & (1<<CONTROL_IOMMU_EN)) + iommu->flags |= AMD_IOMMU_FLAG_TRANS_PRE_ENABLED; +} + static inline void update_last_devid(u16 devid) { if (devid > amd_iommu_last_bdf) @@ -616,6 +644,14 @@ static void iommu_enable_command_buffer(struct amd_iommu *iommu) amd_iommu_reset_cmd_buffer(iommu); } +/* + * This function disables the command buffer + */ +static void iommu_disable_command_buffer(struct amd_iommu *iommu) +{ + iommu_feature_disable(iommu, CONTROL_CMDBUF_EN); +} + static void __init free_command_buffer(struct amd_iommu *iommu) { free_pages((unsigned long)iommu->cmd_buf, get_order(CMD_BUFFER_SIZE)); @@ -648,6 +684,14 @@ static void iommu_enable_event_buffer(struct amd_iommu *iommu) iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN); } +/* + * This function disables the event log buffer + */ +static void iommu_disable_event_buffer(struct amd_iommu *iommu) +{ + iommu_feature_disable(iommu, CONTROL_EVT_LOG_EN); +} + static void __init free_event_buffer(struct amd_iommu *iommu) { free_pages((unsigned long)iommu->evt_buf, get_order(EVT_BUFFER_SIZE)); @@ -809,6 +853,96 @@ static int get_dev_entry_bit(u16 devid, u8 bit) } +static bool copy_device_table(void) +{ + u64 int_ctl, int_tab_len, entry = 0, last_entry = 0; + struct dev_table_entry *old_devtb = NULL; + u32 lo, hi, devid, old_devtb_size; + phys_addr_t old_devtb_phys; + struct amd_iommu *iommu; + u16 dom_id, dte_v, irq_v; + gfp_t gfp_flag; + u64 tmp; + + if (!amd_iommu_pre_enabled) + return false; + + pr_warn("Translation is already enabled - trying to copy translation structures\n"); + for_each_iommu(iommu) { + /* All IOMMUs should use the same device table with the same size */ + lo = readl(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET); + hi = readl(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET + 4); + entry = (((u64) hi) << 32) + lo; + if (last_entry && last_entry != entry) { + pr_err("IOMMU:%d should use the same dev table as others!/n", + iommu->index); + return false; + } + last_entry = entry; + + old_devtb_size = ((entry & ~PAGE_MASK) + 1) << 12; + if (old_devtb_size != dev_table_size) { + pr_err("The device table size of IOMMU:%d is not expected!/n", + iommu->index); + return false; + } + } + + old_devtb_phys = entry & PAGE_MASK; + if (old_devtb_phys >= 0x100000000ULL) { + pr_err("The address of old device table is above 4G, not trustworthy!/n"); + return false; + } + old_devtb = memremap(old_devtb_phys, dev_table_size, MEMREMAP_WB); + if (!old_devtb) + return false; + + gfp_flag = GFP_KERNEL | __GFP_ZERO | GFP_DMA32; + old_dev_tbl_cpy = (void *)__get_free_pages(gfp_flag, + get_order(dev_table_size)); + if (old_dev_tbl_cpy == NULL) { + pr_err("Failed to allocate memory for copying old device table!/n"); + return false; + } + + for (devid = 0; devid <= amd_iommu_last_bdf; ++devid) { + old_dev_tbl_cpy[devid] = old_devtb[devid]; + dom_id = old_devtb[devid].data[1] & DEV_DOMID_MASK; + dte_v = old_devtb[devid].data[0] & DTE_FLAG_V; + + if (dte_v && dom_id) { + old_dev_tbl_cpy[devid].data[0] = old_devtb[devid].data[0]; + old_dev_tbl_cpy[devid].data[1] = old_devtb[devid].data[1]; + __set_bit(dom_id, amd_iommu_pd_alloc_bitmap); + /* If gcr3 table existed, mask it out */ + if (old_devtb[devid].data[0] & DTE_FLAG_GV) { + tmp = DTE_GCR3_VAL_B(~0ULL) << DTE_GCR3_SHIFT_B; + tmp |= DTE_GCR3_VAL_C(~0ULL) << DTE_GCR3_SHIFT_C; + old_dev_tbl_cpy[devid].data[1] &= ~tmp; + tmp = DTE_GCR3_VAL_A(~0ULL) << DTE_GCR3_SHIFT_A; + tmp |= DTE_FLAG_GV; + old_dev_tbl_cpy[devid].data[0] &= ~tmp; + } + } + + irq_v = old_devtb[devid].data[2] & DTE_IRQ_REMAP_ENABLE; + int_ctl = old_devtb[devid].data[2] & DTE_IRQ_REMAP_INTCTL_MASK; + int_tab_len = old_devtb[devid].data[2] & DTE_IRQ_TABLE_LEN_MASK; + if (irq_v && (int_ctl || int_tab_len)) { + if ((int_ctl != DTE_IRQ_REMAP_INTCTL) || + (int_tab_len != DTE_IRQ_TABLE_LEN)) { + pr_err("Wrong old irq remapping flag: %#x\n", devid); + return false; + } + + old_dev_tbl_cpy[devid].data[2] = old_devtb[devid].data[2]; + } + } + memunmap(old_devtb); + + return true; +} + void amd_iommu_apply_erratum_63(u16 devid) { int sysmgt; @@ -1400,6 +1534,16 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h) iommu->int_enabled = false; + init_translation_status(iommu); + if (translation_pre_enabled(iommu) && !is_kdump_kernel()) { + iommu_disable(iommu); + clear_translation_pre_enabled(iommu); + pr_warn("Translation was enabled for IOMMU:%d but we are not in kdump mode\n", + iommu->index); + } + if (amd_iommu_pre_enabled) + amd_iommu_pre_enabled = translation_pre_enabled(iommu); + ret = init_iommu_from_acpi(iommu, h); if (ret) return ret; @@ -1893,8 +2037,7 @@ static int __init init_memory_definitions(struct acpi_table_header *table) } /* - * Init the device table to not allow DMA access for devices and - * suppress all page faults + * Init the device table to not allow DMA access for devices */ static void init_device_table_dma(void) { @@ -1903,14 +2046,6 @@ static void init_device_table_dma(void) for (devid = 0; devid <= amd_iommu_last_bdf; ++devid) { set_dev_entry_bit(devid, DEV_ENTRY_VALID); set_dev_entry_bit(devid, DEV_ENTRY_TRANSLATION); - /* - * In kdump kernels in-flight DMA from the old kernel might - * cause IO_PAGE_FAULTs. There are no reports that a kdump - * actually failed because of that, so just disable fault - * reporting in the hardware to get rid of the messages - */ - if (is_kdump_kernel()) - set_dev_entry_bit(devid, DEV_ENTRY_NO_PAGE_FAULT); } } @@ -2023,24 +2158,62 @@ static void iommu_enable_ga(struct amd_iommu *iommu) #endif } +static void early_enable_iommu(struct amd_iommu *iommu) +{ + iommu_disable(iommu); + iommu_init_flags(iommu); + iommu_set_device_table(iommu); + iommu_enable_command_buffer(iommu); + iommu_enable_event_buffer(iommu); + iommu_set_exclusion_range(iommu); + iommu_enable_ga(iommu); + iommu_enable(iommu); + iommu_flush_all_caches(iommu); +} + /* * This function finally enables all IOMMUs found in the system after - * they have been initialized + * they have been initialized. + * + * Or if in kdump kernel and IOMMUs are all pre-enabled, try to copy + * the old content of device table entries. Not this case or copy failed, + * just continue as normal kernel does. */ static void early_enable_iommus(void) { struct amd_iommu *iommu; - for_each_iommu(iommu) { - iommu_disable(iommu); - iommu_init_flags(iommu); - iommu_set_device_table(iommu); - iommu_enable_command_buffer(iommu); - iommu_enable_event_buffer(iommu); - iommu_set_exclusion_range(iommu); - iommu_enable_ga(iommu); - iommu_enable(iommu); - iommu_flush_all_caches(iommu); + + if (!copy_device_table()) { + /* + * If come here because of failure in copying device table from old + * kernel with all IOMMUs enabled, print error message and try to + * free allocated old_dev_tbl_cpy. + */ + if (amd_iommu_pre_enabled) + pr_err("Failed to copy DEV table from previous kernel.\n"); + if (old_dev_tbl_cpy != NULL) + free_pages((unsigned long)old_dev_tbl_cpy, + get_order(dev_table_size)); + + for_each_iommu(iommu) { + clear_translation_pre_enabled(iommu); + early_enable_iommu(iommu); + } + } else { + pr_info("Copied DEV table from previous kernel.\n"); + free_pages((unsigned long)amd_iommu_dev_table, + get_order(dev_table_size)); + amd_iommu_dev_table = old_dev_tbl_cpy; + for_each_iommu(iommu) { + iommu_disable_command_buffer(iommu); + iommu_disable_event_buffer(iommu); + iommu_enable_command_buffer(iommu); + iommu_enable_event_buffer(iommu); + iommu_enable_ga(iommu); + iommu_set_device_table(iommu); + iommu_flush_all_caches(iommu); + } } #ifdef CONFIG_IRQ_REMAP @@ -2276,7 +2449,8 @@ static int __init early_amd_iommu_init(void) /* Device table - directly used by all IOMMUs */ ret = -ENOMEM; - amd_iommu_dev_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, + amd_iommu_dev_table = (void *)__get_free_pages( + GFP_KERNEL | __GFP_ZERO | GFP_DMA32, get_order(dev_table_size)); if (amd_iommu_dev_table == NULL) goto out; @@ -2326,7 +2500,8 @@ static int __init early_amd_iommu_init(void) goto out; /* Disable any previously enabled IOMMUs */ - disable_iommus(); + if (!is_kdump_kernel() || amd_iommu_disabled) + disable_iommus(); if (amd_iommu_irq_remap) amd_iommu_irq_remap = check_ioapic_information(); diff --git a/drivers/iommu/amd_iommu_proto.h b/drivers/iommu/amd_iommu_proto.h index 3f12fb2338ea..640c286a0ab9 100644 --- a/drivers/iommu/amd_iommu_proto.h +++ b/drivers/iommu/amd_iommu_proto.h @@ -97,4 +97,6 @@ static inline void *iommu_phys_to_virt(unsigned long paddr) return phys_to_virt(__sme_clr(paddr)); } +extern bool translation_pre_enabled(struct amd_iommu *iommu); +extern struct iommu_dev_data *get_dev_data(struct device *dev); #endif /* _ASM_X86_AMD_IOMMU_PROTO_H */ diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index 8e3a85759242..f6b24c7d8b70 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -250,6 +250,14 @@ #define GA_GUEST_NR 0x1 +/* Bit value definition for dte irq remapping fields*/ +#define DTE_IRQ_PHYS_ADDR_MASK (((1ULL << 45)-1) << 6) +#define DTE_IRQ_REMAP_INTCTL_MASK (0x3ULL << 60) +#define DTE_IRQ_TABLE_LEN_MASK (0xfULL << 1) +#define DTE_IRQ_REMAP_INTCTL (2ULL << 60) +#define DTE_IRQ_TABLE_LEN (8ULL << 1) +#define DTE_IRQ_REMAP_ENABLE 1ULL + #define PAGE_MODE_NONE 0x00 #define PAGE_MODE_1_LEVEL 0x01 #define PAGE_MODE_2_LEVEL 0x02 @@ -265,7 +273,7 @@ #define PM_LEVEL_INDEX(x, a) (((a) >> PM_LEVEL_SHIFT((x))) & 0x1ffULL) #define PM_LEVEL_ENC(x) (((x) << 9) & 0xe00ULL) #define PM_LEVEL_PDE(x, a) ((a) | PM_LEVEL_ENC((x)) | \ - IOMMU_PTE_P | IOMMU_PTE_IR | IOMMU_PTE_IW) + IOMMU_PTE_PR | IOMMU_PTE_IR | IOMMU_PTE_IW) #define PM_PTE_LEVEL(pte) (((pte) >> 9) & 0x7ULL) #define PM_MAP_4k 0 @@ -314,19 +322,29 @@ #define PTE_LEVEL_PAGE_SIZE(level) \ (1ULL << (12 + (9 * (level)))) -#define IOMMU_PTE_P (1ULL << 0) -#define IOMMU_PTE_TV (1ULL << 1) +/* + * Bit value definition for I/O PTE fields + */ +#define IOMMU_PTE_PR (1ULL << 0) #define IOMMU_PTE_U (1ULL << 59) #define IOMMU_PTE_FC (1ULL << 60) #define IOMMU_PTE_IR (1ULL << 61) #define IOMMU_PTE_IW (1ULL << 62) +/* + * Bit value definition for DTE fields + */ +#define DTE_FLAG_V (1ULL << 0) +#define DTE_FLAG_TV (1ULL << 1) +#define DTE_FLAG_IR (1ULL << 61) +#define DTE_FLAG_IW (1ULL << 62) + #define DTE_FLAG_IOTLB (1ULL << 32) -#define DTE_FLAG_SA (1ULL << 34) #define DTE_FLAG_GV (1ULL << 55) #define DTE_FLAG_MASK (0x3ffULL << 32) #define DTE_GLX_SHIFT (56) #define DTE_GLX_MASK (3) +#define DEV_DOMID_MASK 0xffffULL #define DTE_GCR3_VAL_A(x) (((x) >> 12) & 0x00007ULL) #define DTE_GCR3_VAL_B(x) (((x) >> 15) & 0x0ffffULL) @@ -343,7 +361,7 @@ #define GCR3_VALID 0x01ULL #define IOMMU_PAGE_MASK (((1ULL << 52) - 1) & ~0xfffULL) -#define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_P) +#define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_PR) #define IOMMU_PTE_PAGE(pte) (iommu_phys_to_virt((pte) & IOMMU_PAGE_MASK)) #define IOMMU_PTE_MODE(pte) (((pte) >> 9) & 0x07) @@ -435,6 +453,8 @@ struct iommu_domain; struct irq_domain; struct amd_irte_ops; +#define AMD_IOMMU_FLAG_TRANS_PRE_ENABLED (1 << 0) + /* * This structure contains generic data for IOMMU protection domains * independent of their use. @@ -569,6 +589,7 @@ struct amd_iommu { struct amd_irte_ops *irte_ops; #endif + u32 flags; volatile u64 __aligned(8) cmd_sem; }; @@ -599,6 +620,30 @@ struct devid_map { bool cmd_line; }; +/* + * This struct contains device specific data for the IOMMU + */ +struct iommu_dev_data { + struct list_head list; /* For domain->dev_list */ + struct list_head dev_data_list; /* For global dev_data_list */ + struct protection_domain *domain; /* Domain the device is bound to */ + u16 devid; /* PCI Device ID */ + u16 alias; /* Alias Device ID */ + bool iommu_v2; /* Device can make use of IOMMUv2 */ + bool passthrough; /* Device is identity mapped */ + struct { + bool enabled; + int qdep; + } ats; /* ATS state */ + bool pri_tlp; /* PASID TLB required for + PPR completions */ + u32 errata; /* Bitmap for errata to apply */ + bool use_vapic; /* Enable device to use vapic mode */ + bool defer_attach; + + struct ratelimit_state rs; /* Ratelimit IOPF messages */ +}; + /* Map HPET and IOAPIC ids to the devid used by the IOMMU */ extern struct list_head ioapic_map; extern struct list_head hpet_map; diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index dccf5b76eff2..7d94e1d39e5e 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -554,14 +554,30 @@ static int ppr_notifier(struct notifier_block *nb, unsigned long e, void *data) unsigned long flags; struct fault *fault; bool finish; - u16 tag; + u16 tag, devid; int ret; + struct iommu_dev_data *dev_data; + struct pci_dev *pdev = NULL; iommu_fault = data; tag = iommu_fault->tag & 0x1ff; finish = (iommu_fault->tag >> 9) & 1; + devid = iommu_fault->device_id; + pdev = pci_get_bus_and_slot(PCI_BUS_NUM(devid), devid & 0xff); + if (!pdev) + return -ENODEV; + dev_data = get_dev_data(&pdev->dev); + + /* In kdump kernel pci dev is not initialized yet -> send INVALID */ ret = NOTIFY_DONE; + if (translation_pre_enabled(amd_iommu_rlookup_table[devid]) + && dev_data->defer_attach) { + amd_iommu_complete_ppr(pdev, iommu_fault->pasid, + PPR_INVALID, tag); + goto out; + } + dev_state = get_device_state(iommu_fault->device_id); if (dev_state == NULL) goto out; diff --git a/drivers/iommu/arm-smmu-regs.h b/drivers/iommu/arm-smmu-regs.h new file mode 100644 index 000000000000..a1226e4ab5f8 --- /dev/null +++ b/drivers/iommu/arm-smmu-regs.h @@ -0,0 +1,220 @@ +/* + * IOMMU API for ARM architected SMMU implementations. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2013 ARM Limited + * + * Author: Will Deacon <will.deacon@arm.com> + */ + +#ifndef _ARM_SMMU_REGS_H +#define _ARM_SMMU_REGS_H + +/* Configuration registers */ +#define ARM_SMMU_GR0_sCR0 0x0 +#define sCR0_CLIENTPD (1 << 0) +#define sCR0_GFRE (1 << 1) +#define sCR0_GFIE (1 << 2) +#define sCR0_EXIDENABLE (1 << 3) +#define sCR0_GCFGFRE (1 << 4) +#define sCR0_GCFGFIE (1 << 5) +#define sCR0_USFCFG (1 << 10) +#define sCR0_VMIDPNE (1 << 11) +#define sCR0_PTM (1 << 12) +#define sCR0_FB (1 << 13) +#define sCR0_VMID16EN (1 << 31) +#define sCR0_BSU_SHIFT 14 +#define sCR0_BSU_MASK 0x3 + +/* Auxiliary Configuration register */ +#define ARM_SMMU_GR0_sACR 0x10 + +/* Identification registers */ +#define ARM_SMMU_GR0_ID0 0x20 +#define ARM_SMMU_GR0_ID1 0x24 +#define ARM_SMMU_GR0_ID2 0x28 +#define ARM_SMMU_GR0_ID3 0x2c +#define ARM_SMMU_GR0_ID4 0x30 +#define ARM_SMMU_GR0_ID5 0x34 +#define ARM_SMMU_GR0_ID6 0x38 +#define ARM_SMMU_GR0_ID7 0x3c +#define ARM_SMMU_GR0_sGFSR 0x48 +#define ARM_SMMU_GR0_sGFSYNR0 0x50 +#define ARM_SMMU_GR0_sGFSYNR1 0x54 +#define ARM_SMMU_GR0_sGFSYNR2 0x58 + +#define ID0_S1TS (1 << 30) +#define ID0_S2TS (1 << 29) +#define ID0_NTS (1 << 28) +#define ID0_SMS (1 << 27) +#define ID0_ATOSNS (1 << 26) +#define ID0_PTFS_NO_AARCH32 (1 << 25) +#define ID0_PTFS_NO_AARCH32S (1 << 24) +#define ID0_CTTW (1 << 14) +#define ID0_NUMIRPT_SHIFT 16 +#define ID0_NUMIRPT_MASK 0xff +#define ID0_NUMSIDB_SHIFT 9 +#define ID0_NUMSIDB_MASK 0xf +#define ID0_EXIDS (1 << 8) +#define ID0_NUMSMRG_SHIFT 0 +#define ID0_NUMSMRG_MASK 0xff + +#define ID1_PAGESIZE (1 << 31) +#define ID1_NUMPAGENDXB_SHIFT 28 +#define ID1_NUMPAGENDXB_MASK 7 +#define ID1_NUMS2CB_SHIFT 16 +#define ID1_NUMS2CB_MASK 0xff +#define ID1_NUMCB_SHIFT 0 +#define ID1_NUMCB_MASK 0xff + +#define ID2_OAS_SHIFT 4 +#define ID2_OAS_MASK 0xf +#define ID2_IAS_SHIFT 0 +#define ID2_IAS_MASK 0xf +#define ID2_UBS_SHIFT 8 +#define ID2_UBS_MASK 0xf +#define ID2_PTFS_4K (1 << 12) +#define ID2_PTFS_16K (1 << 13) +#define ID2_PTFS_64K (1 << 14) +#define ID2_VMID16 (1 << 15) + +#define ID7_MAJOR_SHIFT 4 +#define ID7_MAJOR_MASK 0xf + +/* Global TLB invalidation */ +#define ARM_SMMU_GR0_TLBIVMID 0x64 +#define ARM_SMMU_GR0_TLBIALLNSNH 0x68 +#define ARM_SMMU_GR0_TLBIALLH 0x6c +#define ARM_SMMU_GR0_sTLBGSYNC 0x70 +#define ARM_SMMU_GR0_sTLBGSTATUS 0x74 +#define sTLBGSTATUS_GSACTIVE (1 << 0) + +/* Stream mapping registers */ +#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2)) +#define SMR_VALID (1 << 31) +#define SMR_MASK_SHIFT 16 +#define SMR_ID_SHIFT 0 + +#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2)) +#define S2CR_CBNDX_SHIFT 0 +#define S2CR_CBNDX_MASK 0xff +#define S2CR_EXIDVALID (1 << 10) +#define S2CR_TYPE_SHIFT 16 +#define S2CR_TYPE_MASK 0x3 +enum arm_smmu_s2cr_type { + S2CR_TYPE_TRANS, + S2CR_TYPE_BYPASS, + S2CR_TYPE_FAULT, +}; + +#define S2CR_PRIVCFG_SHIFT 24 +#define S2CR_PRIVCFG_MASK 0x3 +enum arm_smmu_s2cr_privcfg { + S2CR_PRIVCFG_DEFAULT, + S2CR_PRIVCFG_DIPAN, + S2CR_PRIVCFG_UNPRIV, + S2CR_PRIVCFG_PRIV, +}; + +/* Context bank attribute registers */ +#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2)) +#define CBAR_VMID_SHIFT 0 +#define CBAR_VMID_MASK 0xff +#define CBAR_S1_BPSHCFG_SHIFT 8 +#define CBAR_S1_BPSHCFG_MASK 3 +#define CBAR_S1_BPSHCFG_NSH 3 +#define CBAR_S1_MEMATTR_SHIFT 12 +#define CBAR_S1_MEMATTR_MASK 0xf +#define CBAR_S1_MEMATTR_WB 0xf +#define CBAR_TYPE_SHIFT 16 +#define CBAR_TYPE_MASK 0x3 +#define CBAR_TYPE_S2_TRANS (0 << CBAR_TYPE_SHIFT) +#define CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << CBAR_TYPE_SHIFT) +#define CBAR_TYPE_S1_TRANS_S2_FAULT (2 << CBAR_TYPE_SHIFT) +#define CBAR_TYPE_S1_TRANS_S2_TRANS (3 << CBAR_TYPE_SHIFT) +#define CBAR_IRPTNDX_SHIFT 24 +#define CBAR_IRPTNDX_MASK 0xff + +#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2)) +#define CBA2R_RW64_32BIT (0 << 0) +#define CBA2R_RW64_64BIT (1 << 0) +#define CBA2R_VMID_SHIFT 16 +#define CBA2R_VMID_MASK 0xffff + +#define ARM_SMMU_CB_SCTLR 0x0 +#define ARM_SMMU_CB_ACTLR 0x4 +#define ARM_SMMU_CB_RESUME 0x8 +#define ARM_SMMU_CB_TTBCR2 0x10 +#define ARM_SMMU_CB_TTBR0 0x20 +#define ARM_SMMU_CB_TTBR1 0x28 +#define ARM_SMMU_CB_TTBCR 0x30 +#define ARM_SMMU_CB_CONTEXTIDR 0x34 +#define ARM_SMMU_CB_S1_MAIR0 0x38 +#define ARM_SMMU_CB_S1_MAIR1 0x3c +#define ARM_SMMU_CB_PAR 0x50 +#define ARM_SMMU_CB_FSR 0x58 +#define ARM_SMMU_CB_FAR 0x60 +#define ARM_SMMU_CB_FSYNR0 0x68 +#define ARM_SMMU_CB_S1_TLBIVA 0x600 +#define ARM_SMMU_CB_S1_TLBIASID 0x610 +#define ARM_SMMU_CB_S1_TLBIVAL 0x620 +#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630 +#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638 +#define ARM_SMMU_CB_TLBSYNC 0x7f0 +#define ARM_SMMU_CB_TLBSTATUS 0x7f4 +#define ARM_SMMU_CB_ATS1PR 0x800 +#define ARM_SMMU_CB_ATSR 0x8f0 + +#define SCTLR_S1_ASIDPNE (1 << 12) +#define SCTLR_CFCFG (1 << 7) +#define SCTLR_CFIE (1 << 6) +#define SCTLR_CFRE (1 << 5) +#define SCTLR_E (1 << 4) +#define SCTLR_AFE (1 << 2) +#define SCTLR_TRE (1 << 1) +#define SCTLR_M (1 << 0) + +#define CB_PAR_F (1 << 0) + +#define ATSR_ACTIVE (1 << 0) + +#define RESUME_RETRY (0 << 0) +#define RESUME_TERMINATE (1 << 0) + +#define TTBCR2_SEP_SHIFT 15 +#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT) +#define TTBCR2_AS (1 << 4) + +#define TTBRn_ASID_SHIFT 48 + +#define FSR_MULTI (1 << 31) +#define FSR_SS (1 << 30) +#define FSR_UUT (1 << 8) +#define FSR_ASF (1 << 7) +#define FSR_TLBLKF (1 << 6) +#define FSR_TLBMCF (1 << 5) +#define FSR_EF (1 << 4) +#define FSR_PF (1 << 3) +#define FSR_AFF (1 << 2) +#define FSR_TF (1 << 1) + +#define FSR_IGN (FSR_AFF | FSR_ASF | \ + FSR_TLBMCF | FSR_TLBLKF) +#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \ + FSR_EF | FSR_PF | FSR_TF | FSR_IGN) + +#define FSYNR0_WNR (1 << 4) + +#endif /* _ARM_SMMU_REGS_H */ diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index 568c400eeaed..e67ba6c40faf 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -2852,9 +2852,15 @@ static int arm_smmu_device_remove(struct platform_device *pdev) struct arm_smmu_device *smmu = platform_get_drvdata(pdev); arm_smmu_device_disable(smmu); + return 0; } +static void arm_smmu_device_shutdown(struct platform_device *pdev) +{ + arm_smmu_device_remove(pdev); +} + static const struct of_device_id arm_smmu_of_match[] = { { .compatible = "arm,smmu-v3", }, { }, @@ -2868,6 +2874,7 @@ static struct platform_driver arm_smmu_driver = { }, .probe = arm_smmu_device_probe, .remove = arm_smmu_device_remove, + .shutdown = arm_smmu_device_shutdown, }; module_platform_driver(arm_smmu_driver); diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 2d80fa8a0634..3bdb799d3b4b 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -54,6 +54,15 @@ #include <linux/amba/bus.h> #include "io-pgtable.h" +#include "arm-smmu-regs.h" + +#define ARM_MMU500_ACTLR_CPRE (1 << 1) + +#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) +#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8) + +#define TLB_LOOP_TIMEOUT 1000000 /* 1s! */ +#define TLB_SPIN_COUNT 10 /* Maximum number of context banks per SMMU */ #define ARM_SMMU_MAX_CBS 128 @@ -83,211 +92,9 @@ #define smmu_write_atomic_lq writel_relaxed #endif -/* Configuration registers */ -#define ARM_SMMU_GR0_sCR0 0x0 -#define sCR0_CLIENTPD (1 << 0) -#define sCR0_GFRE (1 << 1) -#define sCR0_GFIE (1 << 2) -#define sCR0_EXIDENABLE (1 << 3) -#define sCR0_GCFGFRE (1 << 4) -#define sCR0_GCFGFIE (1 << 5) -#define sCR0_USFCFG (1 << 10) -#define sCR0_VMIDPNE (1 << 11) -#define sCR0_PTM (1 << 12) -#define sCR0_FB (1 << 13) -#define sCR0_VMID16EN (1 << 31) -#define sCR0_BSU_SHIFT 14 -#define sCR0_BSU_MASK 0x3 - -/* Auxiliary Configuration register */ -#define ARM_SMMU_GR0_sACR 0x10 - -/* Identification registers */ -#define ARM_SMMU_GR0_ID0 0x20 -#define ARM_SMMU_GR0_ID1 0x24 -#define ARM_SMMU_GR0_ID2 0x28 -#define ARM_SMMU_GR0_ID3 0x2c -#define ARM_SMMU_GR0_ID4 0x30 -#define ARM_SMMU_GR0_ID5 0x34 -#define ARM_SMMU_GR0_ID6 0x38 -#define ARM_SMMU_GR0_ID7 0x3c -#define ARM_SMMU_GR0_sGFSR 0x48 -#define ARM_SMMU_GR0_sGFSYNR0 0x50 -#define ARM_SMMU_GR0_sGFSYNR1 0x54 -#define ARM_SMMU_GR0_sGFSYNR2 0x58 - -#define ID0_S1TS (1 << 30) -#define ID0_S2TS (1 << 29) -#define ID0_NTS (1 << 28) -#define ID0_SMS (1 << 27) -#define ID0_ATOSNS (1 << 26) -#define ID0_PTFS_NO_AARCH32 (1 << 25) -#define ID0_PTFS_NO_AARCH32S (1 << 24) -#define ID0_CTTW (1 << 14) -#define ID0_NUMIRPT_SHIFT 16 -#define ID0_NUMIRPT_MASK 0xff -#define ID0_NUMSIDB_SHIFT 9 -#define ID0_NUMSIDB_MASK 0xf -#define ID0_EXIDS (1 << 8) -#define ID0_NUMSMRG_SHIFT 0 -#define ID0_NUMSMRG_MASK 0xff - -#define ID1_PAGESIZE (1 << 31) -#define ID1_NUMPAGENDXB_SHIFT 28 -#define ID1_NUMPAGENDXB_MASK 7 -#define ID1_NUMS2CB_SHIFT 16 -#define ID1_NUMS2CB_MASK 0xff -#define ID1_NUMCB_SHIFT 0 -#define ID1_NUMCB_MASK 0xff - -#define ID2_OAS_SHIFT 4 -#define ID2_OAS_MASK 0xf -#define ID2_IAS_SHIFT 0 -#define ID2_IAS_MASK 0xf -#define ID2_UBS_SHIFT 8 -#define ID2_UBS_MASK 0xf -#define ID2_PTFS_4K (1 << 12) -#define ID2_PTFS_16K (1 << 13) -#define ID2_PTFS_64K (1 << 14) -#define ID2_VMID16 (1 << 15) - -#define ID7_MAJOR_SHIFT 4 -#define ID7_MAJOR_MASK 0xf - -/* Global TLB invalidation */ -#define ARM_SMMU_GR0_TLBIVMID 0x64 -#define ARM_SMMU_GR0_TLBIALLNSNH 0x68 -#define ARM_SMMU_GR0_TLBIALLH 0x6c -#define ARM_SMMU_GR0_sTLBGSYNC 0x70 -#define ARM_SMMU_GR0_sTLBGSTATUS 0x74 -#define sTLBGSTATUS_GSACTIVE (1 << 0) -#define TLB_LOOP_TIMEOUT 1000000 /* 1s! */ -#define TLB_SPIN_COUNT 10 - -/* Stream mapping registers */ -#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2)) -#define SMR_VALID (1 << 31) -#define SMR_MASK_SHIFT 16 -#define SMR_ID_SHIFT 0 - -#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2)) -#define S2CR_CBNDX_SHIFT 0 -#define S2CR_CBNDX_MASK 0xff -#define S2CR_EXIDVALID (1 << 10) -#define S2CR_TYPE_SHIFT 16 -#define S2CR_TYPE_MASK 0x3 -enum arm_smmu_s2cr_type { - S2CR_TYPE_TRANS, - S2CR_TYPE_BYPASS, - S2CR_TYPE_FAULT, -}; - -#define S2CR_PRIVCFG_SHIFT 24 -#define S2CR_PRIVCFG_MASK 0x3 -enum arm_smmu_s2cr_privcfg { - S2CR_PRIVCFG_DEFAULT, - S2CR_PRIVCFG_DIPAN, - S2CR_PRIVCFG_UNPRIV, - S2CR_PRIVCFG_PRIV, -}; - -/* Context bank attribute registers */ -#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2)) -#define CBAR_VMID_SHIFT 0 -#define CBAR_VMID_MASK 0xff -#define CBAR_S1_BPSHCFG_SHIFT 8 -#define CBAR_S1_BPSHCFG_MASK 3 -#define CBAR_S1_BPSHCFG_NSH 3 -#define CBAR_S1_MEMATTR_SHIFT 12 -#define CBAR_S1_MEMATTR_MASK 0xf -#define CBAR_S1_MEMATTR_WB 0xf -#define CBAR_TYPE_SHIFT 16 -#define CBAR_TYPE_MASK 0x3 -#define CBAR_TYPE_S2_TRANS (0 << CBAR_TYPE_SHIFT) -#define CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << CBAR_TYPE_SHIFT) -#define CBAR_TYPE_S1_TRANS_S2_FAULT (2 << CBAR_TYPE_SHIFT) -#define CBAR_TYPE_S1_TRANS_S2_TRANS (3 << CBAR_TYPE_SHIFT) -#define CBAR_IRPTNDX_SHIFT 24 -#define CBAR_IRPTNDX_MASK 0xff - -#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2)) -#define CBA2R_RW64_32BIT (0 << 0) -#define CBA2R_RW64_64BIT (1 << 0) -#define CBA2R_VMID_SHIFT 16 -#define CBA2R_VMID_MASK 0xffff - /* Translation context bank */ #define ARM_SMMU_CB(smmu, n) ((smmu)->cb_base + ((n) << (smmu)->pgshift)) -#define ARM_SMMU_CB_SCTLR 0x0 -#define ARM_SMMU_CB_ACTLR 0x4 -#define ARM_SMMU_CB_RESUME 0x8 -#define ARM_SMMU_CB_TTBCR2 0x10 -#define ARM_SMMU_CB_TTBR0 0x20 -#define ARM_SMMU_CB_TTBR1 0x28 -#define ARM_SMMU_CB_TTBCR 0x30 -#define ARM_SMMU_CB_CONTEXTIDR 0x34 -#define ARM_SMMU_CB_S1_MAIR0 0x38 -#define ARM_SMMU_CB_S1_MAIR1 0x3c -#define ARM_SMMU_CB_PAR 0x50 -#define ARM_SMMU_CB_FSR 0x58 -#define ARM_SMMU_CB_FAR 0x60 -#define ARM_SMMU_CB_FSYNR0 0x68 -#define ARM_SMMU_CB_S1_TLBIVA 0x600 -#define ARM_SMMU_CB_S1_TLBIASID 0x610 -#define ARM_SMMU_CB_S1_TLBIVAL 0x620 -#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630 -#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638 -#define ARM_SMMU_CB_TLBSYNC 0x7f0 -#define ARM_SMMU_CB_TLBSTATUS 0x7f4 -#define ARM_SMMU_CB_ATS1PR 0x800 -#define ARM_SMMU_CB_ATSR 0x8f0 - -#define SCTLR_S1_ASIDPNE (1 << 12) -#define SCTLR_CFCFG (1 << 7) -#define SCTLR_CFIE (1 << 6) -#define SCTLR_CFRE (1 << 5) -#define SCTLR_E (1 << 4) -#define SCTLR_AFE (1 << 2) -#define SCTLR_TRE (1 << 1) -#define SCTLR_M (1 << 0) - -#define ARM_MMU500_ACTLR_CPRE (1 << 1) - -#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) -#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8) - -#define CB_PAR_F (1 << 0) - -#define ATSR_ACTIVE (1 << 0) - -#define RESUME_RETRY (0 << 0) -#define RESUME_TERMINATE (1 << 0) - -#define TTBCR2_SEP_SHIFT 15 -#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT) -#define TTBCR2_AS (1 << 4) - -#define TTBRn_ASID_SHIFT 48 - -#define FSR_MULTI (1 << 31) -#define FSR_SS (1 << 30) -#define FSR_UUT (1 << 8) -#define FSR_ASF (1 << 7) -#define FSR_TLBLKF (1 << 6) -#define FSR_TLBMCF (1 << 5) -#define FSR_EF (1 << 4) -#define FSR_PF (1 << 3) -#define FSR_AFF (1 << 2) -#define FSR_TF (1 << 1) - -#define FSR_IGN (FSR_AFF | FSR_ASF | \ - FSR_TLBMCF | FSR_TLBLKF) -#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \ - FSR_EF | FSR_PF | FSR_TF | FSR_IGN) - -#define FSYNR0_WNR (1 << 4) - #define MSI_IOVA_BASE 0x8000000 #define MSI_IOVA_LENGTH 0x100000 @@ -338,6 +145,13 @@ struct arm_smmu_smr { bool valid; }; +struct arm_smmu_cb { + u64 ttbr[2]; + u32 tcr[2]; + u32 mair[2]; + struct arm_smmu_cfg *cfg; +}; + struct arm_smmu_master_cfg { struct arm_smmu_device *smmu; s16 smendx[]; @@ -380,6 +194,7 @@ struct arm_smmu_device { u32 num_context_banks; u32 num_s2_context_banks; DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS); + struct arm_smmu_cb *cbs; atomic_t irptndx; u32 num_mapping_groups; @@ -776,17 +591,74 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev) static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, struct io_pgtable_cfg *pgtbl_cfg) { - u32 reg, reg2; - u64 reg64; - bool stage1; struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - struct arm_smmu_device *smmu = smmu_domain->smmu; + struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx]; + bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; + + cb->cfg = cfg; + + /* TTBCR */ + if (stage1) { + if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { + cb->tcr[0] = pgtbl_cfg->arm_v7s_cfg.tcr; + } else { + cb->tcr[0] = pgtbl_cfg->arm_lpae_s1_cfg.tcr; + cb->tcr[1] = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32; + cb->tcr[1] |= TTBCR2_SEP_UPSTREAM; + if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) + cb->tcr[1] |= TTBCR2_AS; + } + } else { + cb->tcr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vtcr; + } + + /* TTBRs */ + if (stage1) { + if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { + cb->ttbr[0] = pgtbl_cfg->arm_v7s_cfg.ttbr[0]; + cb->ttbr[1] = pgtbl_cfg->arm_v7s_cfg.ttbr[1]; + } else { + cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0]; + cb->ttbr[0] |= (u64)cfg->asid << TTBRn_ASID_SHIFT; + cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1]; + cb->ttbr[1] |= (u64)cfg->asid << TTBRn_ASID_SHIFT; + } + } else { + cb->ttbr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; + } + + /* MAIRs (stage-1 only) */ + if (stage1) { + if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { + cb->mair[0] = pgtbl_cfg->arm_v7s_cfg.prrr; + cb->mair[1] = pgtbl_cfg->arm_v7s_cfg.nmrr; + } else { + cb->mair[0] = pgtbl_cfg->arm_lpae_s1_cfg.mair[0]; + cb->mair[1] = pgtbl_cfg->arm_lpae_s1_cfg.mair[1]; + } + } +} + +static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx) +{ + u32 reg; + bool stage1; + struct arm_smmu_cb *cb = &smmu->cbs[idx]; + struct arm_smmu_cfg *cfg = cb->cfg; void __iomem *cb_base, *gr1_base; + cb_base = ARM_SMMU_CB(smmu, idx); + + /* Unassigned context banks only need disabling */ + if (!cfg) { + writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR); + return; + } + gr1_base = ARM_SMMU_GR1(smmu); stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; - cb_base = ARM_SMMU_CB(smmu, cfg->cbndx); + /* CBA2R */ if (smmu->version > ARM_SMMU_V1) { if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) reg = CBA2R_RW64_64BIT; @@ -796,7 +668,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, if (smmu->features & ARM_SMMU_FEAT_VMID16) reg |= cfg->vmid << CBA2R_VMID_SHIFT; - writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx)); + writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(idx)); } /* CBAR */ @@ -815,72 +687,41 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, /* 8-bit VMIDs live in CBAR */ reg |= cfg->vmid << CBAR_VMID_SHIFT; } - writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(cfg->cbndx)); + writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(idx)); /* * TTBCR * We must write this before the TTBRs, since it determines the * access behaviour of some fields (in particular, ASID[15:8]). */ - if (stage1) { - if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { - reg = pgtbl_cfg->arm_v7s_cfg.tcr; - reg2 = 0; - } else { - reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr; - reg2 = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32; - reg2 |= TTBCR2_SEP_UPSTREAM; - if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) - reg2 |= TTBCR2_AS; - } - if (smmu->version > ARM_SMMU_V1) - writel_relaxed(reg2, cb_base + ARM_SMMU_CB_TTBCR2); - } else { - reg = pgtbl_cfg->arm_lpae_s2_cfg.vtcr; - } - writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); + if (stage1 && smmu->version > ARM_SMMU_V1) + writel_relaxed(cb->tcr[1], cb_base + ARM_SMMU_CB_TTBCR2); + writel_relaxed(cb->tcr[0], cb_base + ARM_SMMU_CB_TTBCR); /* TTBRs */ - if (stage1) { - if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { - reg = pgtbl_cfg->arm_v7s_cfg.ttbr[0]; - writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0); - reg = pgtbl_cfg->arm_v7s_cfg.ttbr[1]; - writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1); - writel_relaxed(cfg->asid, cb_base + ARM_SMMU_CB_CONTEXTIDR); - } else { - reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0]; - reg64 |= (u64)cfg->asid << TTBRn_ASID_SHIFT; - writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0); - reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1]; - reg64 |= (u64)cfg->asid << TTBRn_ASID_SHIFT; - writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR1); - } + if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { + writel_relaxed(cfg->asid, cb_base + ARM_SMMU_CB_CONTEXTIDR); + writel_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0); + writel_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1); } else { - reg64 = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; - writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0); + writeq_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0); + if (stage1) + writeq_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1); } /* MAIRs (stage-1 only) */ if (stage1) { - if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { - reg = pgtbl_cfg->arm_v7s_cfg.prrr; - reg2 = pgtbl_cfg->arm_v7s_cfg.nmrr; - } else { - reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[0]; - reg2 = pgtbl_cfg->arm_lpae_s1_cfg.mair[1]; - } - writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0); - writel_relaxed(reg2, cb_base + ARM_SMMU_CB_S1_MAIR1); + writel_relaxed(cb->mair[0], cb_base + ARM_SMMU_CB_S1_MAIR0); + writel_relaxed(cb->mair[1], cb_base + ARM_SMMU_CB_S1_MAIR1); } /* SCTLR */ reg = SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE | SCTLR_M; if (stage1) reg |= SCTLR_S1_ASIDPNE; -#ifdef __BIG_ENDIAN - reg |= SCTLR_E; -#endif + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + reg |= SCTLR_E; + writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR); } @@ -1043,6 +884,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, /* Initialise the context bank with our page table cfg */ arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg); + arm_smmu_write_context_bank(smmu, cfg->cbndx); /* * Request context fault interrupt. Do this last to avoid the @@ -1075,7 +917,6 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_device *smmu = smmu_domain->smmu; struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - void __iomem *cb_base; int irq; if (!smmu || domain->type == IOMMU_DOMAIN_IDENTITY) @@ -1085,8 +926,8 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) * Disable the context bank and free the page tables before freeing * it. */ - cb_base = ARM_SMMU_CB(smmu, cfg->cbndx); - writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR); + smmu->cbs[cfg->cbndx].cfg = NULL; + arm_smmu_write_context_bank(smmu, cfg->cbndx); if (cfg->irptndx != INVALID_IRPTNDX) { irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx]; @@ -1736,7 +1577,6 @@ static struct iommu_ops arm_smmu_ops = { static void arm_smmu_device_reset(struct arm_smmu_device *smmu) { void __iomem *gr0_base = ARM_SMMU_GR0(smmu); - void __iomem *cb_base; int i; u32 reg, major; @@ -1772,8 +1612,9 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) /* Make sure all context banks are disabled and clear CB_FSR */ for (i = 0; i < smmu->num_context_banks; ++i) { - cb_base = ARM_SMMU_CB(smmu, i); - writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR); + void __iomem *cb_base = ARM_SMMU_CB(smmu, i); + + arm_smmu_write_context_bank(smmu, i); writel_relaxed(FSR_FAULT, cb_base + ARM_SMMU_CB_FSR); /* * Disable MMU-500's not-particularly-beneficial next-page @@ -1979,6 +1820,10 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) smmu->cavium_id_base -= smmu->num_context_banks; dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n"); } + smmu->cbs = devm_kcalloc(smmu->dev, smmu->num_context_banks, + sizeof(*smmu->cbs), GFP_KERNEL); + if (!smmu->cbs) + return -ENOMEM; /* ID2 */ id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID2); @@ -2336,13 +2181,30 @@ static int arm_smmu_device_remove(struct platform_device *pdev) return 0; } +static void arm_smmu_device_shutdown(struct platform_device *pdev) +{ + arm_smmu_device_remove(pdev); +} + +static int __maybe_unused arm_smmu_pm_resume(struct device *dev) +{ + struct arm_smmu_device *smmu = dev_get_drvdata(dev); + + arm_smmu_device_reset(smmu); + return 0; +} + +static SIMPLE_DEV_PM_OPS(arm_smmu_pm_ops, NULL, arm_smmu_pm_resume); + static struct platform_driver arm_smmu_driver = { .driver = { .name = "arm-smmu", .of_match_table = of_match_ptr(arm_smmu_of_match), + .pm = &arm_smmu_pm_ops, }, .probe = arm_smmu_device_probe, .remove = arm_smmu_device_remove, + .shutdown = arm_smmu_device_shutdown, }; module_platform_driver(arm_smmu_driver); diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index c8b0329c85d2..ca5ebaeafd6a 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -1343,7 +1343,7 @@ void qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 qdep, if (mask) { BUG_ON(addr & ((1 << (VTD_PAGE_SHIFT + mask)) - 1)); - addr |= (1 << (VTD_PAGE_SHIFT + mask - 1)) - 1; + addr |= (1ULL << (VTD_PAGE_SHIFT + mask - 1)) - 1; desc.high = QI_DEV_IOTLB_ADDR(addr) | QI_DEV_IOTLB_SIZE; } else desc.high = QI_DEV_IOTLB_ADDR(addr); diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 2395478dde75..f596fcc32898 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -54,10 +54,6 @@ typedef u32 sysmmu_pte_t; #define lv2ent_small(pent) ((*(pent) & 2) == 2) #define lv2ent_large(pent) ((*(pent) & 3) == 1) -#ifdef CONFIG_BIG_ENDIAN -#warning "revisit driver if we can enable big-endian ptes" -#endif - /* * v1.x - v3.x SYSMMU supports 32bit physical and 32bit virtual address spaces * v5.0 introduced support for 36bit physical address space by shifting @@ -569,7 +565,7 @@ static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, spin_unlock_irqrestore(&data->lock, flags); } -static struct iommu_ops exynos_iommu_ops; +static const struct iommu_ops exynos_iommu_ops; static int __init exynos_sysmmu_probe(struct platform_device *pdev) { @@ -659,6 +655,13 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) } } + /* + * use the first registered sysmmu device for performing + * dma mapping operations on iommu page tables (cpu cache flush) + */ + if (!dma_dev) + dma_dev = &pdev->dev; + pm_runtime_enable(dev); return 0; @@ -1323,7 +1326,7 @@ static int exynos_iommu_of_xlate(struct device *dev, return 0; } -static struct iommu_ops exynos_iommu_ops = { +static const struct iommu_ops exynos_iommu_ops = { .domain_alloc = exynos_iommu_domain_alloc, .domain_free = exynos_iommu_domain_free, .attach_dev = exynos_iommu_attach_device, @@ -1339,8 +1342,6 @@ static struct iommu_ops exynos_iommu_ops = { .of_xlate = exynos_iommu_of_xlate, }; -static bool init_done; - static int __init exynos_iommu_init(void) { int ret; @@ -1373,8 +1374,6 @@ static int __init exynos_iommu_init(void) goto err_set_iommu; } - init_done = true; - return 0; err_set_iommu: kmem_cache_free(lv2table_kmem_cache, zero_lv2_table); @@ -1384,27 +1383,6 @@ err_reg_driver: kmem_cache_destroy(lv2table_kmem_cache); return ret; } +core_initcall(exynos_iommu_init); -static int __init exynos_iommu_of_setup(struct device_node *np) -{ - struct platform_device *pdev; - - if (!init_done) - exynos_iommu_init(); - - pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root); - if (!pdev) - return -ENODEV; - - /* - * use the first registered sysmmu device for performing - * dma mapping operations on iommu page tables (cpu cache flush) - */ - if (!dma_dev) - dma_dev = &pdev->dev; - - return 0; -} - -IOMMU_OF_DECLARE(exynos_iommu_of, "samsung,exynos-sysmmu", - exynos_iommu_of_setup); +IOMMU_OF_DECLARE(exynos_iommu_of, "samsung,exynos-sysmmu", NULL); diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c index a34355fca37a..8540625796a1 100644 --- a/drivers/iommu/fsl_pamu.c +++ b/drivers/iommu/fsl_pamu.c @@ -42,6 +42,8 @@ struct pamu_isr_data { static struct paace *ppaact; static struct paace *spaact; +static bool probed; /* Has PAMU been probed? */ + /* * Table for matching compatible strings, for device tree * guts node, for QorIQ SOCs. @@ -530,8 +532,8 @@ u32 get_stash_id(u32 stash_dest_hint, u32 vcpu) if (node) { prop = of_get_property(node, "cache-stash-id", NULL); if (!prop) { - pr_debug("missing cache-stash-id at %s\n", - node->full_name); + pr_debug("missing cache-stash-id at %pOF\n", + node); of_node_put(node); return ~(u32)0; } @@ -557,8 +559,8 @@ found_cpu_node: if (stash_dest_hint == cache_level) { prop = of_get_property(node, "cache-stash-id", NULL); if (!prop) { - pr_debug("missing cache-stash-id at %s\n", - node->full_name); + pr_debug("missing cache-stash-id at %pOF\n", + node); of_node_put(node); return ~(u32)0; } @@ -568,8 +570,7 @@ found_cpu_node: prop = of_get_property(node, "next-level-cache", NULL); if (!prop) { - pr_debug("can't find next-level-cache at %s\n", - node->full_name); + pr_debug("can't find next-level-cache at %pOF\n", node); of_node_put(node); return ~(u32)0; /* can't traverse any further */ } @@ -1033,6 +1034,9 @@ static int fsl_pamu_probe(struct platform_device *pdev) * NOTE : All PAMUs share the same LIODN tables. */ + if (WARN_ON(probed)) + return -EBUSY; + pamu_regs = of_iomap(dev->of_node, 0); if (!pamu_regs) { dev_err(dev, "ioremap of PAMU node failed\n"); @@ -1063,8 +1067,7 @@ static int fsl_pamu_probe(struct platform_device *pdev) guts_node = of_find_matching_node(NULL, guts_device_ids); if (!guts_node) { - dev_err(dev, "could not find GUTS node %s\n", - dev->of_node->full_name); + dev_err(dev, "could not find GUTS node %pOF\n", dev->of_node); ret = -ENODEV; goto error; } @@ -1172,6 +1175,8 @@ static int fsl_pamu_probe(struct platform_device *pdev) setup_liodns(); + probed = true; + return 0; error_genpool: @@ -1246,8 +1251,7 @@ static __init int fsl_pamu_init(void) pdev = platform_device_alloc("fsl-of-pamu", 0); if (!pdev) { - pr_err("could not allocate device %s\n", - np->full_name); + pr_err("could not allocate device %pOF\n", np); ret = -ENOMEM; goto error_device_alloc; } @@ -1259,8 +1263,7 @@ static __init int fsl_pamu_init(void) ret = platform_device_add(pdev); if (ret) { - pr_err("could not add device %s (err=%i)\n", - np->full_name, ret); + pr_err("could not add device %pOF (err=%i)\n", np, ret); goto error_device_add; } diff --git a/drivers/iommu/fsl_pamu_domain.c b/drivers/iommu/fsl_pamu_domain.c index da0e1e30ef37..f089136e9c3f 100644 --- a/drivers/iommu/fsl_pamu_domain.c +++ b/drivers/iommu/fsl_pamu_domain.c @@ -33,6 +33,8 @@ static struct kmem_cache *fsl_pamu_domain_cache; static struct kmem_cache *iommu_devinfo_cache; static DEFINE_SPINLOCK(device_domain_lock); +struct iommu_device pamu_iommu; /* IOMMU core code handle */ + static struct fsl_dma_domain *to_fsl_dma_domain(struct iommu_domain *dom) { return container_of(dom, struct fsl_dma_domain, iommu_domain); @@ -619,8 +621,8 @@ static int handle_attach_device(struct fsl_dma_domain *dma_domain, for (i = 0; i < num; i++) { /* Ensure that LIODN value is valid */ if (liodn[i] >= PAACE_NUMBER_ENTRIES) { - pr_debug("Invalid liodn %d, attach device failed for %s\n", - liodn[i], dev->of_node->full_name); + pr_debug("Invalid liodn %d, attach device failed for %pOF\n", + liodn[i], dev->of_node); ret = -EINVAL; break; } @@ -684,8 +686,7 @@ static int fsl_pamu_attach_device(struct iommu_domain *domain, liodn_cnt = len / sizeof(u32); ret = handle_attach_device(dma_domain, dev, liodn, liodn_cnt); } else { - pr_debug("missing fsl,liodn property at %s\n", - dev->of_node->full_name); + pr_debug("missing fsl,liodn property at %pOF\n", dev->of_node); ret = -EINVAL; } @@ -720,8 +721,7 @@ static void fsl_pamu_detach_device(struct iommu_domain *domain, if (prop) detach_device(dev, dma_domain); else - pr_debug("missing fsl,liodn property at %s\n", - dev->of_node->full_name); + pr_debug("missing fsl,liodn property at %pOF\n", dev->of_node); } static int configure_domain_geometry(struct iommu_domain *domain, void *data) @@ -983,11 +983,14 @@ static int fsl_pamu_add_device(struct device *dev) iommu_group_put(group); + iommu_device_link(&pamu_iommu, dev); + return 0; } static void fsl_pamu_remove_device(struct device *dev) { + iommu_device_unlink(&pamu_iommu, dev); iommu_group_remove_device(dev); } @@ -1073,6 +1076,19 @@ int __init pamu_domain_init(void) if (ret) return ret; + ret = iommu_device_sysfs_add(&pamu_iommu, NULL, NULL, "iommu0"); + if (ret) + return ret; + + iommu_device_set_ops(&pamu_iommu, &fsl_pamu_ops); + + ret = iommu_device_register(&pamu_iommu); + if (ret) { + iommu_device_sysfs_remove(&pamu_iommu); + pr_err("Can't register iommu device\n"); + return ret; + } + bus_set_iommu(&platform_bus_type, &fsl_pamu_ops); bus_set_iommu(&pci_bus_type, &fsl_pamu_ops); diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index bb05fc50ee2e..6784a05dd6b2 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -458,31 +458,6 @@ static LIST_HEAD(dmar_rmrr_units); #define for_each_rmrr_units(rmrr) \ list_for_each_entry(rmrr, &dmar_rmrr_units, list) -static void flush_unmaps_timeout(unsigned long data); - -struct deferred_flush_entry { - unsigned long iova_pfn; - unsigned long nrpages; - struct dmar_domain *domain; - struct page *freelist; -}; - -#define HIGH_WATER_MARK 250 -struct deferred_flush_table { - int next; - struct deferred_flush_entry entries[HIGH_WATER_MARK]; -}; - -struct deferred_flush_data { - spinlock_t lock; - int timer_on; - struct timer_list timer; - long size; - struct deferred_flush_table *tables; -}; - -static DEFINE_PER_CPU(struct deferred_flush_data, deferred_flush); - /* bitmap for indexing intel_iommus */ static int g_num_of_iommus; @@ -981,20 +956,6 @@ static int device_context_mapped(struct intel_iommu *iommu, u8 bus, u8 devfn) return ret; } -static void clear_context_table(struct intel_iommu *iommu, u8 bus, u8 devfn) -{ - struct context_entry *context; - unsigned long flags; - - spin_lock_irqsave(&iommu->lock, flags); - context = iommu_context_addr(iommu, bus, devfn, 0); - if (context) { - context_clear_entry(context); - __iommu_flush_cache(iommu, context, sizeof(*context)); - } - spin_unlock_irqrestore(&iommu->lock, flags); -} - static void free_context_table(struct intel_iommu *iommu) { int i; @@ -1144,8 +1105,9 @@ static void dma_pte_clear_range(struct dmar_domain *domain, } static void dma_pte_free_level(struct dmar_domain *domain, int level, - struct dma_pte *pte, unsigned long pfn, - unsigned long start_pfn, unsigned long last_pfn) + int retain_level, struct dma_pte *pte, + unsigned long pfn, unsigned long start_pfn, + unsigned long last_pfn) { pfn = max(start_pfn, pfn); pte = &pte[pfn_level_offset(pfn, level)]; @@ -1160,12 +1122,17 @@ static void dma_pte_free_level(struct dmar_domain *domain, int level, level_pfn = pfn & level_mask(level); level_pte = phys_to_virt(dma_pte_addr(pte)); - if (level > 2) - dma_pte_free_level(domain, level - 1, level_pte, - level_pfn, start_pfn, last_pfn); + if (level > 2) { + dma_pte_free_level(domain, level - 1, retain_level, + level_pte, level_pfn, start_pfn, + last_pfn); + } - /* If range covers entire pagetable, free it */ - if (!(start_pfn > level_pfn || + /* + * Free the page table if we're below the level we want to + * retain and the range covers the entire table. + */ + if (level < retain_level && !(start_pfn > level_pfn || last_pfn < level_pfn + level_size(level) - 1)) { dma_clear_pte(pte); domain_flush_cache(domain, pte, sizeof(*pte)); @@ -1176,10 +1143,14 @@ next: } while (!first_pte_in_page(++pte) && pfn <= last_pfn); } -/* clear last level (leaf) ptes and free page table pages. */ +/* + * clear last level (leaf) ptes and free page table pages below the + * level we wish to keep intact. + */ static void dma_pte_free_pagetable(struct dmar_domain *domain, unsigned long start_pfn, - unsigned long last_pfn) + unsigned long last_pfn, + int retain_level) { BUG_ON(!domain_pfn_supported(domain, start_pfn)); BUG_ON(!domain_pfn_supported(domain, last_pfn)); @@ -1188,7 +1159,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, dma_pte_clear_range(domain, start_pfn, last_pfn); /* We don't need lock here; nobody else touches the iova range */ - dma_pte_free_level(domain, agaw_to_level(domain->agaw), + dma_pte_free_level(domain, agaw_to_level(domain->agaw), retain_level, domain->pgd, 0, start_pfn, last_pfn); /* free pgd */ @@ -1316,6 +1287,13 @@ static void dma_free_pagelist(struct page *freelist) } } +static void iova_entry_free(unsigned long data) +{ + struct page *freelist = (struct page *)data; + + dma_free_pagelist(freelist); +} + /* iommu handling */ static int iommu_alloc_root_entry(struct intel_iommu *iommu) { @@ -1629,6 +1607,25 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, addr, mask); } +static void iommu_flush_iova(struct iova_domain *iovad) +{ + struct dmar_domain *domain; + int idx; + + domain = container_of(iovad, struct dmar_domain, iovad); + + for_each_domain_iommu(idx, domain) { + struct intel_iommu *iommu = g_iommus[idx]; + u16 did = domain->iommu_did[iommu->seq_id]; + + iommu->flush.flush_iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH); + + if (!cap_caching_mode(iommu->cap)) + iommu_flush_dev_iotlb(get_iommu_domain(iommu, did), + 0, MAX_AGAW_PFN_WIDTH); + } +} + static void iommu_disable_protect_mem_regions(struct intel_iommu *iommu) { u32 pmen; @@ -1939,9 +1936,16 @@ static int domain_init(struct dmar_domain *domain, struct intel_iommu *iommu, { int adjust_width, agaw; unsigned long sagaw; + int err; init_iova_domain(&domain->iovad, VTD_PAGE_SIZE, IOVA_START_PFN, DMA_32BIT_PFN); + + err = init_iova_flush_queue(&domain->iovad, + iommu_flush_iova, iova_entry_free); + if (err) + return err; + domain_reserve_special_ranges(domain); /* calculate AGAW */ @@ -1993,14 +1997,6 @@ static void domain_exit(struct dmar_domain *domain) if (!domain) return; - /* Flush any lazy unmaps that may reference this domain */ - if (!intel_iommu_strict) { - int cpu; - - for_each_possible_cpu(cpu) - flush_unmaps_timeout(cpu); - } - /* Remove associated devices and clear attached or cached domains */ rcu_read_lock(); domain_remove_dev_info(domain); @@ -2284,8 +2280,11 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, /* * Ensure that old small page tables are * removed to make room for superpage(s). + * We're adding new large pages, so make sure + * we don't remove their parent tables. */ - dma_pte_free_pagetable(domain, iov_pfn, end_pfn); + dma_pte_free_pagetable(domain, iov_pfn, end_pfn, + largepage_lvl + 1); } else { pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE; } @@ -2358,13 +2357,33 @@ static inline int domain_pfn_mapping(struct dmar_domain *domain, unsigned long i static void domain_context_clear_one(struct intel_iommu *iommu, u8 bus, u8 devfn) { + unsigned long flags; + struct context_entry *context; + u16 did_old; + if (!iommu) return; - clear_context_table(iommu, bus, devfn); - iommu->flush.flush_context(iommu, 0, 0, 0, - DMA_CCMD_GLOBAL_INVL); - iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH); + spin_lock_irqsave(&iommu->lock, flags); + context = iommu_context_addr(iommu, bus, devfn, 0); + if (!context) { + spin_unlock_irqrestore(&iommu->lock, flags); + return; + } + did_old = context_domain_id(context); + context_clear_entry(context); + __iommu_flush_cache(iommu, context, sizeof(*context)); + spin_unlock_irqrestore(&iommu->lock, flags); + iommu->flush.flush_context(iommu, + did_old, + (((u16)bus) << 8) | devfn, + DMA_CCMD_MASK_NOBIT, + DMA_CCMD_DEVICE_INVL); + iommu->flush.flush_iotlb(iommu, + did_old, + 0, + 0, + DMA_TLB_DSI_FLUSH); } static inline void unlink_domain_info(struct device_domain_info *info) @@ -3213,7 +3232,7 @@ static int __init init_dmars(void) bool copied_tables = false; struct device *dev; struct intel_iommu *iommu; - int i, ret, cpu; + int i, ret; /* * for each drhd @@ -3246,22 +3265,6 @@ static int __init init_dmars(void) goto error; } - for_each_possible_cpu(cpu) { - struct deferred_flush_data *dfd = per_cpu_ptr(&deferred_flush, - cpu); - - dfd->tables = kzalloc(g_num_of_iommus * - sizeof(struct deferred_flush_table), - GFP_KERNEL); - if (!dfd->tables) { - ret = -ENOMEM; - goto free_g_iommus; - } - - spin_lock_init(&dfd->lock); - setup_timer(&dfd->timer, flush_unmaps_timeout, cpu); - } - for_each_active_iommu(iommu, drhd) { g_iommus[iommu->seq_id] = iommu; @@ -3444,10 +3447,9 @@ free_iommu: disable_dmar_iommu(iommu); free_dmar_iommu(iommu); } -free_g_iommus: - for_each_possible_cpu(cpu) - kfree(per_cpu_ptr(&deferred_flush, cpu)->tables); + kfree(g_iommus); + error: return ret; } @@ -3652,110 +3654,6 @@ static dma_addr_t intel_map_page(struct device *dev, struct page *page, dir, *dev->dma_mask); } -static void flush_unmaps(struct deferred_flush_data *flush_data) -{ - int i, j; - - flush_data->timer_on = 0; - - /* just flush them all */ - for (i = 0; i < g_num_of_iommus; i++) { - struct intel_iommu *iommu = g_iommus[i]; - struct deferred_flush_table *flush_table = - &flush_data->tables[i]; - if (!iommu) - continue; - - if (!flush_table->next) - continue; - - /* In caching mode, global flushes turn emulation expensive */ - if (!cap_caching_mode(iommu->cap)) - iommu->flush.flush_iotlb(iommu, 0, 0, 0, - DMA_TLB_GLOBAL_FLUSH); - for (j = 0; j < flush_table->next; j++) { - unsigned long mask; - struct deferred_flush_entry *entry = - &flush_table->entries[j]; - unsigned long iova_pfn = entry->iova_pfn; - unsigned long nrpages = entry->nrpages; - struct dmar_domain *domain = entry->domain; - struct page *freelist = entry->freelist; - - /* On real hardware multiple invalidations are expensive */ - if (cap_caching_mode(iommu->cap)) - iommu_flush_iotlb_psi(iommu, domain, - mm_to_dma_pfn(iova_pfn), - nrpages, !freelist, 0); - else { - mask = ilog2(nrpages); - iommu_flush_dev_iotlb(domain, - (uint64_t)iova_pfn << PAGE_SHIFT, mask); - } - free_iova_fast(&domain->iovad, iova_pfn, nrpages); - if (freelist) - dma_free_pagelist(freelist); - } - flush_table->next = 0; - } - - flush_data->size = 0; -} - -static void flush_unmaps_timeout(unsigned long cpuid) -{ - struct deferred_flush_data *flush_data = per_cpu_ptr(&deferred_flush, cpuid); - unsigned long flags; - - spin_lock_irqsave(&flush_data->lock, flags); - flush_unmaps(flush_data); - spin_unlock_irqrestore(&flush_data->lock, flags); -} - -static void add_unmap(struct dmar_domain *dom, unsigned long iova_pfn, - unsigned long nrpages, struct page *freelist) -{ - unsigned long flags; - int entry_id, iommu_id; - struct intel_iommu *iommu; - struct deferred_flush_entry *entry; - struct deferred_flush_data *flush_data; - - flush_data = raw_cpu_ptr(&deferred_flush); - - /* Flush all CPUs' entries to avoid deferring too much. If - * this becomes a bottleneck, can just flush us, and rely on - * flush timer for the rest. - */ - if (flush_data->size == HIGH_WATER_MARK) { - int cpu; - - for_each_online_cpu(cpu) - flush_unmaps_timeout(cpu); - } - - spin_lock_irqsave(&flush_data->lock, flags); - - iommu = domain_get_iommu(dom); - iommu_id = iommu->seq_id; - - entry_id = flush_data->tables[iommu_id].next; - ++(flush_data->tables[iommu_id].next); - - entry = &flush_data->tables[iommu_id].entries[entry_id]; - entry->domain = dom; - entry->iova_pfn = iova_pfn; - entry->nrpages = nrpages; - entry->freelist = freelist; - - if (!flush_data->timer_on) { - mod_timer(&flush_data->timer, jiffies + msecs_to_jiffies(10)); - flush_data->timer_on = 1; - } - flush_data->size++; - spin_unlock_irqrestore(&flush_data->lock, flags); -} - static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) { struct dmar_domain *domain; @@ -3791,7 +3689,8 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) free_iova_fast(&domain->iovad, iova_pfn, dma_to_mm_pfn(nrpages)); dma_free_pagelist(freelist); } else { - add_unmap(domain, iova_pfn, nrpages, freelist); + queue_iova(&domain->iovad, iova_pfn, nrpages, + (unsigned long)freelist); /* * queue up the release of the unmap to save the 1/6th of the * cpu used up by the iotlb flush operation... @@ -3945,7 +3844,8 @@ static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nele ret = domain_sg_mapping(domain, start_vpfn, sglist, size, prot); if (unlikely(ret)) { dma_pte_free_pagetable(domain, start_vpfn, - start_vpfn + size - 1); + start_vpfn + size - 1, + agaw_to_level(domain->agaw) + 1); free_iova_fast(&domain->iovad, iova_pfn, dma_to_mm_pfn(size)); return 0; } @@ -4728,7 +4628,6 @@ static void free_all_cpu_cached_iovas(unsigned int cpu) static int intel_iommu_cpu_dead(unsigned int cpu) { free_all_cpu_cached_iovas(cpu); - flush_unmaps_timeout(cpu); return 0; } @@ -5350,7 +5249,8 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd sdev->sid = PCI_DEVID(info->bus, info->devfn); if (!(ctx_lo & CONTEXT_PASIDE)) { - context[1].hi = (u64)virt_to_phys(iommu->pasid_state_table); + if (iommu->pasid_state_table) + context[1].hi = (u64)virt_to_phys(iommu->pasid_state_table); context[1].lo = (u64)virt_to_phys(iommu->pasid_table) | intel_iommu_get_pts(iommu); diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index f620dccec8ee..f6697e55c2d4 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -24,6 +24,7 @@ #include <linux/pci-ats.h> #include <linux/dmar.h> #include <linux/interrupt.h> +#include <asm/page.h> static irqreturn_t prq_event_thread(int irq, void *d); @@ -546,6 +547,14 @@ static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req) return (requested & ~vma->vm_flags) != 0; } +static bool is_canonical_address(u64 addr) +{ + int shift = 64 - (__VIRTUAL_MASK_SHIFT + 1); + long saddr = (long) addr; + + return (((saddr << shift) >> shift) == saddr); +} + static irqreturn_t prq_event_thread(int irq, void *d) { struct intel_iommu *iommu = d; @@ -603,6 +612,11 @@ static irqreturn_t prq_event_thread(int irq, void *d) /* If the mm is already defunct, don't handle faults. */ if (!mmget_not_zero(svm->mm)) goto bad_req; + + /* If address is not canonical, return invalid response */ + if (!is_canonical_address(address)) + goto bad_req; + down_read(&svm->mm->mmap_sem); vma = find_extend_vma(svm->mm, address); if (!vma || address < vma->vm_start) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 3f6ea160afed..3de5c0bcb5cc 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -527,6 +527,8 @@ static int iommu_group_create_direct_mappings(struct iommu_group *group, } + iommu_flush_tlb_all(domain); + out: iommu_put_resv_regions(dev, &mappings); @@ -1005,11 +1007,10 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev) if (group) return group; - group = ERR_PTR(-EINVAL); - - if (ops && ops->device_group) - group = ops->device_group(dev); + if (!ops) + return ERR_PTR(-EINVAL); + group = ops->device_group(dev); if (WARN_ON_ONCE(group == NULL)) return ERR_PTR(-EINVAL); @@ -1283,6 +1284,10 @@ static int __iommu_attach_device(struct iommu_domain *domain, struct device *dev) { int ret; + if ((domain->ops->is_attach_deferred != NULL) && + domain->ops->is_attach_deferred(domain, dev)) + return 0; + if (unlikely(domain->ops->attach_dev == NULL)) return -ENODEV; @@ -1298,12 +1303,8 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev) int ret; group = iommu_group_get(dev); - /* FIXME: Remove this when groups a mandatory for iommu drivers */ - if (group == NULL) - return __iommu_attach_device(domain, dev); - /* - * We have a group - lock it to make sure the device-count doesn't + * Lock the group to make sure the device-count doesn't * change while we are attaching */ mutex_lock(&group->mutex); @@ -1324,6 +1325,10 @@ EXPORT_SYMBOL_GPL(iommu_attach_device); static void __iommu_detach_device(struct iommu_domain *domain, struct device *dev) { + if ((domain->ops->is_attach_deferred != NULL) && + domain->ops->is_attach_deferred(domain, dev)) + return; + if (unlikely(domain->ops->detach_dev == NULL)) return; @@ -1336,9 +1341,6 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) struct iommu_group *group; group = iommu_group_get(dev); - /* FIXME: Remove this when groups a mandatory for iommu drivers */ - if (group == NULL) - return __iommu_detach_device(domain, dev); mutex_lock(&group->mutex); if (iommu_group_device_count(group) != 1) { @@ -1360,8 +1362,7 @@ struct iommu_domain *iommu_get_domain_for_dev(struct device *dev) struct iommu_group *group; group = iommu_group_get(dev); - /* FIXME: Remove this when groups a mandatory for iommu drivers */ - if (group == NULL) + if (!group) return NULL; domain = group->domain; @@ -1556,13 +1557,16 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, } EXPORT_SYMBOL_GPL(iommu_map); -size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) +static size_t __iommu_unmap(struct iommu_domain *domain, + unsigned long iova, size_t size, + bool sync) { + const struct iommu_ops *ops = domain->ops; size_t unmapped_page, unmapped = 0; - unsigned int min_pagesz; unsigned long orig_iova = iova; + unsigned int min_pagesz; - if (unlikely(domain->ops->unmap == NULL || + if (unlikely(ops->unmap == NULL || domain->pgsize_bitmap == 0UL)) return -ENODEV; @@ -1592,10 +1596,13 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) while (unmapped < size) { size_t pgsize = iommu_pgsize(domain, iova, size - unmapped); - unmapped_page = domain->ops->unmap(domain, iova, pgsize); + unmapped_page = ops->unmap(domain, iova, pgsize); if (!unmapped_page) break; + if (sync && ops->iotlb_range_add) + ops->iotlb_range_add(domain, iova, pgsize); + pr_debug("unmapped: iova 0x%lx size 0x%zx\n", iova, unmapped_page); @@ -1603,11 +1610,27 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) unmapped += unmapped_page; } + if (sync && ops->iotlb_sync) + ops->iotlb_sync(domain); + trace_unmap(orig_iova, size, unmapped); return unmapped; } + +size_t iommu_unmap(struct iommu_domain *domain, + unsigned long iova, size_t size) +{ + return __iommu_unmap(domain, iova, size, true); +} EXPORT_SYMBOL_GPL(iommu_unmap); +size_t iommu_unmap_fast(struct iommu_domain *domain, + unsigned long iova, size_t size) +{ + return __iommu_unmap(domain, iova, size, false); +} +EXPORT_SYMBOL_GPL(iommu_unmap_fast); + size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova, struct scatterlist *sg, unsigned int nents, int prot) { diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index 246f14c83944..33edfa794ae9 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -32,6 +32,8 @@ static unsigned long iova_rcache_get(struct iova_domain *iovad, unsigned long limit_pfn); static void init_iova_rcaches(struct iova_domain *iovad); static void free_iova_rcaches(struct iova_domain *iovad); +static void fq_destroy_all_entries(struct iova_domain *iovad); +static void fq_flush_timeout(unsigned long data); void init_iova_domain(struct iova_domain *iovad, unsigned long granule, @@ -50,10 +52,61 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule, iovad->granule = granule; iovad->start_pfn = start_pfn; iovad->dma_32bit_pfn = pfn_32bit + 1; + iovad->flush_cb = NULL; + iovad->fq = NULL; init_iova_rcaches(iovad); } EXPORT_SYMBOL_GPL(init_iova_domain); +static void free_iova_flush_queue(struct iova_domain *iovad) +{ + if (!iovad->fq) + return; + + if (timer_pending(&iovad->fq_timer)) + del_timer(&iovad->fq_timer); + + fq_destroy_all_entries(iovad); + + free_percpu(iovad->fq); + + iovad->fq = NULL; + iovad->flush_cb = NULL; + iovad->entry_dtor = NULL; +} + +int init_iova_flush_queue(struct iova_domain *iovad, + iova_flush_cb flush_cb, iova_entry_dtor entry_dtor) +{ + int cpu; + + atomic64_set(&iovad->fq_flush_start_cnt, 0); + atomic64_set(&iovad->fq_flush_finish_cnt, 0); + + iovad->fq = alloc_percpu(struct iova_fq); + if (!iovad->fq) + return -ENOMEM; + + iovad->flush_cb = flush_cb; + iovad->entry_dtor = entry_dtor; + + for_each_possible_cpu(cpu) { + struct iova_fq *fq; + + fq = per_cpu_ptr(iovad->fq, cpu); + fq->head = 0; + fq->tail = 0; + + spin_lock_init(&fq->lock); + } + + setup_timer(&iovad->fq_timer, fq_flush_timeout, (unsigned long)iovad); + atomic_set(&iovad->fq_timer_on, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(init_iova_flush_queue); + static struct rb_node * __get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn) { @@ -423,6 +476,135 @@ free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size) } EXPORT_SYMBOL_GPL(free_iova_fast); +#define fq_ring_for_each(i, fq) \ + for ((i) = (fq)->head; (i) != (fq)->tail; (i) = ((i) + 1) % IOVA_FQ_SIZE) + +static inline bool fq_full(struct iova_fq *fq) +{ + assert_spin_locked(&fq->lock); + return (((fq->tail + 1) % IOVA_FQ_SIZE) == fq->head); +} + +static inline unsigned fq_ring_add(struct iova_fq *fq) +{ + unsigned idx = fq->tail; + + assert_spin_locked(&fq->lock); + + fq->tail = (idx + 1) % IOVA_FQ_SIZE; + + return idx; +} + +static void fq_ring_free(struct iova_domain *iovad, struct iova_fq *fq) +{ + u64 counter = atomic64_read(&iovad->fq_flush_finish_cnt); + unsigned idx; + + assert_spin_locked(&fq->lock); + + fq_ring_for_each(idx, fq) { + + if (fq->entries[idx].counter >= counter) + break; + + if (iovad->entry_dtor) + iovad->entry_dtor(fq->entries[idx].data); + + free_iova_fast(iovad, + fq->entries[idx].iova_pfn, + fq->entries[idx].pages); + + fq->head = (fq->head + 1) % IOVA_FQ_SIZE; + } +} + +static void iova_domain_flush(struct iova_domain *iovad) +{ + atomic64_inc(&iovad->fq_flush_start_cnt); + iovad->flush_cb(iovad); + atomic64_inc(&iovad->fq_flush_finish_cnt); +} + +static void fq_destroy_all_entries(struct iova_domain *iovad) +{ + int cpu; + + /* + * This code runs when the iova_domain is being detroyed, so don't + * bother to free iovas, just call the entry_dtor on all remaining + * entries. + */ + if (!iovad->entry_dtor) + return; + + for_each_possible_cpu(cpu) { + struct iova_fq *fq = per_cpu_ptr(iovad->fq, cpu); + int idx; + + fq_ring_for_each(idx, fq) + iovad->entry_dtor(fq->entries[idx].data); + } +} + +static void fq_flush_timeout(unsigned long data) +{ + struct iova_domain *iovad = (struct iova_domain *)data; + int cpu; + + atomic_set(&iovad->fq_timer_on, 0); + iova_domain_flush(iovad); + + for_each_possible_cpu(cpu) { + unsigned long flags; + struct iova_fq *fq; + + fq = per_cpu_ptr(iovad->fq, cpu); + spin_lock_irqsave(&fq->lock, flags); + fq_ring_free(iovad, fq); + spin_unlock_irqrestore(&fq->lock, flags); + } +} + +void queue_iova(struct iova_domain *iovad, + unsigned long pfn, unsigned long pages, + unsigned long data) +{ + struct iova_fq *fq = get_cpu_ptr(iovad->fq); + unsigned long flags; + unsigned idx; + + spin_lock_irqsave(&fq->lock, flags); + + /* + * First remove all entries from the flush queue that have already been + * flushed out on another CPU. This makes the fq_full() check below less + * likely to be true. + */ + fq_ring_free(iovad, fq); + + if (fq_full(fq)) { + iova_domain_flush(iovad); + fq_ring_free(iovad, fq); + } + + idx = fq_ring_add(fq); + + fq->entries[idx].iova_pfn = pfn; + fq->entries[idx].pages = pages; + fq->entries[idx].data = data; + fq->entries[idx].counter = atomic64_read(&iovad->fq_flush_start_cnt); + + spin_unlock_irqrestore(&fq->lock, flags); + + if (atomic_cmpxchg(&iovad->fq_timer_on, 0, 1) == 0) + mod_timer(&iovad->fq_timer, + jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT)); + + put_cpu_ptr(iovad->fq); +} +EXPORT_SYMBOL_GPL(queue_iova); + /** * put_iova_domain - destroys the iova doamin * @iovad: - iova domain in question. @@ -433,6 +615,7 @@ void put_iova_domain(struct iova_domain *iovad) struct rb_node *node; unsigned long flags; + free_iova_flush_queue(iovad); free_iova_rcaches(iovad); spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); node = rb_first(&iovad->rbroot); diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 2a38aa15be17..195d6e93ac71 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -19,6 +19,7 @@ #include <linux/iommu.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/sizes.h> #include <linux/slab.h> @@ -35,7 +36,7 @@ struct ipmmu_vmsa_device { struct device *dev; void __iomem *base; - struct list_head list; + struct iommu_device iommu; unsigned int num_utlbs; spinlock_t lock; /* Protects ctx and domains[] */ @@ -58,36 +59,18 @@ struct ipmmu_vmsa_domain { struct ipmmu_vmsa_iommu_priv { struct ipmmu_vmsa_device *mmu; - unsigned int *utlbs; - unsigned int num_utlbs; struct device *dev; struct list_head list; }; -static DEFINE_SPINLOCK(ipmmu_devices_lock); -static LIST_HEAD(ipmmu_devices); - static struct ipmmu_vmsa_domain *to_vmsa_domain(struct iommu_domain *dom) { return container_of(dom, struct ipmmu_vmsa_domain, io_domain); } - static struct ipmmu_vmsa_iommu_priv *to_priv(struct device *dev) { -#if defined(CONFIG_ARM) - return dev->archdata.iommu; -#else - return dev->iommu_fwspec->iommu_priv; -#endif -} -static void set_priv(struct device *dev, struct ipmmu_vmsa_iommu_priv *p) -{ -#if defined(CONFIG_ARM) - dev->archdata.iommu = p; -#else - dev->iommu_fwspec->iommu_priv = p; -#endif + return dev->iommu_fwspec ? dev->iommu_fwspec->iommu_priv : NULL; } #define TLB_LOOP_TIMEOUT 100 /* 100us */ @@ -312,7 +295,7 @@ static void ipmmu_tlb_add_flush(unsigned long iova, size_t size, /* The hardware doesn't support selective TLB flush. */ } -static struct iommu_gather_ops ipmmu_gather_ops = { +static const struct iommu_gather_ops ipmmu_gather_ops = { .tlb_flush_all = ipmmu_tlb_flush_all, .tlb_add_flush = ipmmu_tlb_add_flush, .tlb_sync = ipmmu_tlb_flush_all, @@ -341,6 +324,19 @@ static int ipmmu_domain_allocate_context(struct ipmmu_vmsa_device *mmu, return ret; } +static void ipmmu_domain_free_context(struct ipmmu_vmsa_device *mmu, + unsigned int context_id) +{ + unsigned long flags; + + spin_lock_irqsave(&mmu->lock, flags); + + clear_bit(context_id, mmu->ctx); + mmu->domains[context_id] = NULL; + + spin_unlock_irqrestore(&mmu->lock, flags); +} + static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) { u64 ttbr; @@ -370,22 +366,22 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) */ domain->cfg.iommu_dev = domain->mmu->dev; - domain->iop = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &domain->cfg, - domain); - if (!domain->iop) - return -EINVAL; - /* * Find an unused context. */ ret = ipmmu_domain_allocate_context(domain->mmu, domain); - if (ret == IPMMU_CTX_MAX) { - free_io_pgtable_ops(domain->iop); + if (ret == IPMMU_CTX_MAX) return -EBUSY; - } domain->context_id = ret; + domain->iop = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &domain->cfg, + domain); + if (!domain->iop) { + ipmmu_domain_free_context(domain->mmu, domain->context_id); + return -EINVAL; + } + /* TTBR0 */ ttbr = domain->cfg.arm_lpae_s1_cfg.ttbr[0]; ipmmu_ctx_write(domain, IMTTLBR0, ttbr); @@ -426,19 +422,6 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) return 0; } -static void ipmmu_domain_free_context(struct ipmmu_vmsa_device *mmu, - unsigned int context_id) -{ - unsigned long flags; - - spin_lock_irqsave(&mmu->lock, flags); - - clear_bit(context_id, mmu->ctx); - mmu->domains[context_id] = NULL; - - spin_unlock_irqrestore(&mmu->lock, flags); -} - static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain) { /* @@ -562,13 +545,14 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, struct device *dev) { struct ipmmu_vmsa_iommu_priv *priv = to_priv(dev); + struct iommu_fwspec *fwspec = dev->iommu_fwspec; struct ipmmu_vmsa_device *mmu = priv->mmu; struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); unsigned long flags; unsigned int i; int ret = 0; - if (!mmu) { + if (!priv || !priv->mmu) { dev_err(dev, "Cannot attach to IPMMU\n"); return -ENXIO; } @@ -595,8 +579,8 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, if (ret < 0) return ret; - for (i = 0; i < priv->num_utlbs; ++i) - ipmmu_utlb_enable(domain, priv->utlbs[i]); + for (i = 0; i < fwspec->num_ids; ++i) + ipmmu_utlb_enable(domain, fwspec->ids[i]); return 0; } @@ -604,12 +588,12 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, static void ipmmu_detach_device(struct iommu_domain *io_domain, struct device *dev) { - struct ipmmu_vmsa_iommu_priv *priv = to_priv(dev); + struct iommu_fwspec *fwspec = dev->iommu_fwspec; struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); unsigned int i; - for (i = 0; i < priv->num_utlbs; ++i) - ipmmu_utlb_disable(domain, priv->utlbs[i]); + for (i = 0; i < fwspec->num_ids; ++i) + ipmmu_utlb_disable(domain, fwspec->ids[i]); /* * TODO: Optimize by disabling the context when no device is attached. @@ -645,92 +629,36 @@ static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, return domain->iop->iova_to_phys(domain->iop, iova); } -static int ipmmu_find_utlbs(struct ipmmu_vmsa_device *mmu, struct device *dev, - unsigned int *utlbs, unsigned int num_utlbs) -{ - unsigned int i; - - for (i = 0; i < num_utlbs; ++i) { - struct of_phandle_args args; - int ret; - - ret = of_parse_phandle_with_args(dev->of_node, "iommus", - "#iommu-cells", i, &args); - if (ret < 0) - return ret; - - of_node_put(args.np); - - if (args.np != mmu->dev->of_node || args.args_count != 1) - return -EINVAL; - - utlbs[i] = args.args[0]; - } - - return 0; -} - -static int ipmmu_init_platform_device(struct device *dev) +static int ipmmu_init_platform_device(struct device *dev, + struct of_phandle_args *args) { + struct platform_device *ipmmu_pdev; struct ipmmu_vmsa_iommu_priv *priv; - struct ipmmu_vmsa_device *mmu; - unsigned int *utlbs; - unsigned int i; - int num_utlbs; - int ret = -ENODEV; - - /* Find the master corresponding to the device. */ - num_utlbs = of_count_phandle_with_args(dev->of_node, "iommus", - "#iommu-cells"); - if (num_utlbs < 0) + ipmmu_pdev = of_find_device_by_node(args->np); + if (!ipmmu_pdev) return -ENODEV; - utlbs = kcalloc(num_utlbs, sizeof(*utlbs), GFP_KERNEL); - if (!utlbs) - return -ENOMEM; - - spin_lock(&ipmmu_devices_lock); - - list_for_each_entry(mmu, &ipmmu_devices, list) { - ret = ipmmu_find_utlbs(mmu, dev, utlbs, num_utlbs); - if (!ret) { - /* - * TODO Take a reference to the MMU to protect - * against device removal. - */ - break; - } - } - - spin_unlock(&ipmmu_devices_lock); - - if (ret < 0) - goto error; - - for (i = 0; i < num_utlbs; ++i) { - if (utlbs[i] >= mmu->num_utlbs) { - ret = -EINVAL; - goto error; - } - } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto error; - } + if (!priv) + return -ENOMEM; - priv->mmu = mmu; - priv->utlbs = utlbs; - priv->num_utlbs = num_utlbs; + priv->mmu = platform_get_drvdata(ipmmu_pdev); priv->dev = dev; - set_priv(dev, priv); + dev->iommu_fwspec->iommu_priv = priv; return 0; +} -error: - kfree(utlbs); - return ret; +static int ipmmu_of_xlate(struct device *dev, + struct of_phandle_args *spec) +{ + iommu_fwspec_add_ids(dev, spec->args, 1); + + /* Initialize once - xlate() will call multiple times */ + if (to_priv(dev)) + return 0; + + return ipmmu_init_platform_device(dev, spec); } #if defined(CONFIG_ARM) && !defined(CONFIG_IOMMU_DMA) @@ -749,11 +677,11 @@ static int ipmmu_add_device(struct device *dev) struct iommu_group *group; int ret; - if (to_priv(dev)) { - dev_warn(dev, "IOMMU driver already assigned to device %s\n", - dev_name(dev)); - return -EINVAL; - } + /* + * Only let through devices that have been verified in xlate() + */ + if (!to_priv(dev)) + return -ENODEV; /* Create a device group and add the device to it. */ group = iommu_group_alloc(); @@ -772,10 +700,6 @@ static int ipmmu_add_device(struct device *dev) goto error; } - ret = ipmmu_init_platform_device(dev); - if (ret < 0) - goto error; - /* * Create the ARM mapping, used by the ARM DMA mapping core to allocate * VAs. This will allocate a corresponding IOMMU domain. @@ -816,24 +740,13 @@ error: if (!IS_ERR_OR_NULL(group)) iommu_group_remove_device(dev); - kfree(to_priv(dev)->utlbs); - kfree(to_priv(dev)); - set_priv(dev, NULL); - return ret; } static void ipmmu_remove_device(struct device *dev) { - struct ipmmu_vmsa_iommu_priv *priv = to_priv(dev); - arm_iommu_detach_device(dev); iommu_group_remove_device(dev); - - kfree(priv->utlbs); - kfree(priv); - - set_priv(dev, NULL); } static const struct iommu_ops ipmmu_ops = { @@ -848,6 +761,7 @@ static const struct iommu_ops ipmmu_ops = { .add_device = ipmmu_add_device, .remove_device = ipmmu_remove_device, .pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K, + .of_xlate = ipmmu_of_xlate, }; #endif /* !CONFIG_ARM && CONFIG_IOMMU_DMA */ @@ -890,14 +804,12 @@ static void ipmmu_domain_free_dma(struct iommu_domain *io_domain) static int ipmmu_add_device_dma(struct device *dev) { - struct iommu_fwspec *fwspec = dev->iommu_fwspec; struct iommu_group *group; /* * Only let through devices that have been verified in xlate() - * We may get called with dev->iommu_fwspec set to NULL. */ - if (!fwspec || !fwspec->iommu_priv) + if (!to_priv(dev)) return -ENODEV; group = iommu_group_get_for_dev(dev); @@ -957,19 +869,6 @@ static struct iommu_group *ipmmu_find_group_dma(struct device *dev) return group; } -static int ipmmu_of_xlate_dma(struct device *dev, - struct of_phandle_args *spec) -{ - /* If the IPMMU device is disabled in DT then return error - * to make sure the of_iommu code does not install ops - * even though the iommu device is disabled - */ - if (!of_device_is_available(spec->np)) - return -ENODEV; - - return ipmmu_init_platform_device(dev); -} - static const struct iommu_ops ipmmu_ops = { .domain_alloc = ipmmu_domain_alloc_dma, .domain_free = ipmmu_domain_free_dma, @@ -983,7 +882,7 @@ static const struct iommu_ops ipmmu_ops = { .remove_device = ipmmu_remove_device_dma, .device_group = ipmmu_find_group_dma, .pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K, - .of_xlate = ipmmu_of_xlate_dma, + .of_xlate = ipmmu_of_xlate, }; #endif /* CONFIG_IOMMU_DMA */ @@ -1054,16 +953,24 @@ static int ipmmu_probe(struct platform_device *pdev) ipmmu_device_reset(mmu); + ret = iommu_device_sysfs_add(&mmu->iommu, &pdev->dev, NULL, + dev_name(&pdev->dev)); + if (ret) + return ret; + + iommu_device_set_ops(&mmu->iommu, &ipmmu_ops); + iommu_device_set_fwnode(&mmu->iommu, &pdev->dev.of_node->fwnode); + + ret = iommu_device_register(&mmu->iommu); + if (ret) + return ret; + /* * We can't create the ARM mapping here as it requires the bus to have * an IOMMU, which only happens when bus_set_iommu() is called in * ipmmu_init() after the probe function returns. */ - spin_lock(&ipmmu_devices_lock); - list_add(&mmu->list, &ipmmu_devices); - spin_unlock(&ipmmu_devices_lock); - platform_set_drvdata(pdev, mmu); return 0; @@ -1073,9 +980,8 @@ static int ipmmu_remove(struct platform_device *pdev) { struct ipmmu_vmsa_device *mmu = platform_get_drvdata(pdev); - spin_lock(&ipmmu_devices_lock); - list_del(&mmu->list); - spin_unlock(&ipmmu_devices_lock); + iommu_device_sysfs_remove(&mmu->iommu); + iommu_device_unregister(&mmu->iommu); #if defined(CONFIG_ARM) && !defined(CONFIG_IOMMU_DMA) arm_iommu_release_mapping(mmu->mapping); diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index d0448353d501..04f4d51ffacb 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -393,6 +393,7 @@ static struct msm_iommu_dev *find_iommu_for_dev(struct device *dev) static int msm_iommu_add_device(struct device *dev) { struct msm_iommu_dev *iommu; + struct iommu_group *group; unsigned long flags; int ret = 0; @@ -406,7 +407,16 @@ static int msm_iommu_add_device(struct device *dev) spin_unlock_irqrestore(&msm_iommu_lock, flags); - return ret; + if (ret) + return ret; + + group = iommu_group_get_for_dev(dev); + if (IS_ERR(group)) + return PTR_ERR(group); + + iommu_group_put(group); + + return 0; } static void msm_iommu_remove_device(struct device *dev) @@ -421,6 +431,8 @@ static void msm_iommu_remove_device(struct device *dev) iommu_device_unlink(&iommu->iommu, dev); spin_unlock_irqrestore(&msm_iommu_lock, flags); + + iommu_group_remove_device(dev); } static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) @@ -700,6 +712,7 @@ static struct iommu_ops msm_iommu_ops = { .iova_to_phys = msm_iommu_iova_to_phys, .add_device = msm_iommu_add_device, .remove_device = msm_iommu_remove_device, + .device_group = generic_device_group, .pgsize_bitmap = MSM_IOMMU_PGSIZES, .of_xlate = qcom_iommu_of_xlate, }; diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c index 91c6d367ab35..bd515be5b380 100644 --- a/drivers/iommu/mtk_iommu.c +++ b/drivers/iommu/mtk_iommu.c @@ -31,7 +31,6 @@ #include <linux/slab.h> #include <linux/spinlock.h> #include <asm/barrier.h> -#include <dt-bindings/memory/mt8173-larb-port.h> #include <soc/mediatek/smi.h> #include "mtk_iommu.h" @@ -54,10 +53,16 @@ #define REG_MMU_CTRL_REG 0x110 #define F_MMU_PREFETCH_RT_REPLACE_MOD BIT(4) -#define F_MMU_TF_PROTECT_SEL(prot) (((prot) & 0x3) << 5) +#define F_MMU_TF_PROTECT_SEL_SHIFT(data) \ + ((data)->m4u_plat == M4U_MT2712 ? 4 : 5) +/* It's named by F_MMU_TF_PROT_SEL in mt2712. */ +#define F_MMU_TF_PROTECT_SEL(prot, data) \ + (((prot) & 0x3) << F_MMU_TF_PROTECT_SEL_SHIFT(data)) #define REG_MMU_IVRP_PADDR 0x114 #define F_MMU_IVRP_PA_SET(pa, ext) (((pa) >> 1) | ((!!(ext)) << 31)) +#define REG_MMU_VLD_PA_RNG 0x118 +#define F_MMU_VLD_PA_RNG(EA, SA) (((EA) << 8) | (SA)) #define REG_MMU_INT_CONTROL0 0x120 #define F_L2_MULIT_HIT_EN BIT(0) @@ -82,7 +87,6 @@ #define REG_MMU_FAULT_ST1 0x134 #define REG_MMU_FAULT_VA 0x13c -#define F_MMU_FAULT_VA_MSK 0xfffff000 #define F_MMU_FAULT_VA_WRITE_BIT BIT(1) #define F_MMU_FAULT_VA_LAYER_BIT BIT(0) @@ -93,6 +97,13 @@ #define MTK_PROTECT_PA_ALIGN 128 +/* + * Get the local arbiter ID and the portid within the larb arbiter + * from mtk_m4u_id which is defined by MTK_M4U_ID. + */ +#define MTK_M4U_TO_LARB(id) (((id) >> 5) & 0xf) +#define MTK_M4U_TO_PORT(id) ((id) & 0x1f) + struct mtk_iommu_domain { spinlock_t pgtlock; /* lock for page table */ @@ -104,6 +115,27 @@ struct mtk_iommu_domain { static struct iommu_ops mtk_iommu_ops; +static LIST_HEAD(m4ulist); /* List all the M4U HWs */ + +#define for_each_m4u(data) list_for_each_entry(data, &m4ulist, list) + +/* + * There may be 1 or 2 M4U HWs, But we always expect they are in the same domain + * for the performance. + * + * Here always return the mtk_iommu_data of the first probed M4U where the + * iommu domain information is recorded. + */ +static struct mtk_iommu_data *mtk_iommu_get_m4u_data(void) +{ + struct mtk_iommu_data *data; + + for_each_m4u(data) + return data; + + return NULL; +} + static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom) { return container_of(dom, struct mtk_iommu_domain, domain); @@ -113,9 +145,12 @@ static void mtk_iommu_tlb_flush_all(void *cookie) { struct mtk_iommu_data *data = cookie; - writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, data->base + REG_MMU_INV_SEL); - writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE); - wmb(); /* Make sure the tlb flush all done */ + for_each_m4u(data) { + writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, + data->base + REG_MMU_INV_SEL); + writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE); + wmb(); /* Make sure the tlb flush all done */ + } } static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size, @@ -124,12 +159,17 @@ static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size, { struct mtk_iommu_data *data = cookie; - writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, data->base + REG_MMU_INV_SEL); + for_each_m4u(data) { + writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, + data->base + REG_MMU_INV_SEL); - writel_relaxed(iova, data->base + REG_MMU_INVLD_START_A); - writel_relaxed(iova + size - 1, data->base + REG_MMU_INVLD_END_A); - writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE); - data->tlb_flush_active = true; + writel_relaxed(iova, data->base + REG_MMU_INVLD_START_A); + writel_relaxed(iova + size - 1, + data->base + REG_MMU_INVLD_END_A); + writel_relaxed(F_MMU_INV_RANGE, + data->base + REG_MMU_INVALIDATE); + data->tlb_flush_active = true; + } } static void mtk_iommu_tlb_sync(void *cookie) @@ -138,20 +178,22 @@ static void mtk_iommu_tlb_sync(void *cookie) int ret; u32 tmp; - /* Avoid timing out if there's nothing to wait for */ - if (!data->tlb_flush_active) - return; + for_each_m4u(data) { + /* Avoid timing out if there's nothing to wait for */ + if (!data->tlb_flush_active) + return; - ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, tmp, - tmp != 0, 10, 100000); - if (ret) { - dev_warn(data->dev, - "Partial TLB flush timed out, falling back to full flush\n"); - mtk_iommu_tlb_flush_all(cookie); + ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, + tmp, tmp != 0, 10, 100000); + if (ret) { + dev_warn(data->dev, + "Partial TLB flush timed out, falling back to full flush\n"); + mtk_iommu_tlb_flush_all(cookie); + } + /* Clear the CPE status */ + writel_relaxed(0, data->base + REG_MMU_CPE_DONE); + data->tlb_flush_active = false; } - /* Clear the CPE status */ - writel_relaxed(0, data->base + REG_MMU_CPE_DONE); - data->tlb_flush_active = false; } static const struct iommu_gather_ops mtk_iommu_gather_ops = { @@ -173,7 +215,6 @@ static irqreturn_t mtk_iommu_isr(int irq, void *dev_id) fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA); layer = fault_iova & F_MMU_FAULT_VA_LAYER_BIT; write = fault_iova & F_MMU_FAULT_VA_WRITE_BIT; - fault_iova &= F_MMU_FAULT_VA_MSK; fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA); regval = readl_relaxed(data->base + REG_MMU_INT_ID); fault_larb = F_MMU0_INT_ID_LARB_ID(regval); @@ -221,9 +262,9 @@ static void mtk_iommu_config(struct mtk_iommu_data *data, } } -static int mtk_iommu_domain_finalise(struct mtk_iommu_data *data) +static int mtk_iommu_domain_finalise(struct mtk_iommu_domain *dom) { - struct mtk_iommu_domain *dom = data->m4u_dom; + struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); spin_lock_init(&dom->pgtlock); @@ -249,9 +290,6 @@ static int mtk_iommu_domain_finalise(struct mtk_iommu_data *data) /* Update our support page sizes bitmap */ dom->domain.pgsize_bitmap = dom->cfg.pgsize_bitmap; - - writel(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0], - data->base + REG_MMU_PT_BASE_ADDR); return 0; } @@ -266,20 +304,30 @@ static struct iommu_domain *mtk_iommu_domain_alloc(unsigned type) if (!dom) return NULL; - if (iommu_get_dma_cookie(&dom->domain)) { - kfree(dom); - return NULL; - } + if (iommu_get_dma_cookie(&dom->domain)) + goto free_dom; + + if (mtk_iommu_domain_finalise(dom)) + goto put_dma_cookie; dom->domain.geometry.aperture_start = 0; dom->domain.geometry.aperture_end = DMA_BIT_MASK(32); dom->domain.geometry.force_aperture = true; return &dom->domain; + +put_dma_cookie: + iommu_put_dma_cookie(&dom->domain); +free_dom: + kfree(dom); + return NULL; } static void mtk_iommu_domain_free(struct iommu_domain *domain) { + struct mtk_iommu_domain *dom = to_mtk_domain(domain); + + free_io_pgtable_ops(dom->iop); iommu_put_dma_cookie(domain); kfree(to_mtk_domain(domain)); } @@ -289,22 +337,15 @@ static int mtk_iommu_attach_device(struct iommu_domain *domain, { struct mtk_iommu_domain *dom = to_mtk_domain(domain); struct mtk_iommu_data *data = dev->iommu_fwspec->iommu_priv; - int ret; if (!data) return -ENODEV; + /* Update the pgtable base address register of the M4U HW */ if (!data->m4u_dom) { data->m4u_dom = dom; - ret = mtk_iommu_domain_finalise(data); - if (ret) { - data->m4u_dom = NULL; - return ret; - } - } else if (data->m4u_dom != dom) { - /* All the client devices should be in the same m4u domain */ - dev_err(dev, "try to attach into the error iommu domain\n"); - return -EPERM; + writel(dom->cfg.arm_v7s_cfg.ttbr[0], + data->base + REG_MMU_PT_BASE_ADDR); } mtk_iommu_config(data, dev, true); @@ -354,6 +395,7 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { struct mtk_iommu_domain *dom = to_mtk_domain(domain); + struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); unsigned long flags; phys_addr_t pa; @@ -361,6 +403,9 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, pa = dom->iop->iova_to_phys(dom->iop, iova); spin_unlock_irqrestore(&dom->pgtlock, flags); + if (data->enable_4GB) + pa |= BIT_ULL(32); + return pa; } @@ -399,7 +444,7 @@ static void mtk_iommu_remove_device(struct device *dev) static struct iommu_group *mtk_iommu_device_group(struct device *dev) { - struct mtk_iommu_data *data = dev->iommu_fwspec->iommu_priv; + struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); if (!data) return ERR_PTR(-ENODEV); @@ -464,8 +509,9 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) return ret; } - regval = F_MMU_PREFETCH_RT_REPLACE_MOD | - F_MMU_TF_PROTECT_SEL(2); + regval = F_MMU_TF_PROTECT_SEL(2, data); + if (data->m4u_plat == M4U_MT8173) + regval |= F_MMU_PREFETCH_RT_REPLACE_MOD; writel_relaxed(regval, data->base + REG_MMU_CTRL_REG); regval = F_L2_MULIT_HIT_EN | @@ -487,9 +533,19 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) writel_relaxed(F_MMU_IVRP_PA_SET(data->protect_base, data->enable_4GB), data->base + REG_MMU_IVRP_PADDR); - + if (data->enable_4GB && data->m4u_plat != M4U_MT8173) { + /* + * If 4GB mode is enabled, the validate PA range is from + * 0x1_0000_0000 to 0x1_ffff_ffff. here record bit[32:30]. + */ + regval = F_MMU_VLD_PA_RNG(7, 4); + writel_relaxed(regval, data->base + REG_MMU_VLD_PA_RNG); + } writel_relaxed(0, data->base + REG_MMU_DCM_DIS); - writel_relaxed(0, data->base + REG_MMU_STANDARD_AXI_MODE); + + /* It's MISC control register whose default value is ok except mt8173.*/ + if (data->m4u_plat == M4U_MT8173) + writel_relaxed(0, data->base + REG_MMU_STANDARD_AXI_MODE); if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0, dev_name(data->dev), (void *)data)) { @@ -521,6 +577,7 @@ static int mtk_iommu_probe(struct platform_device *pdev) if (!data) return -ENOMEM; data->dev = dev; + data->m4u_plat = (enum mtk_iommu_plat)of_device_get_match_data(dev); /* Protect memory. HW will access here while translation fault.*/ protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2, GFP_KERNEL); @@ -529,7 +586,7 @@ static int mtk_iommu_probe(struct platform_device *pdev) data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN); /* Whether the current dram is over 4GB */ - data->enable_4GB = !!(max_pfn > (0xffffffffUL >> PAGE_SHIFT)); + data->enable_4GB = !!(max_pfn > (BIT_ULL(32) >> PAGE_SHIFT)); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); data->base = devm_ioremap_resource(dev, res); @@ -554,6 +611,7 @@ static int mtk_iommu_probe(struct platform_device *pdev) for (i = 0; i < larb_nr; i++) { struct device_node *larbnode; struct platform_device *plarbdev; + u32 id; larbnode = of_parse_phandle(dev->of_node, "mediatek,larbs", i); if (!larbnode) @@ -562,17 +620,14 @@ static int mtk_iommu_probe(struct platform_device *pdev) if (!of_device_is_available(larbnode)) continue; + ret = of_property_read_u32(larbnode, "mediatek,larb-id", &id); + if (ret)/* The id is consecutive if there is no this property */ + id = i; + plarbdev = of_find_device_by_node(larbnode); - if (!plarbdev) { - plarbdev = of_platform_device_create( - larbnode, NULL, - platform_bus_type.dev_root); - if (!plarbdev) { - of_node_put(larbnode); - return -EPROBE_DEFER; - } - } - data->smi_imu.larb_imu[i].dev = &plarbdev->dev; + if (!plarbdev) + return -EPROBE_DEFER; + data->smi_imu.larb_imu[id].dev = &plarbdev->dev; component_match_add_release(dev, &match, release_of, compare_of, larbnode); @@ -596,6 +651,8 @@ static int mtk_iommu_probe(struct platform_device *pdev) if (ret) return ret; + list_add_tail(&data->list, &m4ulist); + if (!iommu_present(&platform_bus_type)) bus_set_iommu(&platform_bus_type, &mtk_iommu_ops); @@ -612,7 +669,6 @@ static int mtk_iommu_remove(struct platform_device *pdev) if (iommu_present(&platform_bus_type)) bus_set_iommu(&platform_bus_type, NULL); - free_io_pgtable_ops(data->m4u_dom->iop); clk_disable_unprepare(data->bclk); devm_free_irq(&pdev->dev, data->irq, data); component_master_del(&pdev->dev, &mtk_iommu_com_ops); @@ -631,6 +687,7 @@ static int __maybe_unused mtk_iommu_suspend(struct device *dev) reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG); reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL0); reg->int_main_control = readl_relaxed(base + REG_MMU_INT_MAIN_CONTROL); + clk_disable_unprepare(data->bclk); return 0; } @@ -639,9 +696,13 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev) struct mtk_iommu_data *data = dev_get_drvdata(dev); struct mtk_iommu_suspend_reg *reg = &data->reg; void __iomem *base = data->base; + int ret; - writel_relaxed(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0], - base + REG_MMU_PT_BASE_ADDR); + ret = clk_prepare_enable(data->bclk); + if (ret) { + dev_err(data->dev, "Failed to enable clk(%d) in resume\n", ret); + return ret; + } writel_relaxed(reg->standard_axi_mode, base + REG_MMU_STANDARD_AXI_MODE); writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM_DIS); @@ -650,15 +711,19 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev) writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL); writel_relaxed(F_MMU_IVRP_PA_SET(data->protect_base, data->enable_4GB), base + REG_MMU_IVRP_PADDR); + if (data->m4u_dom) + writel(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0], + base + REG_MMU_PT_BASE_ADDR); return 0; } -const struct dev_pm_ops mtk_iommu_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume) +static const struct dev_pm_ops mtk_iommu_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume) }; static const struct of_device_id mtk_iommu_of_ids[] = { - { .compatible = "mediatek,mt8173-m4u", }, + { .compatible = "mediatek,mt2712-m4u", .data = (void *)M4U_MT2712}, + { .compatible = "mediatek,mt8173-m4u", .data = (void *)M4U_MT8173}, {} }; @@ -667,27 +732,20 @@ static struct platform_driver mtk_iommu_driver = { .remove = mtk_iommu_remove, .driver = { .name = "mtk-iommu", - .of_match_table = mtk_iommu_of_ids, + .of_match_table = of_match_ptr(mtk_iommu_of_ids), .pm = &mtk_iommu_pm_ops, } }; -static int mtk_iommu_init_fn(struct device_node *np) +static int __init mtk_iommu_init(void) { int ret; - struct platform_device *pdev; - - pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root); - if (!pdev) - return -ENOMEM; ret = platform_driver_register(&mtk_iommu_driver); - if (ret) { - pr_err("%s: Failed to register driver\n", __func__); - return ret; - } + if (ret != 0) + pr_err("Failed to register MTK IOMMU driver\n"); - return 0; + return ret; } -IOMMU_OF_DECLARE(mtkm4u, "mediatek,mt8173-m4u", mtk_iommu_init_fn); +subsys_initcall(mtk_iommu_init) diff --git a/drivers/iommu/mtk_iommu.h b/drivers/iommu/mtk_iommu.h index c06cc91b5d9a..b4451a1c7c2f 100644 --- a/drivers/iommu/mtk_iommu.h +++ b/drivers/iommu/mtk_iommu.h @@ -34,6 +34,12 @@ struct mtk_iommu_suspend_reg { u32 int_main_control; }; +enum mtk_iommu_plat { + M4U_MT2701, + M4U_MT2712, + M4U_MT8173, +}; + struct mtk_iommu_domain; struct mtk_iommu_data { @@ -50,6 +56,9 @@ struct mtk_iommu_data { bool tlb_flush_active; struct iommu_device iommu; + enum mtk_iommu_plat m4u_plat; + + struct list_head list; }; static inline int compare_of(struct device *dev, void *data) diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 8cb60829a7a1..e60e3dba85a0 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -25,6 +25,8 @@ #include <linux/of_pci.h> #include <linux/slab.h> +#define NO_IOMMU 1 + static const struct of_device_id __iommu_of_table_sentinel __used __section(__iommu_of_table_end); @@ -109,8 +111,8 @@ static bool of_iommu_driver_present(struct device_node *np) return of_match_node(&__iommu_of_table, np); } -static const struct iommu_ops -*of_iommu_xlate(struct device *dev, struct of_phandle_args *iommu_spec) +static int of_iommu_xlate(struct device *dev, + struct of_phandle_args *iommu_spec) { const struct iommu_ops *ops; struct fwnode_handle *fwnode = &iommu_spec->np->fwnode; @@ -120,95 +122,53 @@ static const struct iommu_ops if ((ops && !ops->of_xlate) || !of_device_is_available(iommu_spec->np) || (!ops && !of_iommu_driver_present(iommu_spec->np))) - return NULL; + return NO_IOMMU; err = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, ops); if (err) - return ERR_PTR(err); + return err; /* * The otherwise-empty fwspec handily serves to indicate the specific * IOMMU device we're waiting for, which will be useful if we ever get * a proper probe-ordering dependency mechanism in future. */ if (!ops) - return ERR_PTR(-EPROBE_DEFER); - - err = ops->of_xlate(dev, iommu_spec); - if (err) - return ERR_PTR(err); + return -EPROBE_DEFER; - return ops; + return ops->of_xlate(dev, iommu_spec); } -static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data) -{ - struct of_phandle_args *iommu_spec = data; - - iommu_spec->args[0] = alias; - return iommu_spec->np == pdev->bus->dev.of_node; -} +struct of_pci_iommu_alias_info { + struct device *dev; + struct device_node *np; +}; -static const struct iommu_ops -*of_pci_iommu_init(struct pci_dev *pdev, struct device_node *bridge_np) +static int of_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) { - const struct iommu_ops *ops; - struct of_phandle_args iommu_spec; + struct of_pci_iommu_alias_info *info = data; + struct of_phandle_args iommu_spec = { .args_count = 1 }; int err; - /* - * Start by tracing the RID alias down the PCI topology as - * far as the host bridge whose OF node we have... - * (we're not even attempting to handle multi-alias devices yet) - */ - iommu_spec.args_count = 1; - iommu_spec.np = bridge_np; - pci_for_each_dma_alias(pdev, __get_pci_rid, &iommu_spec); - /* - * ...then find out what that becomes once it escapes the PCI - * bus into the system beyond, and which IOMMU it ends up at. - */ - iommu_spec.np = NULL; - err = of_pci_map_rid(bridge_np, iommu_spec.args[0], "iommu-map", + err = of_pci_map_rid(info->np, alias, "iommu-map", "iommu-map-mask", &iommu_spec.np, iommu_spec.args); if (err) - return err == -ENODEV ? NULL : ERR_PTR(err); - - ops = of_iommu_xlate(&pdev->dev, &iommu_spec); + return err == -ENODEV ? NO_IOMMU : err; + err = of_iommu_xlate(info->dev, &iommu_spec); of_node_put(iommu_spec.np); - return ops; -} - -static const struct iommu_ops -*of_platform_iommu_init(struct device *dev, struct device_node *np) -{ - struct of_phandle_args iommu_spec; - const struct iommu_ops *ops = NULL; - int idx = 0; - - /* - * We don't currently walk up the tree looking for a parent IOMMU. - * See the `Notes:' section of - * Documentation/devicetree/bindings/iommu/iommu.txt - */ - while (!of_parse_phandle_with_args(np, "iommus", "#iommu-cells", - idx, &iommu_spec)) { - ops = of_iommu_xlate(dev, &iommu_spec); - of_node_put(iommu_spec.np); - idx++; - if (IS_ERR_OR_NULL(ops)) - break; - } + if (err) + return err; - return ops; + return info->np == pdev->bus->dev.of_node; } const struct iommu_ops *of_iommu_configure(struct device *dev, struct device_node *master_np) { - const struct iommu_ops *ops; + const struct iommu_ops *ops = NULL; struct iommu_fwspec *fwspec = dev->iommu_fwspec; + int err = NO_IOMMU; if (!master_np) return NULL; @@ -221,25 +181,54 @@ const struct iommu_ops *of_iommu_configure(struct device *dev, iommu_fwspec_free(dev); } - if (dev_is_pci(dev)) - ops = of_pci_iommu_init(to_pci_dev(dev), master_np); - else - ops = of_platform_iommu_init(dev, master_np); + /* + * We don't currently walk up the tree looking for a parent IOMMU. + * See the `Notes:' section of + * Documentation/devicetree/bindings/iommu/iommu.txt + */ + if (dev_is_pci(dev)) { + struct of_pci_iommu_alias_info info = { + .dev = dev, + .np = master_np, + }; + + err = pci_for_each_dma_alias(to_pci_dev(dev), + of_pci_iommu_init, &info); + } else { + struct of_phandle_args iommu_spec; + int idx = 0; + + while (!of_parse_phandle_with_args(master_np, "iommus", + "#iommu-cells", + idx, &iommu_spec)) { + err = of_iommu_xlate(dev, &iommu_spec); + of_node_put(iommu_spec.np); + idx++; + if (err) + break; + } + } + + /* + * Two success conditions can be represented by non-negative err here: + * >0 : there is no IOMMU, or one was unavailable for non-fatal reasons + * 0 : we found an IOMMU, and dev->fwspec is initialised appropriately + * <0 : any actual error + */ + if (!err) + ops = dev->iommu_fwspec->ops; /* * If we have reason to believe the IOMMU driver missed the initial * add_device callback for dev, replay it to get things in order. */ - if (!IS_ERR_OR_NULL(ops) && ops->add_device && - dev->bus && !dev->iommu_group) { - int err = ops->add_device(dev); - - if (err) - ops = ERR_PTR(err); - } + if (ops && ops->add_device && dev->bus && !dev->iommu_group) + err = ops->add_device(dev); /* Ignore all other errors apart from EPROBE_DEFER */ - if (IS_ERR(ops) && (PTR_ERR(ops) != -EPROBE_DEFER)) { - dev_dbg(dev, "Adding to IOMMU failed: %ld\n", PTR_ERR(ops)); + if (err == -EPROBE_DEFER) { + ops = ERR_PTR(err); + } else if (err < 0) { + dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); ops = NULL; } @@ -255,8 +244,7 @@ static int __init of_iommu_init(void) const of_iommu_init_fn init_fn = match->data; if (init_fn && init_fn(np)) - pr_err("Failed to initialise IOMMU %s\n", - of_node_full_name(np)); + pr_err("Failed to initialise IOMMU %pOF\n", np); } return 0; diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index 641e035cf866..bd67e1b2c64e 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -11,6 +11,7 @@ * published by the Free Software Foundation. */ +#include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/interrupt.h> @@ -29,8 +30,6 @@ #include <linux/regmap.h> #include <linux/mfd/syscon.h> -#include <asm/cacheflush.h> - #include <linux/platform_data/iommu-omap.h> #include "omap-iopgtable.h" @@ -454,36 +453,35 @@ static void flush_iotlb_all(struct omap_iommu *obj) /* * H/W pagetable operations */ -static void flush_iopgd_range(u32 *first, u32 *last) +static void flush_iopte_range(struct device *dev, dma_addr_t dma, + unsigned long offset, int num_entries) { - /* FIXME: L2 cache should be taken care of if it exists */ - do { - asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pgd" - : : "r" (first)); - first += L1_CACHE_BYTES / sizeof(*first); - } while (first <= last); -} + size_t size = num_entries * sizeof(u32); -static void flush_iopte_range(u32 *first, u32 *last) -{ - /* FIXME: L2 cache should be taken care of if it exists */ - do { - asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pte" - : : "r" (first)); - first += L1_CACHE_BYTES / sizeof(*first); - } while (first <= last); + dma_sync_single_range_for_device(dev, dma, offset, size, DMA_TO_DEVICE); } -static void iopte_free(u32 *iopte) +static void iopte_free(struct omap_iommu *obj, u32 *iopte, bool dma_valid) { + dma_addr_t pt_dma; + /* Note: freed iopte's must be clean ready for re-use */ - if (iopte) + if (iopte) { + if (dma_valid) { + pt_dma = virt_to_phys(iopte); + dma_unmap_single(obj->dev, pt_dma, IOPTE_TABLE_SIZE, + DMA_TO_DEVICE); + } + kmem_cache_free(iopte_cachep, iopte); + } } -static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da) +static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, + dma_addr_t *pt_dma, u32 da) { u32 *iopte; + unsigned long offset = iopgd_index(da) * sizeof(da); /* a table has already existed */ if (*iopgd) @@ -500,18 +498,38 @@ static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da) if (!iopte) return ERR_PTR(-ENOMEM); + *pt_dma = dma_map_single(obj->dev, iopte, IOPTE_TABLE_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(obj->dev, *pt_dma)) { + dev_err(obj->dev, "DMA map error for L2 table\n"); + iopte_free(obj, iopte, false); + return ERR_PTR(-ENOMEM); + } + + /* + * we rely on dma address and the physical address to be + * the same for mapping the L2 table + */ + if (WARN_ON(*pt_dma != virt_to_phys(iopte))) { + dev_err(obj->dev, "DMA translation error for L2 table\n"); + dma_unmap_single(obj->dev, *pt_dma, IOPTE_TABLE_SIZE, + DMA_TO_DEVICE); + iopte_free(obj, iopte, false); + return ERR_PTR(-ENOMEM); + } + *iopgd = virt_to_phys(iopte) | IOPGD_TABLE; - flush_iopgd_range(iopgd, iopgd); + flush_iopte_range(obj->dev, obj->pd_dma, offset, 1); dev_vdbg(obj->dev, "%s: a new pte:%p\n", __func__, iopte); } else { /* We raced, free the reduniovant table */ - iopte_free(iopte); + iopte_free(obj, iopte, false); } pte_ready: iopte = iopte_offset(iopgd, da); - + *pt_dma = virt_to_phys(iopte); dev_vdbg(obj->dev, "%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n", __func__, da, iopgd, *iopgd, iopte, *iopte); @@ -522,6 +540,7 @@ pte_ready: static int iopgd_alloc_section(struct omap_iommu *obj, u32 da, u32 pa, u32 prot) { u32 *iopgd = iopgd_offset(obj, da); + unsigned long offset = iopgd_index(da) * sizeof(da); if ((da | pa) & ~IOSECTION_MASK) { dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n", @@ -530,13 +549,14 @@ static int iopgd_alloc_section(struct omap_iommu *obj, u32 da, u32 pa, u32 prot) } *iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION; - flush_iopgd_range(iopgd, iopgd); + flush_iopte_range(obj->dev, obj->pd_dma, offset, 1); return 0; } static int iopgd_alloc_super(struct omap_iommu *obj, u32 da, u32 pa, u32 prot) { u32 *iopgd = iopgd_offset(obj, da); + unsigned long offset = iopgd_index(da) * sizeof(da); int i; if ((da | pa) & ~IOSUPER_MASK) { @@ -547,20 +567,22 @@ static int iopgd_alloc_super(struct omap_iommu *obj, u32 da, u32 pa, u32 prot) for (i = 0; i < 16; i++) *(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER; - flush_iopgd_range(iopgd, iopgd + 15); + flush_iopte_range(obj->dev, obj->pd_dma, offset, 16); return 0; } static int iopte_alloc_page(struct omap_iommu *obj, u32 da, u32 pa, u32 prot) { u32 *iopgd = iopgd_offset(obj, da); - u32 *iopte = iopte_alloc(obj, iopgd, da); + dma_addr_t pt_dma; + u32 *iopte = iopte_alloc(obj, iopgd, &pt_dma, da); + unsigned long offset = iopte_index(da) * sizeof(da); if (IS_ERR(iopte)) return PTR_ERR(iopte); *iopte = (pa & IOPAGE_MASK) | prot | IOPTE_SMALL; - flush_iopte_range(iopte, iopte); + flush_iopte_range(obj->dev, pt_dma, offset, 1); dev_vdbg(obj->dev, "%s: da:%08x pa:%08x pte:%p *pte:%08x\n", __func__, da, pa, iopte, *iopte); @@ -571,7 +593,9 @@ static int iopte_alloc_page(struct omap_iommu *obj, u32 da, u32 pa, u32 prot) static int iopte_alloc_large(struct omap_iommu *obj, u32 da, u32 pa, u32 prot) { u32 *iopgd = iopgd_offset(obj, da); - u32 *iopte = iopte_alloc(obj, iopgd, da); + dma_addr_t pt_dma; + u32 *iopte = iopte_alloc(obj, iopgd, &pt_dma, da); + unsigned long offset = iopte_index(da) * sizeof(da); int i; if ((da | pa) & ~IOLARGE_MASK) { @@ -585,7 +609,7 @@ static int iopte_alloc_large(struct omap_iommu *obj, u32 da, u32 pa, u32 prot) for (i = 0; i < 16; i++) *(iopte + i) = (pa & IOLARGE_MASK) | prot | IOPTE_LARGE; - flush_iopte_range(iopte, iopte + 15); + flush_iopte_range(obj->dev, pt_dma, offset, 16); return 0; } @@ -674,6 +698,9 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da) size_t bytes; u32 *iopgd = iopgd_offset(obj, da); int nent = 1; + dma_addr_t pt_dma; + unsigned long pd_offset = iopgd_index(da) * sizeof(da); + unsigned long pt_offset = iopte_index(da) * sizeof(da); if (!*iopgd) return 0; @@ -690,7 +717,8 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da) } bytes *= nent; memset(iopte, 0, nent * sizeof(*iopte)); - flush_iopte_range(iopte, iopte + (nent - 1) * sizeof(*iopte)); + pt_dma = virt_to_phys(iopte); + flush_iopte_range(obj->dev, pt_dma, pt_offset, nent); /* * do table walk to check if this table is necessary or not @@ -700,7 +728,7 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da) if (iopte[i]) goto out; - iopte_free(iopte); + iopte_free(obj, iopte, true); nent = 1; /* for the next L1 entry */ } else { bytes = IOPGD_SIZE; @@ -712,7 +740,7 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da) bytes *= nent; } memset(iopgd, 0, nent * sizeof(*iopgd)); - flush_iopgd_range(iopgd, iopgd + (nent - 1) * sizeof(*iopgd)); + flush_iopte_range(obj->dev, obj->pd_dma, pd_offset, nent); out: return bytes; } @@ -738,6 +766,7 @@ static size_t iopgtable_clear_entry(struct omap_iommu *obj, u32 da) static void iopgtable_clear_entry_all(struct omap_iommu *obj) { + unsigned long offset; int i; spin_lock(&obj->page_table_lock); @@ -748,15 +777,16 @@ static void iopgtable_clear_entry_all(struct omap_iommu *obj) da = i << IOPGD_SHIFT; iopgd = iopgd_offset(obj, da); + offset = iopgd_index(da) * sizeof(da); if (!*iopgd) continue; if (iopgd_is_table(*iopgd)) - iopte_free(iopte_offset(iopgd, 0)); + iopte_free(obj, iopte_offset(iopgd, 0), true); *iopgd = 0; - flush_iopgd_range(iopgd, iopgd); + flush_iopte_range(obj->dev, obj->pd_dma, offset, 1); } flush_iotlb_all(obj); @@ -786,7 +816,7 @@ static irqreturn_t iommu_fault_handler(int irq, void *data) if (!report_iommu_fault(domain, obj->dev, da, 0)) return IRQ_HANDLED; - iommu_disable(obj); + iommu_write_reg(obj, 0, MMU_IRQENABLE); iopgd = iopgd_offset(obj, da); @@ -815,10 +845,18 @@ static int omap_iommu_attach(struct omap_iommu *obj, u32 *iopgd) spin_lock(&obj->iommu_lock); + obj->pd_dma = dma_map_single(obj->dev, iopgd, IOPGD_TABLE_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(obj->dev, obj->pd_dma)) { + dev_err(obj->dev, "DMA map error for L1 table\n"); + err = -ENOMEM; + goto out_err; + } + obj->iopgd = iopgd; err = iommu_enable(obj); if (err) - goto err_enable; + goto out_err; flush_iotlb_all(obj); spin_unlock(&obj->iommu_lock); @@ -827,7 +865,7 @@ static int omap_iommu_attach(struct omap_iommu *obj, u32 *iopgd) return 0; -err_enable: +out_err: spin_unlock(&obj->iommu_lock); return err; @@ -844,7 +882,10 @@ static void omap_iommu_detach(struct omap_iommu *obj) spin_lock(&obj->iommu_lock); + dma_unmap_single(obj->dev, obj->pd_dma, IOPGD_TABLE_SIZE, + DMA_TO_DEVICE); iommu_disable(obj); + obj->pd_dma = 0; obj->iopgd = NULL; spin_unlock(&obj->iommu_lock); @@ -1008,11 +1049,6 @@ static struct platform_driver omap_iommu_driver = { }, }; -static void iopte_cachep_ctor(void *iopte) -{ - clean_dcache_area(iopte, IOPTE_TABLE_SIZE); -} - static u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa, int pgsz) { memset(e, 0, sizeof(*e)); @@ -1159,7 +1195,6 @@ static struct iommu_domain *omap_iommu_domain_alloc(unsigned type) if (WARN_ON(!IS_ALIGNED((long)omap_domain->pgtable, IOPGD_TABLE_SIZE))) goto fail_align; - clean_dcache_area(omap_domain->pgtable, IOPGD_TABLE_SIZE); spin_lock_init(&omap_domain->lock); omap_domain->domain.geometry.aperture_start = 0; @@ -1347,7 +1382,7 @@ static int __init omap_iommu_init(void) of_node_put(np); p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, align, flags, - iopte_cachep_ctor); + NULL); if (!p) return -ENOMEM; iopte_cachep = p; diff --git a/drivers/iommu/omap-iommu.h b/drivers/iommu/omap-iommu.h index 6e70515e6038..a675af29a6ec 100644 --- a/drivers/iommu/omap-iommu.h +++ b/drivers/iommu/omap-iommu.h @@ -61,6 +61,7 @@ struct omap_iommu { */ u32 *iopgd; spinlock_t page_table_lock; /* protect iopgd */ + dma_addr_t pd_dma; int nr_tlb_entries; diff --git a/drivers/iommu/qcom_iommu.c b/drivers/iommu/qcom_iommu.c new file mode 100644 index 000000000000..c8a587d034b0 --- /dev/null +++ b/drivers/iommu/qcom_iommu.c @@ -0,0 +1,930 @@ +/* + * IOMMU API for QCOM secure IOMMUs. Somewhat based on arm-smmu.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (C) 2013 ARM Limited + * Copyright (C) 2017 Red Hat + */ + +#include <linux/atomic.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-iommu.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/io-64-nonatomic-hi-lo.h> +#include <linux/iommu.h> +#include <linux/iopoll.h> +#include <linux/kconfig.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_iommu.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/qcom_scm.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "io-pgtable.h" +#include "arm-smmu-regs.h" + +#define SMMU_INTR_SEL_NS 0x2000 + +struct qcom_iommu_ctx; + +struct qcom_iommu_dev { + /* IOMMU core code handle */ + struct iommu_device iommu; + struct device *dev; + struct clk *iface_clk; + struct clk *bus_clk; + void __iomem *local_base; + u32 sec_id; + u8 num_ctxs; + struct qcom_iommu_ctx *ctxs[0]; /* indexed by asid-1 */ +}; + +struct qcom_iommu_ctx { + struct device *dev; + void __iomem *base; + bool secure_init; + u8 asid; /* asid and ctx bank # are 1:1 */ +}; + +struct qcom_iommu_domain { + struct io_pgtable_ops *pgtbl_ops; + spinlock_t pgtbl_lock; + struct mutex init_mutex; /* Protects iommu pointer */ + struct iommu_domain domain; + struct qcom_iommu_dev *iommu; +}; + +static struct qcom_iommu_domain *to_qcom_iommu_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct qcom_iommu_domain, domain); +} + +static const struct iommu_ops qcom_iommu_ops; + +static struct qcom_iommu_dev * to_iommu(struct iommu_fwspec *fwspec) +{ + if (!fwspec || fwspec->ops != &qcom_iommu_ops) + return NULL; + return fwspec->iommu_priv; +} + +static struct qcom_iommu_ctx * to_ctx(struct iommu_fwspec *fwspec, unsigned asid) +{ + struct qcom_iommu_dev *qcom_iommu = to_iommu(fwspec); + if (!qcom_iommu) + return NULL; + return qcom_iommu->ctxs[asid - 1]; +} + +static inline void +iommu_writel(struct qcom_iommu_ctx *ctx, unsigned reg, u32 val) +{ + writel_relaxed(val, ctx->base + reg); +} + +static inline void +iommu_writeq(struct qcom_iommu_ctx *ctx, unsigned reg, u64 val) +{ + writeq_relaxed(val, ctx->base + reg); +} + +static inline u32 +iommu_readl(struct qcom_iommu_ctx *ctx, unsigned reg) +{ + return readl_relaxed(ctx->base + reg); +} + +static inline u64 +iommu_readq(struct qcom_iommu_ctx *ctx, unsigned reg) +{ + return readq_relaxed(ctx->base + reg); +} + +static void qcom_iommu_tlb_sync(void *cookie) +{ + struct iommu_fwspec *fwspec = cookie; + unsigned i; + + for (i = 0; i < fwspec->num_ids; i++) { + struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]); + unsigned int val, ret; + + iommu_writel(ctx, ARM_SMMU_CB_TLBSYNC, 0); + + ret = readl_poll_timeout(ctx->base + ARM_SMMU_CB_TLBSTATUS, val, + (val & 0x1) == 0, 0, 5000000); + if (ret) + dev_err(ctx->dev, "timeout waiting for TLB SYNC\n"); + } +} + +static void qcom_iommu_tlb_inv_context(void *cookie) +{ + struct iommu_fwspec *fwspec = cookie; + unsigned i; + + for (i = 0; i < fwspec->num_ids; i++) { + struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]); + iommu_writel(ctx, ARM_SMMU_CB_S1_TLBIASID, ctx->asid); + } + + qcom_iommu_tlb_sync(cookie); +} + +static void qcom_iommu_tlb_inv_range_nosync(unsigned long iova, size_t size, + size_t granule, bool leaf, void *cookie) +{ + struct iommu_fwspec *fwspec = cookie; + unsigned i, reg; + + reg = leaf ? ARM_SMMU_CB_S1_TLBIVAL : ARM_SMMU_CB_S1_TLBIVA; + + for (i = 0; i < fwspec->num_ids; i++) { + struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]); + size_t s = size; + + iova &= ~12UL; + iova |= ctx->asid; + do { + iommu_writel(ctx, reg, iova); + iova += granule; + } while (s -= granule); + } +} + +static const struct iommu_gather_ops qcom_gather_ops = { + .tlb_flush_all = qcom_iommu_tlb_inv_context, + .tlb_add_flush = qcom_iommu_tlb_inv_range_nosync, + .tlb_sync = qcom_iommu_tlb_sync, +}; + +static irqreturn_t qcom_iommu_fault(int irq, void *dev) +{ + struct qcom_iommu_ctx *ctx = dev; + u32 fsr, fsynr; + u64 iova; + + fsr = iommu_readl(ctx, ARM_SMMU_CB_FSR); + + if (!(fsr & FSR_FAULT)) + return IRQ_NONE; + + fsynr = iommu_readl(ctx, ARM_SMMU_CB_FSYNR0); + iova = iommu_readq(ctx, ARM_SMMU_CB_FAR); + + dev_err_ratelimited(ctx->dev, + "Unhandled context fault: fsr=0x%x, " + "iova=0x%016llx, fsynr=0x%x, cb=%d\n", + fsr, iova, fsynr, ctx->asid); + + iommu_writel(ctx, ARM_SMMU_CB_FSR, fsr); + + return IRQ_HANDLED; +} + +static int qcom_iommu_init_domain(struct iommu_domain *domain, + struct qcom_iommu_dev *qcom_iommu, + struct iommu_fwspec *fwspec) +{ + struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); + struct io_pgtable_ops *pgtbl_ops; + struct io_pgtable_cfg pgtbl_cfg; + int i, ret = 0; + u32 reg; + + mutex_lock(&qcom_domain->init_mutex); + if (qcom_domain->iommu) + goto out_unlock; + + pgtbl_cfg = (struct io_pgtable_cfg) { + .pgsize_bitmap = qcom_iommu_ops.pgsize_bitmap, + .ias = 32, + .oas = 40, + .tlb = &qcom_gather_ops, + .iommu_dev = qcom_iommu->dev, + }; + + qcom_domain->iommu = qcom_iommu; + pgtbl_ops = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &pgtbl_cfg, fwspec); + if (!pgtbl_ops) { + dev_err(qcom_iommu->dev, "failed to allocate pagetable ops\n"); + ret = -ENOMEM; + goto out_clear_iommu; + } + + /* Update the domain's page sizes to reflect the page table format */ + domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; + domain->geometry.aperture_end = (1ULL << pgtbl_cfg.ias) - 1; + domain->geometry.force_aperture = true; + + for (i = 0; i < fwspec->num_ids; i++) { + struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]); + + if (!ctx->secure_init) { + ret = qcom_scm_restore_sec_cfg(qcom_iommu->sec_id, ctx->asid); + if (ret) { + dev_err(qcom_iommu->dev, "secure init failed: %d\n", ret); + goto out_clear_iommu; + } + ctx->secure_init = true; + } + + /* TTBRs */ + iommu_writeq(ctx, ARM_SMMU_CB_TTBR0, + pgtbl_cfg.arm_lpae_s1_cfg.ttbr[0] | + ((u64)ctx->asid << TTBRn_ASID_SHIFT)); + iommu_writeq(ctx, ARM_SMMU_CB_TTBR1, + pgtbl_cfg.arm_lpae_s1_cfg.ttbr[1] | + ((u64)ctx->asid << TTBRn_ASID_SHIFT)); + + /* TTBCR */ + iommu_writel(ctx, ARM_SMMU_CB_TTBCR2, + (pgtbl_cfg.arm_lpae_s1_cfg.tcr >> 32) | + TTBCR2_SEP_UPSTREAM); + iommu_writel(ctx, ARM_SMMU_CB_TTBCR, + pgtbl_cfg.arm_lpae_s1_cfg.tcr); + + /* MAIRs (stage-1 only) */ + iommu_writel(ctx, ARM_SMMU_CB_S1_MAIR0, + pgtbl_cfg.arm_lpae_s1_cfg.mair[0]); + iommu_writel(ctx, ARM_SMMU_CB_S1_MAIR1, + pgtbl_cfg.arm_lpae_s1_cfg.mair[1]); + + /* SCTLR */ + reg = SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE | + SCTLR_M | SCTLR_S1_ASIDPNE; + + if (IS_ENABLED(CONFIG_BIG_ENDIAN)) + reg |= SCTLR_E; + + iommu_writel(ctx, ARM_SMMU_CB_SCTLR, reg); + } + + mutex_unlock(&qcom_domain->init_mutex); + + /* Publish page table ops for map/unmap */ + qcom_domain->pgtbl_ops = pgtbl_ops; + + return 0; + +out_clear_iommu: + qcom_domain->iommu = NULL; +out_unlock: + mutex_unlock(&qcom_domain->init_mutex); + return ret; +} + +static struct iommu_domain *qcom_iommu_domain_alloc(unsigned type) +{ + struct qcom_iommu_domain *qcom_domain; + + if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA) + return NULL; + /* + * Allocate the domain and initialise some of its data structures. + * We can't really do anything meaningful until we've added a + * master. + */ + qcom_domain = kzalloc(sizeof(*qcom_domain), GFP_KERNEL); + if (!qcom_domain) + return NULL; + + if (type == IOMMU_DOMAIN_DMA && + iommu_get_dma_cookie(&qcom_domain->domain)) { + kfree(qcom_domain); + return NULL; + } + + mutex_init(&qcom_domain->init_mutex); + spin_lock_init(&qcom_domain->pgtbl_lock); + + return &qcom_domain->domain; +} + +static void qcom_iommu_domain_free(struct iommu_domain *domain) +{ + struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); + + if (WARN_ON(qcom_domain->iommu)) /* forgot to detach? */ + return; + + iommu_put_dma_cookie(domain); + + /* NOTE: unmap can be called after client device is powered off, + * for example, with GPUs or anything involving dma-buf. So we + * cannot rely on the device_link. Make sure the IOMMU is on to + * avoid unclocked accesses in the TLB inv path: + */ + pm_runtime_get_sync(qcom_domain->iommu->dev); + + free_io_pgtable_ops(qcom_domain->pgtbl_ops); + + pm_runtime_put_sync(qcom_domain->iommu->dev); + + kfree(qcom_domain); +} + +static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) +{ + struct qcom_iommu_dev *qcom_iommu = to_iommu(dev->iommu_fwspec); + struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); + int ret; + + if (!qcom_iommu) { + dev_err(dev, "cannot attach to IOMMU, is it on the same bus?\n"); + return -ENXIO; + } + + /* Ensure that the domain is finalized */ + pm_runtime_get_sync(qcom_iommu->dev); + ret = qcom_iommu_init_domain(domain, qcom_iommu, dev->iommu_fwspec); + pm_runtime_put_sync(qcom_iommu->dev); + if (ret < 0) + return ret; + + /* + * Sanity check the domain. We don't support domains across + * different IOMMUs. + */ + if (qcom_domain->iommu != qcom_iommu) { + dev_err(dev, "cannot attach to IOMMU %s while already " + "attached to domain on IOMMU %s\n", + dev_name(qcom_domain->iommu->dev), + dev_name(qcom_iommu->dev)); + return -EINVAL; + } + + return 0; +} + +static void qcom_iommu_detach_dev(struct iommu_domain *domain, struct device *dev) +{ + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + struct qcom_iommu_dev *qcom_iommu = to_iommu(fwspec); + struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); + unsigned i; + + if (!qcom_domain->iommu) + return; + + pm_runtime_get_sync(qcom_iommu->dev); + for (i = 0; i < fwspec->num_ids; i++) { + struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]); + + /* Disable the context bank: */ + iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0); + } + pm_runtime_put_sync(qcom_iommu->dev); + + qcom_domain->iommu = NULL; +} + +static int qcom_iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot) +{ + int ret; + unsigned long flags; + struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); + struct io_pgtable_ops *ops = qcom_domain->pgtbl_ops; + + if (!ops) + return -ENODEV; + + spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags); + ret = ops->map(ops, iova, paddr, size, prot); + spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags); + return ret; +} + +static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long iova, + size_t size) +{ + size_t ret; + unsigned long flags; + struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); + struct io_pgtable_ops *ops = qcom_domain->pgtbl_ops; + + if (!ops) + return 0; + + /* NOTE: unmap can be called after client device is powered off, + * for example, with GPUs or anything involving dma-buf. So we + * cannot rely on the device_link. Make sure the IOMMU is on to + * avoid unclocked accesses in the TLB inv path: + */ + pm_runtime_get_sync(qcom_domain->iommu->dev); + spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags); + ret = ops->unmap(ops, iova, size); + spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags); + pm_runtime_put_sync(qcom_domain->iommu->dev); + + return ret; +} + +static phys_addr_t qcom_iommu_iova_to_phys(struct iommu_domain *domain, + dma_addr_t iova) +{ + phys_addr_t ret; + unsigned long flags; + struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); + struct io_pgtable_ops *ops = qcom_domain->pgtbl_ops; + + if (!ops) + return 0; + + spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags); + ret = ops->iova_to_phys(ops, iova); + spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags); + + return ret; +} + +static bool qcom_iommu_capable(enum iommu_cap cap) +{ + switch (cap) { + case IOMMU_CAP_CACHE_COHERENCY: + /* + * Return true here as the SMMU can always send out coherent + * requests. + */ + return true; + case IOMMU_CAP_NOEXEC: + return true; + default: + return false; + } +} + +static int qcom_iommu_add_device(struct device *dev) +{ + struct qcom_iommu_dev *qcom_iommu = to_iommu(dev->iommu_fwspec); + struct iommu_group *group; + struct device_link *link; + + if (!qcom_iommu) + return -ENODEV; + + /* + * Establish the link between iommu and master, so that the + * iommu gets runtime enabled/disabled as per the master's + * needs. + */ + link = device_link_add(dev, qcom_iommu->dev, DL_FLAG_PM_RUNTIME); + if (!link) { + dev_err(qcom_iommu->dev, "Unable to create device link between %s and %s\n", + dev_name(qcom_iommu->dev), dev_name(dev)); + return -ENODEV; + } + + group = iommu_group_get_for_dev(dev); + if (IS_ERR_OR_NULL(group)) + return PTR_ERR_OR_ZERO(group); + + iommu_group_put(group); + iommu_device_link(&qcom_iommu->iommu, dev); + + return 0; +} + +static void qcom_iommu_remove_device(struct device *dev) +{ + struct qcom_iommu_dev *qcom_iommu = to_iommu(dev->iommu_fwspec); + + if (!qcom_iommu) + return; + + iommu_device_unlink(&qcom_iommu->iommu, dev); + iommu_group_remove_device(dev); + iommu_fwspec_free(dev); +} + +static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args) +{ + struct qcom_iommu_dev *qcom_iommu; + struct platform_device *iommu_pdev; + unsigned asid = args->args[0]; + + if (args->args_count != 1) { + dev_err(dev, "incorrect number of iommu params found for %s " + "(found %d, expected 1)\n", + args->np->full_name, args->args_count); + return -EINVAL; + } + + iommu_pdev = of_find_device_by_node(args->np); + if (WARN_ON(!iommu_pdev)) + return -EINVAL; + + qcom_iommu = platform_get_drvdata(iommu_pdev); + + /* make sure the asid specified in dt is valid, so we don't have + * to sanity check this elsewhere, since 'asid - 1' is used to + * index into qcom_iommu->ctxs: + */ + if (WARN_ON(asid < 1) || + WARN_ON(asid > qcom_iommu->num_ctxs)) + return -EINVAL; + + if (!dev->iommu_fwspec->iommu_priv) { + dev->iommu_fwspec->iommu_priv = qcom_iommu; + } else { + /* make sure devices iommus dt node isn't referring to + * multiple different iommu devices. Multiple context + * banks are ok, but multiple devices are not: + */ + if (WARN_ON(qcom_iommu != dev->iommu_fwspec->iommu_priv)) + return -EINVAL; + } + + return iommu_fwspec_add_ids(dev, &asid, 1); +} + +static const struct iommu_ops qcom_iommu_ops = { + .capable = qcom_iommu_capable, + .domain_alloc = qcom_iommu_domain_alloc, + .domain_free = qcom_iommu_domain_free, + .attach_dev = qcom_iommu_attach_dev, + .detach_dev = qcom_iommu_detach_dev, + .map = qcom_iommu_map, + .unmap = qcom_iommu_unmap, + .map_sg = default_iommu_map_sg, + .iova_to_phys = qcom_iommu_iova_to_phys, + .add_device = qcom_iommu_add_device, + .remove_device = qcom_iommu_remove_device, + .device_group = generic_device_group, + .of_xlate = qcom_iommu_of_xlate, + .pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M, +}; + +static int qcom_iommu_enable_clocks(struct qcom_iommu_dev *qcom_iommu) +{ + int ret; + + ret = clk_prepare_enable(qcom_iommu->iface_clk); + if (ret) { + dev_err(qcom_iommu->dev, "Couldn't enable iface_clk\n"); + return ret; + } + + ret = clk_prepare_enable(qcom_iommu->bus_clk); + if (ret) { + dev_err(qcom_iommu->dev, "Couldn't enable bus_clk\n"); + clk_disable_unprepare(qcom_iommu->iface_clk); + return ret; + } + + return 0; +} + +static void qcom_iommu_disable_clocks(struct qcom_iommu_dev *qcom_iommu) +{ + clk_disable_unprepare(qcom_iommu->bus_clk); + clk_disable_unprepare(qcom_iommu->iface_clk); +} + +static int qcom_iommu_sec_ptbl_init(struct device *dev) +{ + size_t psize = 0; + unsigned int spare = 0; + void *cpu_addr; + dma_addr_t paddr; + unsigned long attrs; + static bool allocated = false; + int ret; + + if (allocated) + return 0; + + ret = qcom_scm_iommu_secure_ptbl_size(spare, &psize); + if (ret) { + dev_err(dev, "failed to get iommu secure pgtable size (%d)\n", + ret); + return ret; + } + + dev_info(dev, "iommu sec: pgtable size: %zu\n", psize); + + attrs = DMA_ATTR_NO_KERNEL_MAPPING; + + cpu_addr = dma_alloc_attrs(dev, psize, &paddr, GFP_KERNEL, attrs); + if (!cpu_addr) { + dev_err(dev, "failed to allocate %zu bytes for pgtable\n", + psize); + return -ENOMEM; + } + + ret = qcom_scm_iommu_secure_ptbl_init(paddr, psize, spare); + if (ret) { + dev_err(dev, "failed to init iommu pgtable (%d)\n", ret); + goto free_mem; + } + + allocated = true; + return 0; + +free_mem: + dma_free_attrs(dev, psize, cpu_addr, paddr, attrs); + return ret; +} + +static int get_asid(const struct device_node *np) +{ + u32 reg; + + /* read the "reg" property directly to get the relative address + * of the context bank, and calculate the asid from that: + */ + if (of_property_read_u32_index(np, "reg", 0, ®)) + return -ENODEV; + + return reg / 0x1000; /* context banks are 0x1000 apart */ +} + +static int qcom_iommu_ctx_probe(struct platform_device *pdev) +{ + struct qcom_iommu_ctx *ctx; + struct device *dev = &pdev->dev; + struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(dev->parent); + struct resource *res; + int ret, irq; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = dev; + platform_set_drvdata(pdev, ctx); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctx->base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctx->base)) + return PTR_ERR(ctx->base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "failed to get irq\n"); + return -ENODEV; + } + + /* clear IRQs before registering fault handler, just in case the + * boot-loader left us a surprise: + */ + iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR)); + + ret = devm_request_irq(dev, irq, + qcom_iommu_fault, + IRQF_SHARED, + "qcom-iommu-fault", + ctx); + if (ret) { + dev_err(dev, "failed to request IRQ %u\n", irq); + return ret; + } + + ret = get_asid(dev->of_node); + if (ret < 0) { + dev_err(dev, "missing reg property\n"); + return ret; + } + + ctx->asid = ret; + + dev_dbg(dev, "found asid %u\n", ctx->asid); + + qcom_iommu->ctxs[ctx->asid - 1] = ctx; + + return 0; +} + +static int qcom_iommu_ctx_remove(struct platform_device *pdev) +{ + struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(pdev->dev.parent); + struct qcom_iommu_ctx *ctx = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + qcom_iommu->ctxs[ctx->asid - 1] = NULL; + + return 0; +} + +static const struct of_device_id ctx_of_match[] = { + { .compatible = "qcom,msm-iommu-v1-ns" }, + { .compatible = "qcom,msm-iommu-v1-sec" }, + { /* sentinel */ } +}; + +static struct platform_driver qcom_iommu_ctx_driver = { + .driver = { + .name = "qcom-iommu-ctx", + .of_match_table = of_match_ptr(ctx_of_match), + }, + .probe = qcom_iommu_ctx_probe, + .remove = qcom_iommu_ctx_remove, +}; + +static bool qcom_iommu_has_secure_context(struct qcom_iommu_dev *qcom_iommu) +{ + struct device_node *child; + + for_each_child_of_node(qcom_iommu->dev->of_node, child) + if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec")) + return true; + + return false; +} + +static int qcom_iommu_device_probe(struct platform_device *pdev) +{ + struct device_node *child; + struct qcom_iommu_dev *qcom_iommu; + struct device *dev = &pdev->dev; + struct resource *res; + int ret, sz, max_asid = 0; + + /* find the max asid (which is 1:1 to ctx bank idx), so we know how + * many child ctx devices we have: + */ + for_each_child_of_node(dev->of_node, child) + max_asid = max(max_asid, get_asid(child)); + + sz = sizeof(*qcom_iommu) + (max_asid * sizeof(qcom_iommu->ctxs[0])); + + qcom_iommu = devm_kzalloc(dev, sz, GFP_KERNEL); + if (!qcom_iommu) + return -ENOMEM; + qcom_iommu->num_ctxs = max_asid; + qcom_iommu->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + qcom_iommu->local_base = devm_ioremap_resource(dev, res); + + qcom_iommu->iface_clk = devm_clk_get(dev, "iface"); + if (IS_ERR(qcom_iommu->iface_clk)) { + dev_err(dev, "failed to get iface clock\n"); + return PTR_ERR(qcom_iommu->iface_clk); + } + + qcom_iommu->bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(qcom_iommu->bus_clk)) { + dev_err(dev, "failed to get bus clock\n"); + return PTR_ERR(qcom_iommu->bus_clk); + } + + if (of_property_read_u32(dev->of_node, "qcom,iommu-secure-id", + &qcom_iommu->sec_id)) { + dev_err(dev, "missing qcom,iommu-secure-id property\n"); + return -ENODEV; + } + + if (qcom_iommu_has_secure_context(qcom_iommu)) { + ret = qcom_iommu_sec_ptbl_init(dev); + if (ret) { + dev_err(dev, "cannot init secure pg table(%d)\n", ret); + return ret; + } + } + + platform_set_drvdata(pdev, qcom_iommu); + + pm_runtime_enable(dev); + + /* register context bank devices, which are child nodes: */ + ret = devm_of_platform_populate(dev); + if (ret) { + dev_err(dev, "Failed to populate iommu contexts\n"); + return ret; + } + + ret = iommu_device_sysfs_add(&qcom_iommu->iommu, dev, NULL, + dev_name(dev)); + if (ret) { + dev_err(dev, "Failed to register iommu in sysfs\n"); + return ret; + } + + iommu_device_set_ops(&qcom_iommu->iommu, &qcom_iommu_ops); + iommu_device_set_fwnode(&qcom_iommu->iommu, dev->fwnode); + + ret = iommu_device_register(&qcom_iommu->iommu); + if (ret) { + dev_err(dev, "Failed to register iommu\n"); + return ret; + } + + bus_set_iommu(&platform_bus_type, &qcom_iommu_ops); + + if (qcom_iommu->local_base) { + pm_runtime_get_sync(dev); + writel_relaxed(0xffffffff, qcom_iommu->local_base + SMMU_INTR_SEL_NS); + pm_runtime_put_sync(dev); + } + + return 0; +} + +static int qcom_iommu_device_remove(struct platform_device *pdev) +{ + struct qcom_iommu_dev *qcom_iommu = platform_get_drvdata(pdev); + + bus_set_iommu(&platform_bus_type, NULL); + + pm_runtime_force_suspend(&pdev->dev); + platform_set_drvdata(pdev, NULL); + iommu_device_sysfs_remove(&qcom_iommu->iommu); + iommu_device_unregister(&qcom_iommu->iommu); + + return 0; +} + +static int __maybe_unused qcom_iommu_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct qcom_iommu_dev *qcom_iommu = platform_get_drvdata(pdev); + + return qcom_iommu_enable_clocks(qcom_iommu); +} + +static int __maybe_unused qcom_iommu_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct qcom_iommu_dev *qcom_iommu = platform_get_drvdata(pdev); + + qcom_iommu_disable_clocks(qcom_iommu); + + return 0; +} + +static const struct dev_pm_ops qcom_iommu_pm_ops = { + SET_RUNTIME_PM_OPS(qcom_iommu_suspend, qcom_iommu_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static const struct of_device_id qcom_iommu_of_match[] = { + { .compatible = "qcom,msm-iommu-v1" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, qcom_iommu_of_match); + +static struct platform_driver qcom_iommu_driver = { + .driver = { + .name = "qcom-iommu", + .of_match_table = of_match_ptr(qcom_iommu_of_match), + .pm = &qcom_iommu_pm_ops, + }, + .probe = qcom_iommu_device_probe, + .remove = qcom_iommu_device_remove, +}; + +static int __init qcom_iommu_init(void) +{ + int ret; + + ret = platform_driver_register(&qcom_iommu_ctx_driver); + if (ret) + return ret; + + ret = platform_driver_register(&qcom_iommu_driver); + if (ret) + platform_driver_unregister(&qcom_iommu_ctx_driver); + + return ret; +} + +static void __exit qcom_iommu_exit(void) +{ + platform_driver_unregister(&qcom_iommu_driver); + platform_driver_unregister(&qcom_iommu_ctx_driver); +} + +module_init(qcom_iommu_init); +module_exit(qcom_iommu_exit); + +IOMMU_OF_DECLARE(qcom_iommu_dev, "qcom,msm-iommu-v1", NULL); + +MODULE_DESCRIPTION("IOMMU API for QCOM IOMMU v1 implementations"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index 4ba48a26b389..9d991c2d8767 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -90,7 +90,9 @@ struct rk_iommu { struct device *dev; void __iomem **bases; int num_mmu; - int irq; + int *irq; + int num_irq; + bool reset_disabled; struct iommu_device iommu; struct list_head node; /* entry in rk_iommu_domain.iommus */ struct iommu_domain *domain; /* domain to which iommu is attached */ @@ -414,6 +416,9 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu) int ret, i; u32 dte_addr; + if (iommu->reset_disabled) + return 0; + /* * Check if register DTE_ADDR is working by writing DTE_ADDR_DUMMY * and verifying that upper 5 nybbles are read back. @@ -825,10 +830,12 @@ static int rk_iommu_attach_device(struct iommu_domain *domain, iommu->domain = domain; - ret = devm_request_irq(iommu->dev, iommu->irq, rk_iommu_irq, - IRQF_SHARED, dev_name(dev), iommu); - if (ret) - return ret; + for (i = 0; i < iommu->num_irq; i++) { + ret = devm_request_irq(iommu->dev, iommu->irq[i], rk_iommu_irq, + IRQF_SHARED, dev_name(dev), iommu); + if (ret) + return ret; + } for (i = 0; i < iommu->num_mmu; i++) { rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, @@ -878,7 +885,8 @@ static void rk_iommu_detach_device(struct iommu_domain *domain, } rk_iommu_disable_stall(iommu); - devm_free_irq(iommu->dev, iommu->irq, iommu); + for (i = 0; i < iommu->num_irq; i++) + devm_free_irq(iommu->dev, iommu->irq[i], iommu); iommu->domain = NULL; @@ -1008,20 +1016,20 @@ static int rk_iommu_group_set_iommudata(struct iommu_group *group, ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0, &args); if (ret) { - dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n", - np->full_name, ret); + dev_err(dev, "of_parse_phandle_with_args(%pOF) => %d\n", + np, ret); return ret; } if (args.args_count != 0) { - dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n", - args.np->full_name, args.args_count); + dev_err(dev, "incorrect number of iommu params found for %pOF (found %d, expected 0)\n", + args.np, args.args_count); return -EINVAL; } pd = of_find_device_by_node(args.np); of_node_put(args.np); if (!pd) { - dev_err(dev, "iommu %s not found\n", args.np->full_name); + dev_err(dev, "iommu %pOF not found\n", args.np); return -EPROBE_DEFER; } @@ -1157,12 +1165,28 @@ static int rk_iommu_probe(struct platform_device *pdev) if (iommu->num_mmu == 0) return PTR_ERR(iommu->bases[0]); - iommu->irq = platform_get_irq(pdev, 0); - if (iommu->irq < 0) { - dev_err(dev, "Failed to get IRQ, %d\n", iommu->irq); + iommu->num_irq = platform_irq_count(pdev); + if (iommu->num_irq < 0) + return iommu->num_irq; + if (iommu->num_irq == 0) return -ENXIO; + + iommu->irq = devm_kcalloc(dev, iommu->num_irq, sizeof(*iommu->irq), + GFP_KERNEL); + if (!iommu->irq) + return -ENOMEM; + + for (i = 0; i < iommu->num_irq; i++) { + iommu->irq[i] = platform_get_irq(pdev, i); + if (iommu->irq[i] < 0) { + dev_err(dev, "Failed to get IRQ, %d\n", iommu->irq[i]); + return -ENXIO; + } } + iommu->reset_disabled = device_property_read_bool(dev, + "rockchip,disable-mmu-reset"); + err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, dev_name(dev)); if (err) return err; diff --git a/drivers/iommu/s390-iommu.c b/drivers/iommu/s390-iommu.c index 8788640756a7..0e2f31f9032b 100644 --- a/drivers/iommu/s390-iommu.c +++ b/drivers/iommu/s390-iommu.c @@ -18,6 +18,8 @@ */ #define S390_IOMMU_PGSIZES (~0xFFFUL) +static const struct iommu_ops s390_iommu_ops; + struct s390_domain { struct iommu_domain domain; struct list_head devices; @@ -166,11 +168,13 @@ static void s390_iommu_detach_device(struct iommu_domain *domain, static int s390_iommu_add_device(struct device *dev) { struct iommu_group *group = iommu_group_get_for_dev(dev); + struct zpci_dev *zdev = to_pci_dev(dev)->sysdata; if (IS_ERR(group)) return PTR_ERR(group); iommu_group_put(group); + iommu_device_link(&zdev->iommu_dev, dev); return 0; } @@ -197,6 +201,7 @@ static void s390_iommu_remove_device(struct device *dev) s390_iommu_detach_device(domain, dev); } + iommu_device_unlink(&zdev->iommu_dev, dev); iommu_group_remove_device(dev); } @@ -327,7 +332,37 @@ static size_t s390_iommu_unmap(struct iommu_domain *domain, return size; } -static struct iommu_ops s390_iommu_ops = { +int zpci_init_iommu(struct zpci_dev *zdev) +{ + int rc = 0; + + rc = iommu_device_sysfs_add(&zdev->iommu_dev, NULL, NULL, + "s390-iommu.%08x", zdev->fid); + if (rc) + goto out_err; + + iommu_device_set_ops(&zdev->iommu_dev, &s390_iommu_ops); + + rc = iommu_device_register(&zdev->iommu_dev); + if (rc) + goto out_sysfs; + + return 0; + +out_sysfs: + iommu_device_sysfs_remove(&zdev->iommu_dev); + +out_err: + return rc; +} + +void zpci_destroy_iommu(struct zpci_dev *zdev) +{ + iommu_device_unregister(&zdev->iommu_dev); + iommu_device_sysfs_remove(&zdev->iommu_dev); +} + +static const struct iommu_ops s390_iommu_ops = { .capable = s390_iommu_capable, .domain_alloc = s390_domain_alloc, .domain_free = s390_domain_free, diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 37e708fdbb5a..b62f790ad1ba 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -61,6 +61,8 @@ struct gart_device { struct list_head client; spinlock_t client_lock; /* for client list */ struct device *dev; + + struct iommu_device iommu; /* IOMMU Core handle */ }; struct gart_domain { @@ -334,12 +336,35 @@ static bool gart_iommu_capable(enum iommu_cap cap) return false; } +static int gart_iommu_add_device(struct device *dev) +{ + struct iommu_group *group = iommu_group_get_for_dev(dev); + + if (IS_ERR(group)) + return PTR_ERR(group); + + iommu_group_put(group); + + iommu_device_link(&gart_handle->iommu, dev); + + return 0; +} + +static void gart_iommu_remove_device(struct device *dev) +{ + iommu_group_remove_device(dev); + iommu_device_unlink(&gart_handle->iommu, dev); +} + static const struct iommu_ops gart_iommu_ops = { .capable = gart_iommu_capable, .domain_alloc = gart_iommu_domain_alloc, .domain_free = gart_iommu_domain_free, .attach_dev = gart_iommu_attach_dev, .detach_dev = gart_iommu_detach_dev, + .add_device = gart_iommu_add_device, + .remove_device = gart_iommu_remove_device, + .device_group = generic_device_group, .map = gart_iommu_map, .map_sg = default_iommu_map_sg, .unmap = gart_iommu_unmap, @@ -378,6 +403,7 @@ static int tegra_gart_probe(struct platform_device *pdev) struct resource *res, *res_remap; void __iomem *gart_regs; struct device *dev = &pdev->dev; + int ret; if (gart_handle) return -EIO; @@ -404,6 +430,22 @@ static int tegra_gart_probe(struct platform_device *pdev) return -ENXIO; } + ret = iommu_device_sysfs_add(&gart->iommu, &pdev->dev, NULL, + dev_name(&pdev->dev)); + if (ret) { + dev_err(dev, "Failed to register IOMMU in sysfs\n"); + return ret; + } + + iommu_device_set_ops(&gart->iommu, &gart_iommu_ops); + + ret = iommu_device_register(&gart->iommu); + if (ret) { + dev_err(dev, "Failed to register IOMMU\n"); + iommu_device_sysfs_remove(&gart->iommu); + return ret; + } + gart->dev = &pdev->dev; spin_lock_init(&gart->pte_lock); spin_lock_init(&gart->client_lock); @@ -430,6 +472,9 @@ static int tegra_gart_remove(struct platform_device *pdev) { struct gart_device *gart = platform_get_drvdata(pdev); + iommu_device_unregister(&gart->iommu); + iommu_device_sysfs_remove(&gart->iommu); + writel(0, gart->regs + GART_CONFIG); if (gart->savedata) vfree(gart->savedata); diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index eeb19f560a05..3b6449e2cbf1 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -36,6 +36,8 @@ struct tegra_smmu { struct list_head list; struct dentry *debugfs; + + struct iommu_device iommu; /* IOMMU Core code handle */ }; struct tegra_smmu_as { @@ -704,6 +706,7 @@ static struct tegra_smmu *tegra_smmu_find(struct device_node *np) static int tegra_smmu_add_device(struct device *dev) { struct device_node *np = dev->of_node; + struct iommu_group *group; struct of_phandle_args args; unsigned int index = 0; @@ -719,18 +722,33 @@ static int tegra_smmu_add_device(struct device *dev) * first match. */ dev->archdata.iommu = smmu; + + iommu_device_link(&smmu->iommu, dev); + break; } index++; } + group = iommu_group_get_for_dev(dev); + if (IS_ERR(group)) + return PTR_ERR(group); + + iommu_group_put(group); + return 0; } static void tegra_smmu_remove_device(struct device *dev) { + struct tegra_smmu *smmu = dev->archdata.iommu; + + if (smmu) + iommu_device_unlink(&smmu->iommu, dev); + dev->archdata.iommu = NULL; + iommu_group_remove_device(dev); } static const struct iommu_ops tegra_smmu_ops = { @@ -741,6 +759,7 @@ static const struct iommu_ops tegra_smmu_ops = { .detach_dev = tegra_smmu_detach_dev, .add_device = tegra_smmu_add_device, .remove_device = tegra_smmu_remove_device, + .device_group = generic_device_group, .map = tegra_smmu_map, .unmap = tegra_smmu_unmap, .map_sg = default_iommu_map_sg, @@ -930,9 +949,24 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, tegra_smmu_ahb_enable(); + err = iommu_device_sysfs_add(&smmu->iommu, dev, NULL, dev_name(dev)); + if (err) + return ERR_PTR(err); + + iommu_device_set_ops(&smmu->iommu, &tegra_smmu_ops); + + err = iommu_device_register(&smmu->iommu); + if (err) { + iommu_device_sysfs_remove(&smmu->iommu); + return ERR_PTR(err); + } + err = bus_set_iommu(&platform_bus_type, &tegra_smmu_ops); - if (err < 0) + if (err < 0) { + iommu_device_unregister(&smmu->iommu); + iommu_device_sysfs_remove(&smmu->iommu); return ERR_PTR(err); + } if (IS_ENABLED(CONFIG_DEBUG_FS)) tegra_smmu_debugfs_init(smmu); @@ -942,6 +976,9 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, void tegra_smmu_remove(struct tegra_smmu *smmu) { + iommu_device_unregister(&smmu->iommu); + iommu_device_sysfs_remove(&smmu->iommu); + if (IS_ENABLED(CONFIG_DEBUG_FS)) tegra_smmu_debugfs_exit(smmu); } diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c index 6ffd13466b8c..e97232646ba1 100644 --- a/drivers/isdn/isdnloop/isdnloop.c +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -409,7 +409,7 @@ isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card *card) return -EINVAL; } if (len) { - if (!(card->flags & (channel) ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE)) + if (!(card->flags & (channel ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE))) return 0; if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE) return 0; diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index 0f9ed1ea0e89..492789f56896 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -40,7 +40,7 @@ static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id) * detected as working, but in reality it is not) as low as * possible. */ -static struct dmi_system_id clevo_mail_led_dmi_table[] __initdata = { +static const struct dmi_system_id clevo_mail_led_dmi_table[] __initconst = { { .callback = clevo_mail_led_dmi_callback, .ident = "Clevo D410J", diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index 732eb86bc1a5..a9db8674cd02 100644 --- a/drivers/leds/leds-ss4200.c +++ b/drivers/leds/leds-ss4200.c @@ -91,7 +91,7 @@ MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection"); * detected as working, but in reality it is not) as low as * possible. */ -static struct dmi_system_id nas_led_whitelist[] __initdata = { +static const struct dmi_system_id nas_led_whitelist[] __initconst = { { .callback = ss4200_led_dmi_callback, .ident = "Intel SS4200-E", diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c index ca4abe1ccd8d..cacbe2dbd5c3 100644 --- a/drivers/md/bcache/alloc.c +++ b/drivers/md/bcache/alloc.c @@ -68,6 +68,8 @@ #include <linux/random.h> #include <trace/events/bcache.h> +#define MAX_OPEN_BUCKETS 128 + /* Bucket heap / gen */ uint8_t bch_inc_gen(struct cache *ca, struct bucket *b) @@ -671,7 +673,7 @@ int bch_open_buckets_alloc(struct cache_set *c) spin_lock_init(&c->data_bucket_lock); - for (i = 0; i < 6; i++) { + for (i = 0; i < MAX_OPEN_BUCKETS; i++) { struct open_bucket *b = kzalloc(sizeof(*b), GFP_KERNEL); if (!b) return -ENOMEM; diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index dee542fff68e..2ed9bd231d84 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h @@ -333,6 +333,7 @@ struct cached_dev { /* Limit number of writeback bios in flight */ struct semaphore in_flight; struct task_struct *writeback_thread; + struct workqueue_struct *writeback_write_wq; struct keybuf writeback_keys; diff --git a/drivers/md/bcache/closure.c b/drivers/md/bcache/closure.c index 864e673aec39..7d5286b05036 100644 --- a/drivers/md/bcache/closure.c +++ b/drivers/md/bcache/closure.c @@ -70,21 +70,10 @@ void __closure_wake_up(struct closure_waitlist *wait_list) list = llist_del_all(&wait_list->list); /* We first reverse the list to preserve FIFO ordering and fairness */ - - while (list) { - struct llist_node *t = list; - list = llist_next(list); - - t->next = reverse; - reverse = t; - } + reverse = llist_reverse_order(list); /* Then do the wakeups */ - - while (reverse) { - cl = container_of(reverse, struct closure, list); - reverse = llist_next(reverse); - + llist_for_each_entry(cl, reverse, list) { closure_set_waiting(cl, 0); closure_sub(cl, CLOSURE_WAITING + 1); } diff --git a/drivers/md/bcache/closure.h b/drivers/md/bcache/closure.h index 1ec84ca81146..295b7e43f92c 100644 --- a/drivers/md/bcache/closure.h +++ b/drivers/md/bcache/closure.h @@ -312,8 +312,6 @@ static inline void closure_wake_up(struct closure_waitlist *list) * been dropped with closure_put()), it will resume execution at @fn running out * of @wq (or, if @wq is NULL, @fn will be called by closure_put() directly). * - * NOTE: This macro expands to a return in the calling function! - * * This is because after calling continue_at() you no longer have a ref on @cl, * and whatever @cl owns may be freed out from under you - a running closure fn * has a ref on its own closure which continue_at() drops. @@ -340,8 +338,6 @@ do { \ * Causes @fn to be executed out of @cl, in @wq context (or called directly if * @wq is NULL). * - * NOTE: like continue_at(), this macro expands to a return in the caller! - * * The ref the caller of continue_at_nobarrier() had on @cl is now owned by @fn, * thus it's not safe to touch anything protected by @cl after a * continue_at_nobarrier(). diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index 0e1463d0c334..681b4f12b05a 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -196,12 +196,12 @@ static void bch_data_insert_start(struct closure *cl) struct data_insert_op *op = container_of(cl, struct data_insert_op, cl); struct bio *bio = op->bio, *n; - if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) - wake_up_gc(op->c); - if (op->bypass) return bch_data_invalidate(cl); + if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) + wake_up_gc(op->c); + /* * Journal writes are marked REQ_PREFLUSH; if the original write was a * flush, it'll wait on the journal write. @@ -400,12 +400,6 @@ static bool check_should_bypass(struct cached_dev *dc, struct bio *bio) if (!congested && !dc->sequential_cutoff) goto rescale; - if (!congested && - mode == CACHE_MODE_WRITEBACK && - op_is_write(bio->bi_opf) && - op_is_sync(bio->bi_opf)) - goto rescale; - spin_lock(&dc->io_lock); hlist_for_each_entry(i, iohash(dc, bio->bi_iter.bi_sector), hash) diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 974d832e54a6..fc0a31b13ac4 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1026,7 +1026,7 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c) } if (BDEV_STATE(&dc->sb) == BDEV_STATE_DIRTY) { - bch_sectors_dirty_init(dc); + bch_sectors_dirty_init(&dc->disk); atomic_set(&dc->has_dirty, 1); atomic_inc(&dc->count); bch_writeback_queue(dc); @@ -1059,6 +1059,8 @@ static void cached_dev_free(struct closure *cl) cancel_delayed_work_sync(&dc->writeback_rate_update); if (!IS_ERR_OR_NULL(dc->writeback_thread)) kthread_stop(dc->writeback_thread); + if (dc->writeback_write_wq) + destroy_workqueue(dc->writeback_write_wq); mutex_lock(&bch_register_lock); @@ -1228,6 +1230,7 @@ static int flash_dev_run(struct cache_set *c, struct uuid_entry *u) goto err; bcache_device_attach(d, c, u - c->uuids); + bch_sectors_dirty_init(d); bch_flash_dev_request_init(d); add_disk(d->disk); @@ -1374,9 +1377,6 @@ static void cache_set_flush(struct closure *cl) struct btree *b; unsigned i; - if (!c) - closure_return(cl); - bch_cache_accounting_destroy(&c->accounting); kobject_put(&c->internal); @@ -1964,6 +1964,8 @@ static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr, else err = "device busy"; mutex_unlock(&bch_register_lock); + if (!IS_ERR(bdev)) + bdput(bdev); if (attr == &ksysfs_register_quiet) goto out; } diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index f90f13616980..104c57cd666c 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -192,7 +192,7 @@ STORE(__cached_dev) { struct cached_dev *dc = container_of(kobj, struct cached_dev, disk.kobj); - unsigned v = size; + ssize_t v = size; struct cache_set *c; struct kobj_uevent_env *env; @@ -227,7 +227,7 @@ STORE(__cached_dev) bch_cached_dev_run(dc); if (attr == &sysfs_cache_mode) { - ssize_t v = bch_read_string_list(buf, bch_cache_modes + 1); + v = bch_read_string_list(buf, bch_cache_modes + 1); if (v < 0) return v; @@ -615,8 +615,21 @@ STORE(__bch_cache_set) bch_cache_accounting_clear(&c->accounting); } - if (attr == &sysfs_trigger_gc) + if (attr == &sysfs_trigger_gc) { + /* + * Garbage collection thread only works when sectors_to_gc < 0, + * when users write to sysfs entry trigger_gc, most of time + * they want to forcibly triger gargage collection. Here -1 is + * set to c->sectors_to_gc, to make gc_should_run() give a + * chance to permit gc thread to run. "give a chance" means + * before going into gc_should_run(), there is still chance + * that c->sectors_to_gc being set to other positive value. So + * writing sysfs entry trigger_gc won't always make sure gc + * thread takes effect. + */ + atomic_set(&c->sectors_to_gc, -1); wake_up_gc(c); + } if (attr == &sysfs_prune_cache) { struct shrink_control sc; diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c index 8c3a938f4bf0..176d3c2ef5f5 100644 --- a/drivers/md/bcache/util.c +++ b/drivers/md/bcache/util.c @@ -74,24 +74,44 @@ STRTO_H(strtouint, unsigned int) STRTO_H(strtoll, long long) STRTO_H(strtoull, unsigned long long) +/** + * bch_hprint() - formats @v to human readable string for sysfs. + * + * @v - signed 64 bit integer + * @buf - the (at least 8 byte) buffer to format the result into. + * + * Returns the number of bytes used by format. + */ ssize_t bch_hprint(char *buf, int64_t v) { static const char units[] = "?kMGTPEZY"; - char dec[4] = ""; - int u, t = 0; - - for (u = 0; v >= 1024 || v <= -1024; u++) { - t = v & ~(~0 << 10); - v >>= 10; - } - - if (!u) - return sprintf(buf, "%llu", v); - - if (v < 100 && v > -100) - snprintf(dec, sizeof(dec), ".%i", t / 100); - - return sprintf(buf, "%lli%s%c", v, dec, units[u]); + int u = 0, t; + + uint64_t q; + + if (v < 0) + q = -v; + else + q = v; + + /* For as long as the number is more than 3 digits, but at least + * once, shift right / divide by 1024. Keep the remainder for + * a digit after the decimal point. + */ + do { + u++; + + t = q & ~(~0 << 10); + q >>= 10; + } while (q >= 1000); + + if (v < 0) + /* '-', up to 3 digits, '.', 1 digit, 1 character, null; + * yields 8 bytes. + */ + return sprintf(buf, "-%llu.%i%c", q, t * 10 / 1024, units[u]); + else + return sprintf(buf, "%llu.%i%c", q, t * 10 / 1024, units[u]); } ssize_t bch_snprint_string_list(char *buf, size_t size, const char * const list[], diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index c49022a8dc9d..e663ca082183 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -21,7 +21,8 @@ static void __update_writeback_rate(struct cached_dev *dc) { struct cache_set *c = dc->disk.c; - uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size; + uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size - + bcache_flash_devs_sectors_dirty(c); uint64_t cache_dirty_target = div_u64(cache_sectors * dc->writeback_percent, 100); @@ -186,7 +187,7 @@ static void write_dirty(struct closure *cl) closure_bio_submit(&io->bio, cl); - continue_at(cl, write_dirty_finish, system_wq); + continue_at(cl, write_dirty_finish, io->dc->writeback_write_wq); } static void read_dirty_endio(struct bio *bio) @@ -206,7 +207,7 @@ static void read_dirty_submit(struct closure *cl) closure_bio_submit(&io->bio, cl); - continue_at(cl, write_dirty, system_wq); + continue_at(cl, write_dirty, io->dc->writeback_write_wq); } static void read_dirty(struct cached_dev *dc) @@ -481,17 +482,17 @@ static int sectors_dirty_init_fn(struct btree_op *_op, struct btree *b, return MAP_CONTINUE; } -void bch_sectors_dirty_init(struct cached_dev *dc) +void bch_sectors_dirty_init(struct bcache_device *d) { struct sectors_dirty_init op; bch_btree_op_init(&op.op, -1); - op.inode = dc->disk.id; + op.inode = d->id; - bch_btree_map_keys(&op.op, dc->disk.c, &KEY(op.inode, 0, 0), + bch_btree_map_keys(&op.op, d->c, &KEY(op.inode, 0, 0), sectors_dirty_init_fn, 0); - dc->disk.sectors_dirty_last = bcache_dev_sectors_dirty(&dc->disk); + d->sectors_dirty_last = bcache_dev_sectors_dirty(d); } void bch_cached_dev_writeback_init(struct cached_dev *dc) @@ -515,6 +516,11 @@ void bch_cached_dev_writeback_init(struct cached_dev *dc) int bch_cached_dev_writeback_start(struct cached_dev *dc) { + dc->writeback_write_wq = alloc_workqueue("bcache_writeback_wq", + WQ_MEM_RECLAIM, 0); + if (!dc->writeback_write_wq) + return -ENOMEM; + dc->writeback_thread = kthread_create(bch_writeback_thread, dc, "bcache_writeback"); if (IS_ERR(dc->writeback_thread)) diff --git a/drivers/md/bcache/writeback.h b/drivers/md/bcache/writeback.h index 629bd1a502fd..e35421d20d2e 100644 --- a/drivers/md/bcache/writeback.h +++ b/drivers/md/bcache/writeback.h @@ -14,6 +14,25 @@ static inline uint64_t bcache_dev_sectors_dirty(struct bcache_device *d) return ret; } +static inline uint64_t bcache_flash_devs_sectors_dirty(struct cache_set *c) +{ + uint64_t i, ret = 0; + + mutex_lock(&bch_register_lock); + + for (i = 0; i < c->nr_uuids; i++) { + struct bcache_device *d = c->devices[i]; + + if (!d || !UUID_FLASH_ONLY(&c->uuids[i])) + continue; + ret += bcache_dev_sectors_dirty(d); + } + + mutex_unlock(&bch_register_lock); + + return ret; +} + static inline unsigned offset_to_stripe(struct bcache_device *d, uint64_t offset) { @@ -84,7 +103,7 @@ static inline void bch_writeback_add(struct cached_dev *dc) void bcache_dev_sectors_dirty_add(struct cache_set *, unsigned, uint64_t, int); -void bch_sectors_dirty_init(struct cached_dev *dc); +void bch_sectors_dirty_init(struct bcache_device *); void bch_cached_dev_writeback_init(struct cached_dev *); int bch_cached_dev_writeback_start(struct cached_dev *); diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index dd769e40416f..eed6c397d840 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -181,7 +181,10 @@ static void cec_queue_msg_fh(struct cec_fh *fh, const struct cec_msg *msg) { static const struct cec_event ev_lost_msgs = { .event = CEC_EVENT_LOST_MSGS, - .lost_msgs.lost_msgs = 1, + .flags = 0, + { + .lost_msgs = { 1 }, + }, }; struct cec_msg_entry *entry; diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 96dc01750bc0..36762ec954e7 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1708,11 +1708,10 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) err = dma_declare_coherent_memory(&pdev->dev, res->start, res->start, resource_size(res), - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); - if (!err) { + if (err) { dev_err(&pdev->dev, "Unable to declare CEU memory.\n"); - return -ENXIO; + return err; } pcdev->video_limit = resource_size(res); diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index c00a7c7f460a..b907865d4664 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -159,8 +159,8 @@ static int atmel_ebi_xslate_smc_timings(struct atmel_ebi_dev *ebid, out: if (ret) { dev_err(ebid->ebi->dev, - "missing or invalid timings definition in %s", - np->full_name); + "missing or invalid timings definition in %pOF", + np); return ret; } @@ -270,8 +270,8 @@ static int atmel_ebi_xslate_smc_config(struct atmel_ebi_dev *ebid, return -EINVAL; if ((ret > 0 && !required) || (!ret && required)) { - dev_err(ebid->ebi->dev, "missing atmel,smc- properties in %s", - np->full_name); + dev_err(ebid->ebi->dev, "missing atmel,smc- properties in %pOF", + np); return -EINVAL; } @@ -314,8 +314,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, if (cs >= AT91_MATRIX_EBI_NUM_CS || !(ebi->caps->available_cs & BIT(cs))) { - dev_err(dev, "invalid reg property in %s\n", - np->full_name); + dev_err(dev, "invalid reg property in %pOF\n", np); return -EINVAL; } @@ -324,7 +323,7 @@ static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np, } if (!numcs) { - dev_err(dev, "invalid reg property in %s\n", np->full_name); + dev_err(dev, "invalid reg property in %pOF\n", np); return -EINVAL; } @@ -576,8 +575,8 @@ static int atmel_ebi_probe(struct platform_device *pdev) ret = atmel_ebi_dev_setup(ebi, child, reg_cells); if (ret) { - dev_err(dev, "failed to configure EBI bus for %s, disabling the device", - child->full_name); + dev_err(dev, "failed to configure EBI bus for %pOF, disabling the device", + child); ret = atmel_ebi_dev_disable(ebi, child); if (ret) diff --git a/drivers/memory/jz4780-nemc.c b/drivers/memory/jz4780-nemc.c index 919d1925acb9..bcf06adefc96 100644 --- a/drivers/memory/jz4780-nemc.c +++ b/drivers/memory/jz4780-nemc.c @@ -322,8 +322,8 @@ static int jz4780_nemc_probe(struct platform_device *pdev) bank = of_read_number(prop, 1); if (bank < 1 || bank >= JZ4780_NEMC_NUM_BANKS) { dev_err(nemc->dev, - "%s requests invalid bank %u\n", - child->full_name, bank); + "%pOF requests invalid bank %u\n", + child, bank); /* Will continue the outer loop below. */ referenced = 0; @@ -334,12 +334,12 @@ static int jz4780_nemc_probe(struct platform_device *pdev) } if (!referenced) { - dev_err(nemc->dev, "%s has no addresses\n", - child->full_name); + dev_err(nemc->dev, "%pOF has no addresses\n", + child); continue; } else if (nemc->banks_present & referenced) { - dev_err(nemc->dev, "%s conflicts with another node\n", - child->full_name); + dev_err(nemc->dev, "%pOF conflicts with another node\n", + child); continue; } diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index 4afbc412f959..8f2d152a78b8 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -16,6 +16,7 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> @@ -23,7 +24,10 @@ #include <soc/mediatek/smi.h> #include <dt-bindings/memory/mt2701-larb-port.h> +/* mt8173 */ #define SMI_LARB_MMU_EN 0xf00 + +/* mt2701 */ #define REG_SMI_SECUR_CON_BASE 0x5c0 /* every register control 8 port, register offset 0x4 */ @@ -41,7 +45,12 @@ /* mt2701 domain should be set to 3 */ #define SMI_SECUR_CON_VAL_DOMAIN(id) (0x3 << ((((id) & 0x7) << 2) + 1)) +/* mt2712 */ +#define SMI_LARB_NONSEC_CON(id) (0x380 + ((id) * 4)) +#define F_MMU_EN BIT(0) + struct mtk_smi_larb_gen { + bool need_larbid; int port_in_larb[MTK_LARB_NR_MAX + 1]; void (*config_port)(struct device *); }; @@ -148,6 +157,15 @@ mtk_smi_larb_bind(struct device *dev, struct device *master, void *data) struct mtk_smi_iommu *smi_iommu = data; unsigned int i; + if (larb->larb_gen->need_larbid) { + larb->mmu = &smi_iommu->larb_imu[larb->larbid].mmu; + return 0; + } + + /* + * If there is no larbid property, Loop to find the corresponding + * iommu information. + */ for (i = 0; i < smi_iommu->larb_nr; i++) { if (dev == smi_iommu->larb_imu[i].dev) { /* The 'mmu' may be updated in iommu-attach/detach. */ @@ -158,13 +176,32 @@ mtk_smi_larb_bind(struct device *dev, struct device *master, void *data) return -ENODEV; } -static void mtk_smi_larb_config_port(struct device *dev) +static void mtk_smi_larb_config_port_mt2712(struct device *dev) { struct mtk_smi_larb *larb = dev_get_drvdata(dev); + u32 reg; + int i; - writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN); + /* + * larb 8/9 is the bdpsys larb, the iommu_en is enabled defaultly. + * Don't need to set it again. + */ + if (larb->larbid == 8 || larb->larbid == 9) + return; + + for_each_set_bit(i, (unsigned long *)larb->mmu, 32) { + reg = readl_relaxed(larb->base + SMI_LARB_NONSEC_CON(i)); + reg |= F_MMU_EN; + writel(reg, larb->base + SMI_LARB_NONSEC_CON(i)); + } } +static void mtk_smi_larb_config_port_mt8173(struct device *dev) +{ + struct mtk_smi_larb *larb = dev_get_drvdata(dev); + + writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN); +} static void mtk_smi_larb_config_port_gen1(struct device *dev) { @@ -210,10 +247,11 @@ static const struct component_ops mtk_smi_larb_component_ops = { static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = { /* mt8173 do not need the port in larb */ - .config_port = mtk_smi_larb_config_port, + .config_port = mtk_smi_larb_config_port_mt8173, }; static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = { + .need_larbid = true, .port_in_larb = { LARB0_PORT_OFFSET, LARB1_PORT_OFFSET, LARB2_PORT_OFFSET, LARB3_PORT_OFFSET @@ -221,6 +259,11 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = { .config_port = mtk_smi_larb_config_port_gen1, }; +static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = { + .need_larbid = true, + .config_port = mtk_smi_larb_config_port_mt2712, +}; + static const struct of_device_id mtk_smi_larb_of_ids[] = { { .compatible = "mediatek,mt8173-smi-larb", @@ -230,6 +273,10 @@ static const struct of_device_id mtk_smi_larb_of_ids[] = { .compatible = "mediatek,mt2701-smi-larb", .data = &mtk_smi_larb_mt2701 }, + { + .compatible = "mediatek,mt2712-smi-larb", + .data = &mtk_smi_larb_mt2712 + }, {} }; @@ -240,20 +287,13 @@ static int mtk_smi_larb_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *smi_node; struct platform_device *smi_pdev; - const struct of_device_id *of_id; - - if (!dev->pm_domain) - return -EPROBE_DEFER; - - of_id = of_match_node(mtk_smi_larb_of_ids, pdev->dev.of_node); - if (!of_id) - return -EINVAL; + int err; larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL); if (!larb) return -ENOMEM; - larb->larb_gen = of_id->data; + larb->larb_gen = of_device_get_match_data(dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); larb->base = devm_ioremap_resource(dev, res); if (IS_ERR(larb->base)) @@ -268,6 +308,15 @@ static int mtk_smi_larb_probe(struct platform_device *pdev) return PTR_ERR(larb->smi.clk_smi); larb->smi.dev = dev; + if (larb->larb_gen->need_larbid) { + err = of_property_read_u32(dev->of_node, "mediatek,larb-id", + &larb->larbid); + if (err) { + dev_err(dev, "missing larbid property\n"); + return err; + } + } + smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0); if (!smi_node) return -EINVAL; @@ -275,6 +324,8 @@ static int mtk_smi_larb_probe(struct platform_device *pdev) smi_pdev = of_find_device_by_node(smi_node); of_node_put(smi_node); if (smi_pdev) { + if (!platform_get_drvdata(smi_pdev)) + return -EPROBE_DEFER; larb->smi_common_dev = &smi_pdev->dev; } else { dev_err(dev, "Failed to get the smi_common device\n"); @@ -311,6 +362,10 @@ static const struct of_device_id mtk_smi_common_of_ids[] = { .compatible = "mediatek,mt2701-smi-common", .data = (void *)MTK_SMI_GEN1 }, + { + .compatible = "mediatek,mt2712-smi-common", + .data = (void *)MTK_SMI_GEN2 + }, {} }; @@ -319,11 +374,8 @@ static int mtk_smi_common_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct mtk_smi *common; struct resource *res; - const struct of_device_id *of_id; enum mtk_smi_gen smi_gen; - - if (!dev->pm_domain) - return -EPROBE_DEFER; + int ret; common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); if (!common) @@ -338,17 +390,13 @@ static int mtk_smi_common_probe(struct platform_device *pdev) if (IS_ERR(common->clk_smi)) return PTR_ERR(common->clk_smi); - of_id = of_match_node(mtk_smi_common_of_ids, pdev->dev.of_node); - if (!of_id) - return -EINVAL; - /* * for mtk smi gen 1, we need to get the ao(always on) base to config * m4u port, and we need to enable the aync clock for transform the smi * clock into emi clock domain, but for mtk smi gen2, there's no smi ao * base. */ - smi_gen = (enum mtk_smi_gen)of_id->data; + smi_gen = (enum mtk_smi_gen)of_device_get_match_data(dev); if (smi_gen == MTK_SMI_GEN1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); common->smi_ao_base = devm_ioremap_resource(dev, res); @@ -359,7 +407,9 @@ static int mtk_smi_common_probe(struct platform_device *pdev) if (IS_ERR(common->clk_async)) return PTR_ERR(common->clk_async); - clk_prepare_enable(common->clk_async); + ret = clk_prepare_enable(common->clk_async); + if (ret) + return ret; } pm_runtime_enable(dev); platform_set_drvdata(pdev, common); @@ -403,4 +453,4 @@ err_unreg_smi: return ret; } -subsys_initcall(mtk_smi_init); +module_init(mtk_smi_init); diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c index 24852812fd44..981860879d02 100644 --- a/drivers/memory/mvebu-devbus.c +++ b/drivers/memory/mvebu-devbus.c @@ -105,8 +105,8 @@ static int get_timing_param_ps(struct devbus *devbus, err = of_property_read_u32(node, name, &time_ps); if (err < 0) { - dev_err(devbus->dev, "%s has no '%s' property\n", - name, node->full_name); + dev_err(devbus->dev, "%pOF has no '%s' property\n", + node, name); return err; } @@ -127,8 +127,8 @@ static int devbus_get_timing_params(struct devbus *devbus, err = of_property_read_u32(node, "devbus,bus-width", &r->bus_width); if (err < 0) { dev_err(devbus->dev, - "%s has no 'devbus,bus-width' property\n", - node->full_name); + "%pOF has no 'devbus,bus-width' property\n", + node); return err; } @@ -180,8 +180,8 @@ static int devbus_get_timing_params(struct devbus *devbus, &w->sync_enable); if (err < 0) { dev_err(devbus->dev, - "%s has no 'devbus,sync-enable' property\n", - node->full_name); + "%pOF has no 'devbus,sync-enable' property\n", + node); return err; } } diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index a80e17de906d..7059bbda2fac 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -1930,8 +1930,8 @@ static int gpmc_probe_onenand_child(struct platform_device *pdev, struct omap_onenand_platform_data *gpmc_onenand_data; if (of_property_read_u32(child, "reg", &val) < 0) { - dev_err(&pdev->dev, "%s has no 'reg' property\n", - child->full_name); + dev_err(&pdev->dev, "%pOF has no 'reg' property\n", + child); return -ENODEV; } @@ -1979,14 +1979,14 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, struct gpmc_device *gpmc = platform_get_drvdata(pdev); if (of_property_read_u32(child, "reg", &cs) < 0) { - dev_err(&pdev->dev, "%s has no 'reg' property\n", - child->full_name); + dev_err(&pdev->dev, "%pOF has no 'reg' property\n", + child); return -ENODEV; } if (of_address_to_resource(child, 0, &res) < 0) { - dev_err(&pdev->dev, "%s has malformed 'reg' property\n", - child->full_name); + dev_err(&pdev->dev, "%pOF has malformed 'reg' property\n", + child); return -ENODEV; } @@ -2084,8 +2084,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width); if (ret < 0) { - dev_err(&pdev->dev, "%s has no 'bank-width' property\n", - child->full_name); + dev_err(&pdev->dev, "%pOF has no 'bank-width' property\n", + child); goto err; } } diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index 895f655780a7..55d824b3a808 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -494,7 +494,7 @@ static struct platform_driver kempld_driver = { .remove = kempld_remove, }; -static struct dmi_system_id kempld_dmi_table[] __initdata = { +static const struct dmi_system_id kempld_dmi_table[] __initconst = { { .ident = "BBD6", .matches = { diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index d18b3d9292fd..3ba04f371380 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -1279,7 +1279,7 @@ ssize_t cxl_pci_afu_read_err_buffer(struct cxl_afu *afu, char *buf, } /* use bounce buffer for copy */ - tbuf = (void *)__get_free_page(GFP_TEMPORARY); + tbuf = (void *)__get_free_page(GFP_KERNEL); if (!tbuf) return -ENOMEM; diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index b833e6cc684c..84b16133554b 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1809,37 +1809,22 @@ static int dbg_protection_show(struct seq_file *s, void *p) } DEBUGFS_RO_ATTR(protection, dbg_protection_show); -static int __init doc_dbg_register(struct docg3 *docg3) -{ - struct dentry *root, *entry; - - root = debugfs_create_dir("docg3", NULL); - if (!root) - return -ENOMEM; - - entry = debugfs_create_file("flashcontrol", S_IRUSR, root, docg3, - &flashcontrol_fops); - if (entry) - entry = debugfs_create_file("asic_mode", S_IRUSR, root, - docg3, &asic_mode_fops); - if (entry) - entry = debugfs_create_file("device_id", S_IRUSR, root, - docg3, &device_id_fops); - if (entry) - entry = debugfs_create_file("protection", S_IRUSR, root, - docg3, &protection_fops); - if (entry) { - docg3->debugfs_root = root; - return 0; - } else { - debugfs_remove_recursive(root); - return -ENOMEM; - } -} - -static void doc_dbg_unregister(struct docg3 *docg3) +static void __init doc_dbg_register(struct mtd_info *floor) { - debugfs_remove_recursive(docg3->debugfs_root); + struct dentry *root = floor->dbg.dfs_dir; + struct docg3 *docg3 = floor->priv; + + if (IS_ERR_OR_NULL(root)) + return; + + debugfs_create_file("docg3_flashcontrol", S_IRUSR, root, docg3, + &flashcontrol_fops); + debugfs_create_file("docg3_asic_mode", S_IRUSR, root, docg3, + &asic_mode_fops); + debugfs_create_file("docg3_device_id", S_IRUSR, root, docg3, + &device_id_fops); + debugfs_create_file("docg3_protection", S_IRUSR, root, docg3, + &protection_fops); } /** @@ -2114,6 +2099,8 @@ static int __init docg3_probe(struct platform_device *pdev) 0); if (ret) goto err_probe; + + doc_dbg_register(cascade->floors[floor]); } ret = doc_register_sysfs(pdev, cascade); @@ -2121,7 +2108,6 @@ static int __init docg3_probe(struct platform_device *pdev) goto err_probe; platform_set_drvdata(pdev, cascade); - doc_dbg_register(cascade->floors[0]->priv); return 0; notfound: @@ -2148,7 +2134,6 @@ static int docg3_release(struct platform_device *pdev) int floor; doc_unregister_sysfs(pdev, cascade); - doc_dbg_unregister(docg3); for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) if (cascade->floors[floor]) doc_release_device(cascade->floors[floor]); diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index 19fb93f96a3a..e99946575398 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -299,7 +299,6 @@ struct docg3_cascade { * @oob_autoecc: if 1, use only bytes 0-7, 15, and fill the others with HW ECC * if 0, use all the 16 bytes. * @oob_write_buf: prepared OOB for next page_write - * @debugfs_root: debugfs root node */ struct docg3 { struct device *dev; @@ -312,7 +311,6 @@ struct docg3 { loff_t oob_write_ofs; int oob_autoecc; u8 oob_write_buf[DOC_LAYOUT_OOB_SIZE]; - struct dentry *debugfs_root; }; #define doc_err(fmt, arg...) dev_err(docg3->dev, (fmt), ## arg) diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index dd5069876537..ddf478976013 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -775,6 +775,8 @@ static int spear_smi_probe_config_dt(struct platform_device *pdev, pdata->board_flash_info = devm_kzalloc(&pdev->dev, sizeof(*pdata->board_flash_info), GFP_KERNEL); + if (!pdata->board_flash_info) + return -ENOMEM; /* Fill structs for each subnode (flash device) */ while ((pp = of_get_next_child(np, pp))) { diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 21afd94cd904..7bc29d725200 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -2073,15 +2073,17 @@ static int stfsm_probe(struct platform_device *pdev) ret = stfsm_init(fsm); if (ret) { dev_err(&pdev->dev, "Failed to initialise FSM Controller\n"); - return ret; + goto err_clk_unprepare; } stfsm_fetch_platform_configs(pdev); /* Detect SPI FLASH device */ info = stfsm_jedec_probe(fsm); - if (!info) - return -ENODEV; + if (!info) { + ret = -ENODEV; + goto err_clk_unprepare; + } fsm->info = info; /* Use device size to determine address width */ @@ -2095,11 +2097,11 @@ static int stfsm_probe(struct platform_device *pdev) if (info->config) { ret = info->config(fsm); if (ret) - return ret; + goto err_clk_unprepare; } else { ret = stfsm_prepare_rwe_seqs_default(fsm); if (ret) - return ret; + goto err_clk_unprepare; } fsm->mtd.name = info->name; @@ -2124,6 +2126,10 @@ static int stfsm_probe(struct platform_device *pdev) fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10)); return mtd_device_register(&fsm->mtd, NULL, 0); + +err_clk_unprepare: + clk_disable_unprepare(fsm->clk); + return ret; } static int stfsm_remove(struct platform_device *pdev) @@ -2147,9 +2153,7 @@ static int stfsmfsm_resume(struct device *dev) { struct stfsm *fsm = dev_get_drvdata(dev); - clk_prepare_enable(fsm->clk); - - return 0; + return clk_prepare_enable(fsm->clk); } #endif diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 8db740d6eb08..57ef1fb42a04 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -33,7 +33,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nftl.h> #include <linux/mtd/inftl.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/uaccess.h> #include <asm/errno.h> #include <asm/io.h> diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index f2b68667ea59..26de0a1d08cf 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -296,7 +296,7 @@ static void amd76xrom_remove_one(struct pci_dev *pdev) amd76xrom_cleanup(window); } -static struct pci_device_id amd76xrom_pci_tbl[] = { +static const struct pci_device_id amd76xrom_pci_tbl[] = { { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440, @@ -319,7 +319,7 @@ static struct pci_driver amd76xrom_driver = { static int __init init_amd76xrom(void) { struct pci_dev *pdev; - struct pci_device_id *id; + const struct pci_device_id *id; pdev = NULL; for(id = amd76xrom_pci_tbl; id->vendor; id++) { pdev = pci_get_device(id->vendor, id->device, NULL); diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 4f206a99164c..584962ec49f8 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -326,7 +326,7 @@ static void ck804xrom_remove_one(struct pci_dev *pdev) ck804xrom_cleanup(window); } -static struct pci_device_id ck804xrom_pci_tbl[] = { +static const struct pci_device_id ck804xrom_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0051), .driver_data = DEV_CK804 }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0360), .driver_data = DEV_MCP55 }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0361), .driver_data = DEV_MCP55 }, @@ -353,7 +353,7 @@ static struct pci_driver ck804xrom_driver = { static int __init init_ck804xrom(void) { struct pci_dev *pdev; - struct pci_device_id *id; + const struct pci_device_id *id; int retVal; pdev = NULL; diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index 9646b0766ce0..da9f6d76ce1d 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -384,7 +384,7 @@ static void esb2rom_remove_one(struct pci_dev *pdev) esb2rom_cleanup(window); } -static struct pci_device_id esb2rom_pci_tbl[] = { +static const struct pci_device_id esb2rom_pci_tbl[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, @@ -414,7 +414,7 @@ static struct pci_driver esb2rom_driver = { static int __init init_esb2rom(void) { struct pci_dev *pdev; - struct pci_device_id *id; + const struct pci_device_id *id; int retVal; pdev = NULL; diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 976d42f63aef..1888c5bf13f8 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -323,7 +323,7 @@ static void ichxrom_remove_one(struct pci_dev *pdev) ichxrom_cleanup(window); } -static struct pci_device_id ichxrom_pci_tbl[] = { +static const struct pci_device_id ichxrom_pci_tbl[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, @@ -351,7 +351,7 @@ static struct pci_driver ichxrom_driver = { static int __init init_ichxrom(void) { struct pci_dev *pdev; - struct pci_device_id *id; + const struct pci_device_id *id; pdev = NULL; for (id = ichxrom_pci_tbl; id->vendor; id++) { diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c index 8bf79775e7c1..dd5d6855f543 100644 --- a/drivers/mtd/maps/intel_vr_nor.c +++ b/drivers/mtd/maps/intel_vr_nor.c @@ -170,7 +170,7 @@ static int vr_nor_init_maps(struct vr_nor_mtd *p) return err; } -static struct pci_device_id vr_nor_pci_ids[] = { +static const struct pci_device_id vr_nor_pci_ids[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)}, {0,} }; diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index eb0242e0b2d9..7b3bb40aff72 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -228,7 +228,7 @@ static struct mtd_pci_info intel_dc21285_info = { * PCI device ID table */ -static struct pci_device_id mtd_pci_ids[] = { +static const struct pci_device_id mtd_pci_ids[] = { { .vendor = PCI_VENDOR_ID_INTEL, .device = 0x530d, diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c index 62fa6836f218..b1bd4faecfb2 100644 --- a/drivers/mtd/maps/physmap_of_core.c +++ b/drivers/mtd/maps/physmap_of_core.c @@ -178,8 +178,8 @@ static int of_flash_probe(struct platform_device *dev) */ p = of_get_property(dp, "reg", &count); if (!p || count % reg_tuple_size != 0) { - dev_err(&dev->dev, "Malformed reg property on %s\n", - dev->dev.of_node->full_name); + dev_err(&dev->dev, "Malformed reg property on %pOF\n", + dev->dev.of_node); err = -EINVAL; goto err_flash_remove; } @@ -235,10 +235,10 @@ static int of_flash_probe(struct platform_device *dev) err = of_flash_probe_gemini(dev, dp, &info->list[i].map); if (err) - return err; + goto err_out; err = of_flash_probe_versatile(dev, dp, &info->list[i].map); if (err) - return err; + goto err_out; err = -ENOMEM; info->list[i].map.virt = ioremap(info->list[i].map.phys, diff --git a/drivers/mtd/maps/physmap_of_gemini.c b/drivers/mtd/maps/physmap_of_gemini.c index 05b286b5289f..4ed1a6bb4d3c 100644 --- a/drivers/mtd/maps/physmap_of_gemini.c +++ b/drivers/mtd/maps/physmap_of_gemini.c @@ -43,13 +43,6 @@ #define FLASH_PARALLEL_HIGH_PIN_CNT (1 << 20) /* else low pin cnt */ -/* Miscellaneous Control Register */ -#define GLOBAL_MISC_CTRL 0x30 -#define FLASH_PADS_MASK 0x07 -#define NAND_PADS_DISABLE BIT(2) -#define PFLASH_PADS_DISABLE BIT(1) -#define SFLASH_PADS_DISABLE BIT(0) - static const struct of_device_id syscon_match[] = { { .compatible = "cortina,gemini-syscon" }, { }, @@ -102,15 +95,6 @@ int of_flash_probe_gemini(struct platform_device *pdev, map->bankwidth * 8); } - /* Activate parallel (NOR flash) mode */ - ret = regmap_update_bits(rmap, GLOBAL_MISC_CTRL, - FLASH_PADS_MASK, - SFLASH_PADS_DISABLE | NAND_PADS_DISABLE); - if (ret) { - dev_err(dev, "unable to set up physmap pads\n"); - return -ENODEV; - } - dev_info(&pdev->dev, "initialized Gemini-specific physmap control\n"); return 0; diff --git a/drivers/mtd/maps/physmap_of_versatile.c b/drivers/mtd/maps/physmap_of_versatile.c index 8c6ccded9be8..03f2b6e7bc7e 100644 --- a/drivers/mtd/maps/physmap_of_versatile.c +++ b/drivers/mtd/maps/physmap_of_versatile.c @@ -97,7 +97,7 @@ static const struct of_device_id ebi_match[] = { static int ap_flash_init(struct platform_device *pdev) { struct device_node *ebi; - static void __iomem *ebi_base; + void __iomem *ebi_base; u32 val; int ret; diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 414956eca0c9..1e73bba6e286 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -55,8 +55,8 @@ int uflash_devinit(struct platform_device *op, struct device_node *dp) /* Non-CFI userflash device-- once I find one we * can work on supporting it. */ - printk(KERN_ERR PFX "Unsupported device at %s, 0x%llx\n", - dp->full_name, (unsigned long long)op->resource[0].start); + printk(KERN_ERR PFX "Unsupported device at %pOF, 0x%llx\n", + dp, (unsigned long long)op->resource[0].start); return -ENODEV; } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 956382cea256..e7ea842ba3db 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -40,6 +40,7 @@ #include <linux/slab.h> #include <linux/reboot.h> #include <linux/leds.h> +#include <linux/debugfs.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -339,7 +340,7 @@ static struct attribute *mtd_attrs[] = { }; ATTRIBUTE_GROUPS(mtd); -static struct device_type mtd_devtype = { +static const struct device_type mtd_devtype = { .name = "mtd", .groups = mtd_groups, .release = mtd_release, @@ -477,6 +478,8 @@ int mtd_pairing_groups(struct mtd_info *mtd) } EXPORT_SYMBOL_GPL(mtd_pairing_groups); +static struct dentry *dfs_dir_mtd; + /** * add_mtd_device - register an MTD device * @mtd: pointer to new MTD device info structure @@ -552,6 +555,14 @@ int add_mtd_device(struct mtd_info *mtd) if (error) goto fail_added; + if (!IS_ERR_OR_NULL(dfs_dir_mtd)) { + mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), dfs_dir_mtd); + if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) { + pr_debug("mtd device %s won't show data in debugfs\n", + dev_name(&mtd->dev)); + } + } + device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL, "mtd%dro", i); @@ -594,6 +605,8 @@ int del_mtd_device(struct mtd_info *mtd) mutex_lock(&mtd_table_mutex); + debugfs_remove_recursive(mtd->dbg.dfs_dir); + if (idr_find(&mtd_idr, mtd->index) != mtd) { ret = -ENODEV; goto out_error; @@ -1811,6 +1824,8 @@ static int __init init_mtd(void) if (ret) goto out_procfs; + dfs_dir_mtd = debugfs_create_dir("mtd", NULL); + return 0; out_procfs: @@ -1826,6 +1841,7 @@ err_reg: static void __exit cleanup_mtd(void) { + debugfs_remove_recursive(dfs_dir_mtd); cleanup_mtdchar(); if (proc_mtd) remove_proc_entry("mtd", NULL); diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index f12879a3d4ff..7d9080e33865 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -138,8 +138,6 @@ struct mtdswap_dev { char *page_buf; char *oob_buf; - - struct dentry *debugfs_root; }; struct mtdswap_oobdata { @@ -1315,29 +1313,19 @@ static const struct file_operations mtdswap_fops = { static int mtdswap_add_debugfs(struct mtdswap_dev *d) { - struct gendisk *gd = d->mbd_dev->disk; - struct device *dev = disk_to_dev(gd); - - struct dentry *root; + struct dentry *root = d->mtd->dbg.dfs_dir; struct dentry *dent; - root = debugfs_create_dir(gd->disk_name, NULL); - if (IS_ERR(root)) + if (!IS_ENABLED(CONFIG_DEBUG_FS)) return 0; - if (!root) { - dev_err(dev, "failed to initialize debugfs\n"); + if (IS_ERR_OR_NULL(root)) return -1; - } - - d->debugfs_root = root; - dent = debugfs_create_file("stats", S_IRUSR, root, d, + dent = debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, &mtdswap_fops); if (!dent) { dev_err(d->dev, "debugfs_create_file failed\n"); - debugfs_remove_recursive(root); - d->debugfs_root = NULL; return -1; } @@ -1540,7 +1528,6 @@ static void mtdswap_remove_dev(struct mtd_blktrans_dev *dev) { struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev); - debugfs_remove_recursive(d->debugfs_root); del_mtd_blktrans_dev(dev); mtdswap_cleanup(d); kfree(d); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index dbfa72d61d5a..3f2036f31da4 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -315,7 +315,7 @@ config MTD_NAND_ATMEL config MTD_NAND_PXA3xx tristate "NAND support on PXA3xx and Armada 370/XP" - depends on PXA3xx || ARCH_MMP || PLAT_ORION + depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU help This enables the driver for the NAND flash device found on PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2). diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 5d6c26f3cf7f..dcec9cf4983f 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -20,7 +20,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/gpio.h> #include <linux/platform_data/gpio-omap.h> diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c index 1913ce18fb1c..f25eca79f4e5 100644 --- a/drivers/mtd/nand/atmel/nand-controller.c +++ b/drivers/mtd/nand/atmel/nand-controller.c @@ -59,7 +59,7 @@ #include <linux/mfd/syscon/atmel-matrix.h> #include <linux/mfd/syscon/atmel-smc.h> #include <linux/module.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> @@ -2091,8 +2091,8 @@ atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc) } nc->irq = of_irq_get(nand_np, 0); - if (nc->irq < 0) { - ret = nc->irq; + if (nc->irq <= 0) { + ret = nc->irq ?: -ENXIO; if (ret != -EPROBE_DEFER) dev_err(dev, "Failed to get IRQ number (err = %d)\n", ret); @@ -2183,11 +2183,12 @@ atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc) nc->irq = of_irq_get(np, 0); of_node_put(np); - if (nc->irq < 0) { - if (nc->irq != -EPROBE_DEFER) + if (nc->irq <= 0) { + ret = nc->irq ?: -ENXIO; + if (ret != -EPROBE_DEFER) dev_err(dev, "Failed to get IRQ number (err = %d)\n", - nc->irq); - return nc->irq; + ret); + return ret; } np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0); diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c index 8c210a5776bc..146af8218314 100644 --- a/drivers/mtd/nand/atmel/pmecc.c +++ b/drivers/mtd/nand/atmel/pmecc.c @@ -47,7 +47,7 @@ #include <linux/genalloc.h> #include <linux/iopoll.h> #include <linux/module.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 9bf6d9915694..9d4a28fa6b73 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -14,7 +14,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/platform_device.h> #include <asm/io.h> diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h index 8ea75710a854..c8834767ab6d 100644 --- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h +++ b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h @@ -6,7 +6,7 @@ #endif #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> struct bcm47xxnflash { struct bcma_drv_cc *cc; diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 3962f55bd034..5655dca6ce43 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -49,7 +49,7 @@ #include <linux/bitops.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index 7419c5ce63f8..e0eb51d8c012 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -29,7 +29,7 @@ #include <linux/bitops.h> #include <linux/mm.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/of.h> #include <linux/of_platform.h> diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 2fd733eba0a3..bc558c438a57 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -13,7 +13,7 @@ #include <linux/device.h> #undef DEBUG #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/rslib.h> #include <linux/pci.h> diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index 949b9400dcb7..1fc435f994e1 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -18,7 +18,7 @@ * CM-X270 board. */ -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/slab.h> #include <linux/gpio.h> diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 594b28684138..d48877540f14 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -24,7 +24,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 7b26e53b95b1..ccc8c43abcff 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -29,7 +29,7 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/io.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/slab.h> #include <linux/of_device.h> diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index d723be352148..3087b0ba7b7f 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -980,9 +980,6 @@ static int denali_erase(struct mtd_info *mtd, int page) return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL; } -#define DIV_ROUND_DOWN_ULL(ll, d) \ - ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) - static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, const struct nand_data_interface *conf) { diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 237cc706b0fb..9239e6793e6e 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -21,7 +21,7 @@ #define __DENALI_H__ #include <linux/bitops.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #define DEVICE_RESET 0x0 #define DEVICE_RESET__BANK(bank) BIT(bank) diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c index 47f398edf18f..56e2e177644d 100644 --- a/drivers/mtd/nand/denali_dt.c +++ b/drivers/mtd/nand/denali_dt.c @@ -118,7 +118,9 @@ static int denali_dt_probe(struct platform_device *pdev) dev_err(&pdev->dev, "no clk available\n"); return PTR_ERR(dt->clk); } - clk_prepare_enable(dt->clk); + ret = clk_prepare_enable(dt->clk); + if (ret) + return ret; denali->clk_x_rate = clk_get_rate(dt->clk); diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index a023ab9e9cbf..c3aa53caab5c 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -27,7 +27,7 @@ #include <linux/io.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/doc2000.h> #include <linux/mtd/partitions.h> #include <linux/mtd/inftl.h> diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index a27a84fbfb84..2436cbc71662 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -41,7 +41,7 @@ #include <linux/bitops.h> #include <linux/mtd/partitions.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/bch.h> #include <linux/bitrev.h> #include <linux/jiffies.h> diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index b9ac16f05057..17db2f90aa2c 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -34,7 +34,7 @@ #include <linux/interrupt.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 59408ec2c69f..9e03bac7f34c 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -26,7 +26,7 @@ #include <linux/of_address.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/mtd/nand_ecc.h> #include <linux/fsl_ifc.h> diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index d85fa2555b68..a88e2cf66e0f 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -14,7 +14,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/delay.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> #include <linux/mtd/mtd.h> diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 9d8b051d3187..eac15d9bf49e 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -28,7 +28,7 @@ #include <linux/sched.h> #include <linux/types.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/platform_device.h> #include <linux/of.h> diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 85294f150f4f..fd3648952b5a 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -26,7 +26,7 @@ #include <linux/gpio.h> #include <linux/io.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/mtd/nand-gpio.h> #include <linux/of.h> diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index 9df0ad64e7e0..a45e4ce13d10 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -17,7 +17,7 @@ #ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H #define __DRIVERS_MTD_NAND_GPMI_NAND_H -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 530caa80b1b6..d9ee1a7e6956 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -26,7 +26,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/interrupt.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/dma-mapping.h> #include <linux/platform_device.h> #include <linux/mtd/partitions.h> diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index 0d06a1f07d82..ad827d4af3e9 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -20,7 +20,7 @@ #include <linux/slab.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/gpio.h> diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/jz4780_nand.c index 8bc835f71b26..e69f6ae4c539 100644 --- a/drivers/mtd/nand/jz4780_nand.c +++ b/drivers/mtd/nand/jz4780_nand.c @@ -20,7 +20,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/jz4780-nemc.h> diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index 846a66c1b133..c3bb358ef01e 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -27,7 +27,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/clk.h> #include <linux/err.h> @@ -705,7 +705,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) res = -ENOENT; goto err_exit1; } - clk_prepare_enable(host->clk); + res = clk_prepare_enable(host->clk); + if (res) + goto err_exit1; nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; nand_chip->dev_ready = lpc32xx_nand_device_ready; @@ -846,9 +848,12 @@ static int lpc32xx_nand_remove(struct platform_device *pdev) static int lpc32xx_nand_resume(struct platform_device *pdev) { struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + int ret; /* Re-enable NAND clock */ - clk_prepare_enable(host->clk); + ret = clk_prepare_enable(host->clk); + if (ret) + return ret; /* Fresh init of NAND controller */ lpc32xx_nand_setup(host); diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index a0669a33f8fe..b61f28a1554d 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -23,7 +23,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/clk.h> #include <linux/err.h> @@ -840,7 +840,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) res = -ENOENT; goto err_exit1; } - clk_prepare_enable(host->clk); + res = clk_prepare_enable(host->clk); + if (res) + goto err_exit1; /* Set NAND IO addresses and command/ready functions */ chip->IO_ADDR_R = SLC_DATA(host->io_base); @@ -972,9 +974,12 @@ static int lpc32xx_nand_remove(struct platform_device *pdev) static int lpc32xx_nand_resume(struct platform_device *pdev) { struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + int ret; /* Re-enable NAND clock */ - clk_prepare_enable(host->clk); + ret = clk_prepare_enable(host->clk); + if (ret) + return ret; /* Fresh init of NAND controller */ lpc32xx_nand_setup(host); diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 0e86fb6277c3..b6b97cc9fba6 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -33,7 +33,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/of_address.h> #include <linux/of_device.h> diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/mtk_ecc.c index 6c3a4aab0b48..7f3b065b6b8f 100644 --- a/drivers/mtd/nand/mtk_ecc.c +++ b/drivers/mtd/nand/mtk_ecc.c @@ -464,8 +464,8 @@ static int mtk_ecc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(dev, "failed to get irq\n"); - return -EINVAL; + dev_err(dev, "failed to get irq: %d\n", irq); + return irq; } ret = dma_set_mask(dev, DMA_BIT_MASK(32)); diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c index f7ae99464375..d86a7d131cc0 100644 --- a/drivers/mtd/nand/mtk_nand.c +++ b/drivers/mtd/nand/mtk_nand.c @@ -19,7 +19,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/clk.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/mtd.h> #include <linux/module.h> #include <linux/iopoll.h> diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index a764d5ca7536..53e5e0337c3e 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -22,7 +22,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/interrupt.h> #include <linux/device.h> @@ -876,6 +876,8 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) } } +#define MXC_V1_ECCBYTES 5 + static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) { @@ -885,7 +887,7 @@ static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section, return -ERANGE; oobregion->offset = (section * 16) + 6; - oobregion->length = nand_chip->ecc.bytes; + oobregion->length = MXC_V1_ECCBYTES; return 0; } @@ -907,8 +909,7 @@ static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section, oobregion->length = 4; } } else { - oobregion->offset = ((section - 1) * 16) + - nand_chip->ecc.bytes + 6; + oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6; if (section < nand_chip->ecc.steps) oobregion->length = (section * 16) + 6 - oobregion->offset; diff --git a/drivers/mtd/nand/nand_amd.c b/drivers/mtd/nand/nand_amd.c index 170403a3bfa8..22f060f38123 100644 --- a/drivers/mtd/nand/nand_amd.c +++ b/drivers/mtd/nand/nand_amd.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> static void amd_nand_decode_id(struct nand_chip *chip) { diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index c6c18b82f8f4..bcc8cef1c615 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -39,7 +39,7 @@ #include <linux/nmi.h> #include <linux/types.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/nand_bch.h> #include <linux/interrupt.h> @@ -1248,179 +1248,6 @@ int nand_reset(struct nand_chip *chip, int chipnr) } /** - * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks - * @mtd: mtd info - * @ofs: offset to start unlock from - * @len: length to unlock - * @invert: - * - when = 0, unlock the range of blocks within the lower and - * upper boundary address - * - when = 1, unlock the range of blocks outside the boundaries - * of the lower and upper boundary address - * - * Returs unlock status. - */ -static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, - uint64_t len, int invert) -{ - int ret = 0; - int status, page; - struct nand_chip *chip = mtd_to_nand(mtd); - - /* Submit address of first page to unlock */ - page = ofs >> chip->page_shift; - chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); - - /* Submit address of last page to unlock */ - page = (ofs + len) >> chip->page_shift; - chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, - (page | invert) & chip->pagemask); - - /* Call wait ready function */ - status = chip->waitfunc(mtd, chip); - /* See if device thinks it succeeded */ - if (status & NAND_STATUS_FAIL) { - pr_debug("%s: error status = 0x%08x\n", - __func__, status); - ret = -EIO; - } - - return ret; -} - -/** - * nand_unlock - [REPLACEABLE] unlocks specified locked blocks - * @mtd: mtd info - * @ofs: offset to start unlock from - * @len: length to unlock - * - * Returns unlock status. - */ -int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - int ret = 0; - int chipnr; - struct nand_chip *chip = mtd_to_nand(mtd); - - pr_debug("%s: start = 0x%012llx, len = %llu\n", - __func__, (unsigned long long)ofs, len); - - if (check_offs_len(mtd, ofs, len)) - return -EINVAL; - - /* Align to last block address if size addresses end of the device */ - if (ofs + len == mtd->size) - len -= mtd->erasesize; - - nand_get_device(mtd, FL_UNLOCKING); - - /* Shift to get chip number */ - chipnr = ofs >> chip->chip_shift; - - /* - * Reset the chip. - * If we want to check the WP through READ STATUS and check the bit 7 - * we must reset the chip - * some operation can also clear the bit 7 of status register - * eg. erase/program a locked block - */ - nand_reset(chip, chipnr); - - chip->select_chip(mtd, chipnr); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - pr_debug("%s: device is write protected!\n", - __func__); - ret = -EIO; - goto out; - } - - ret = __nand_unlock(mtd, ofs, len, 0); - -out: - chip->select_chip(mtd, -1); - nand_release_device(mtd); - - return ret; -} -EXPORT_SYMBOL(nand_unlock); - -/** - * nand_lock - [REPLACEABLE] locks all blocks present in the device - * @mtd: mtd info - * @ofs: offset to start unlock from - * @len: length to unlock - * - * This feature is not supported in many NAND parts. 'Micron' NAND parts do - * have this feature, but it allows only to lock all blocks, not for specified - * range for block. Implementing 'lock' feature by making use of 'unlock', for - * now. - * - * Returns lock status. - */ -int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - int ret = 0; - int chipnr, status, page; - struct nand_chip *chip = mtd_to_nand(mtd); - - pr_debug("%s: start = 0x%012llx, len = %llu\n", - __func__, (unsigned long long)ofs, len); - - if (check_offs_len(mtd, ofs, len)) - return -EINVAL; - - nand_get_device(mtd, FL_LOCKING); - - /* Shift to get chip number */ - chipnr = ofs >> chip->chip_shift; - - /* - * Reset the chip. - * If we want to check the WP through READ STATUS and check the bit 7 - * we must reset the chip - * some operation can also clear the bit 7 of status register - * eg. erase/program a locked block - */ - nand_reset(chip, chipnr); - - chip->select_chip(mtd, chipnr); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - pr_debug("%s: device is write protected!\n", - __func__); - status = MTD_ERASE_FAILED; - ret = -EIO; - goto out; - } - - /* Submit address of first page to lock */ - page = ofs >> chip->page_shift; - chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask); - - /* Call wait ready function */ - status = chip->waitfunc(mtd, chip); - /* See if device thinks it succeeded */ - if (status & NAND_STATUS_FAIL) { - pr_debug("%s: error status = 0x%08x\n", - __func__, status); - ret = -EIO; - goto out; - } - - ret = __nand_unlock(mtd, ofs, len, 0x1); - -out: - chip->select_chip(mtd, -1); - nand_release_device(mtd); - - return ret; -} -EXPORT_SYMBOL(nand_lock); - -/** * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data * @buf: buffer to test * @len: buffer length @@ -3993,10 +3820,13 @@ static void nand_manufacturer_detect(struct nand_chip *chip) * nand_decode_ext_id() otherwise. */ if (chip->manufacturer.desc && chip->manufacturer.desc->ops && - chip->manufacturer.desc->ops->detect) + chip->manufacturer.desc->ops->detect) { + /* The 3rd id byte holds MLC / multichip data */ + chip->bits_per_cell = nand_get_bits_per_cell(chip->id.data[2]); chip->manufacturer.desc->ops->detect(chip); - else + } else { nand_decode_ext_id(chip); + } } /* @@ -4036,7 +3866,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) const struct nand_manufacturer *manufacturer; struct mtd_info *mtd = nand_to_mtd(chip); int busw; - int i, ret; + int i; u8 *id_data = chip->id.data; u8 maf_id, dev_id; @@ -4066,7 +3896,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read entire ID string */ - for (i = 0; i < 8; i++) + for (i = 0; i < ARRAY_SIZE(chip->id.data); i++) id_data[i] = chip->read_byte(mtd); if (id_data[0] != maf_id || id_data[1] != dev_id) { @@ -4075,7 +3905,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) return -ENODEV; } - chip->id.len = nand_id_len(id_data, 8); + chip->id.len = nand_id_len(id_data, ARRAY_SIZE(chip->id.data)); /* Try to identify manufacturer */ manufacturer = nand_get_manufacturer(maf_id); @@ -4177,10 +4007,6 @@ ident_done: if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; - ret = nand_manufacturer_init(chip); - if (ret) - return ret; - pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", maf_id, dev_id); @@ -4388,23 +4214,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, return ret; } - /* Initialize the ->data_interface field. */ - ret = nand_init_data_interface(chip); - if (ret) - goto err_nand_init; - - /* - * Setup the data interface correctly on the chip and controller side. - * This explicit call to nand_setup_data_interface() is only required - * for the first die, because nand_reset() has been called before - * ->data_interface and ->default_onfi_timing_mode were set. - * For the other dies, nand_reset() will automatically switch to the - * best mode for us. - */ - ret = nand_setup_data_interface(chip, 0); - if (ret) - goto err_nand_init; - nand_maf_id = chip->id.data[0]; nand_dev_id = chip->id.data[1]; @@ -4434,12 +4243,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, mtd->size = i * chip->chipsize; return 0; - -err_nand_init: - /* Free manufacturer priv data. */ - nand_manufacturer_cleanup(chip); - - return ret; } EXPORT_SYMBOL(nand_scan_ident); @@ -4826,55 +4629,60 @@ int nand_scan_tail(struct mtd_info *mtd) struct nand_chip *chip = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &chip->ecc; struct nand_buffers *nbuf = NULL; - int ret; + int ret, i; /* New bad blocks should be marked in OOB, flash-based BBT, or both */ if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && !(chip->bbt_options & NAND_BBT_USE_FLASH))) { - ret = -EINVAL; - goto err_ident; + return -EINVAL; } if (invalid_ecc_page_accessors(chip)) { pr_err("Invalid ECC page accessors setup\n"); - ret = -EINVAL; - goto err_ident; + return -EINVAL; } if (!(chip->options & NAND_OWN_BUFFERS)) { nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL); - if (!nbuf) { - ret = -ENOMEM; - goto err_ident; - } + if (!nbuf) + return -ENOMEM; nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL); if (!nbuf->ecccalc) { ret = -ENOMEM; - goto err_free; + goto err_free_nbuf; } nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL); if (!nbuf->ecccode) { ret = -ENOMEM; - goto err_free; + goto err_free_nbuf; } nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); if (!nbuf->databuf) { ret = -ENOMEM; - goto err_free; + goto err_free_nbuf; } chip->buffers = nbuf; - } else { - if (!chip->buffers) { - ret = -ENOMEM; - goto err_ident; - } + } else if (!chip->buffers) { + return -ENOMEM; } + /* + * FIXME: some NAND manufacturer drivers expect the first die to be + * selected when manufacturer->init() is called. They should be fixed + * to explictly select the relevant die when interacting with the NAND + * chip. + */ + chip->select_chip(mtd, 0); + ret = nand_manufacturer_init(chip); + chip->select_chip(mtd, -1); + if (ret) + goto err_free_nbuf; + /* Set the internal oob buffer location, just after the page data */ chip->oob_poi = chip->buffers->databuf + mtd->writesize; @@ -4896,7 +4704,7 @@ int nand_scan_tail(struct mtd_info *mtd) WARN(1, "No oob scheme defined for oobsize %d\n", mtd->oobsize); ret = -EINVAL; - goto err_free; + goto err_nand_manuf_cleanup; } } @@ -4911,7 +4719,7 @@ int nand_scan_tail(struct mtd_info *mtd) if (!ecc->calculate || !ecc->correct || !ecc->hwctl) { WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); ret = -EINVAL; - goto err_free; + goto err_nand_manuf_cleanup; } if (!ecc->read_page) ecc->read_page = nand_read_page_hwecc_oob_first; @@ -4943,7 +4751,7 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->write_page == nand_write_page_hwecc)) { WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); ret = -EINVAL; - goto err_free; + goto err_nand_manuf_cleanup; } /* Use standard syndrome read/write page function? */ if (!ecc->read_page) @@ -4963,7 +4771,7 @@ int nand_scan_tail(struct mtd_info *mtd) if (!ecc->strength) { WARN(1, "Driver must set ecc.strength when using hardware ECC\n"); ret = -EINVAL; - goto err_free; + goto err_nand_manuf_cleanup; } break; } @@ -4976,7 +4784,7 @@ int nand_scan_tail(struct mtd_info *mtd) ret = nand_set_ecc_soft_ops(mtd); if (ret) { ret = -EINVAL; - goto err_free; + goto err_nand_manuf_cleanup; } break; @@ -4984,7 +4792,7 @@ int nand_scan_tail(struct mtd_info *mtd) if (!ecc->read_page || !ecc->write_page) { WARN(1, "No ECC functions supplied; on-die ECC not possible\n"); ret = -EINVAL; - goto err_free; + goto err_nand_manuf_cleanup; } if (!ecc->read_oob) ecc->read_oob = nand_read_oob_std; @@ -5008,7 +4816,7 @@ int nand_scan_tail(struct mtd_info *mtd) default: WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode); ret = -EINVAL; - goto err_free; + goto err_nand_manuf_cleanup; } /* For many systems, the standard OOB write also works for raw */ @@ -5029,13 +4837,13 @@ int nand_scan_tail(struct mtd_info *mtd) if (ecc->steps * ecc->size != mtd->writesize) { WARN(1, "Invalid ECC parameters\n"); ret = -EINVAL; - goto err_free; + goto err_nand_manuf_cleanup; } ecc->total = ecc->steps * ecc->bytes; if (ecc->total > mtd->oobsize) { WARN(1, "Total number of ECC bytes exceeded oobsize\n"); ret = -EINVAL; - goto err_free; + goto err_nand_manuf_cleanup; } /* @@ -5117,6 +4925,21 @@ int nand_scan_tail(struct mtd_info *mtd) if (!mtd->bitflip_threshold) mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4); + /* Initialize the ->data_interface field. */ + ret = nand_init_data_interface(chip); + if (ret) + goto err_nand_manuf_cleanup; + + /* Enter fastest possible mode on all dies. */ + for (i = 0; i < chip->numchips; i++) { + chip->select_chip(mtd, i); + ret = nand_setup_data_interface(chip, i); + chip->select_chip(mtd, -1); + + if (ret) + goto err_nand_data_iface_cleanup; + } + /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) return 0; @@ -5124,10 +4947,17 @@ int nand_scan_tail(struct mtd_info *mtd) /* Build bad block table */ ret = chip->scan_bbt(mtd); if (ret) - goto err_free; + goto err_nand_data_iface_cleanup; + return 0; -err_free: +err_nand_data_iface_cleanup: + nand_release_data_interface(chip); + +err_nand_manuf_cleanup: + nand_manufacturer_cleanup(chip); + +err_free_nbuf: if (nbuf) { kfree(nbuf->databuf); kfree(nbuf->ecccode); @@ -5135,12 +4965,6 @@ err_free: kfree(nbuf); } -err_ident: - /* Clean up nand_scan_ident(). */ - - /* Free manufacturer priv data. */ - nand_manufacturer_cleanup(chip); - return ret; } EXPORT_SYMBOL(nand_scan_tail); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 7695efea65f2..2915b6739bf8 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -61,7 +61,7 @@ #include <linux/types.h> #include <linux/mtd/mtd.h> #include <linux/mtd/bbm.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/vmalloc.h> diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c index 44763f87eae4..505441c9373b 100644 --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/nand_bch.c @@ -25,7 +25,7 @@ #include <linux/slab.h> #include <linux/bitops.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_bch.h> #include <linux/bch.h> diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index d1770b066396..7613a0388044 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -43,7 +43,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <asm/byteorder.h> #else diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c index b12dc7325378..985751eda317 100644 --- a/drivers/mtd/nand/nand_hynix.c +++ b/drivers/mtd/nand/nand_hynix.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/sizes.h> #include <linux/slab.h> @@ -477,7 +477,7 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip, * The ECC requirements field meaning depends on the * NAND technology. */ - u8 nand_tech = chip->id.data[5] & 0x3; + u8 nand_tech = chip->id.data[5] & 0x7; if (nand_tech < 3) { /* > 26nm, reference: H27UBG8T2A datasheet */ @@ -533,7 +533,7 @@ static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip, if (nand_tech > 0) chip->options |= NAND_NEED_SCRAMBLING; } else { - nand_tech = chip->id.data[5] & 0x3; + nand_tech = chip->id.data[5] & 0x7; /* < 32nm */ if (nand_tech > 2) diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 92e2cf8e9ff9..5423c3bb388e 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -6,7 +6,7 @@ * published by the Free Software Foundation. * */ -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/sizes.h> #define LP_OPTIONS 0 diff --git a/drivers/mtd/nand/nand_macronix.c b/drivers/mtd/nand/nand_macronix.c index 84855c3e1a02..d290ff2a6d2f 100644 --- a/drivers/mtd/nand/nand_macronix.c +++ b/drivers/mtd/nand/nand_macronix.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> static int macronix_nand_init(struct nand_chip *chip) { diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/nand_micron.c index c30ab60f8e1b..abf6a3c376e8 100644 --- a/drivers/mtd/nand/nand_micron.c +++ b/drivers/mtd/nand/nand_micron.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> /* * Special Micron status bit that indicates when the block has been diff --git a/drivers/mtd/nand/nand_samsung.c b/drivers/mtd/nand/nand_samsung.c index 1e0755997762..d348f0129ae7 100644 --- a/drivers/mtd/nand/nand_samsung.c +++ b/drivers/mtd/nand/nand_samsung.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> static void samsung_nand_decode_id(struct nand_chip *chip) { diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c index 7e36d7d13c26..5d1533bcc5bd 100644 --- a/drivers/mtd/nand/nand_timings.c +++ b/drivers/mtd/nand/nand_timings.c @@ -11,7 +11,7 @@ #include <linux/kernel.h> #include <linux/err.h> #include <linux/export.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> static const struct nand_data_interface onfi_sdr_timings[] = { /* Mode 0 */ diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/nand_toshiba.c index fa787ba38dcd..57df857074e6 100644 --- a/drivers/mtd/nand/nand_toshiba.c +++ b/drivers/mtd/nand/nand_toshiba.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> static void toshiba_nand_decode_id(struct nand_chip *chip) { diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index e4211c3cc49b..fec613221958 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -33,7 +33,7 @@ #include <linux/errno.h> #include <linux/string.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_bch.h> #include <linux/mtd/partitions.h> #include <linux/delay.h> @@ -287,11 +287,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " /* Maximum page cache pages needed to read or write a NAND page to the cache_file */ #define NS_MAX_HELD_PAGES 16 -struct nandsim_debug_info { - struct dentry *dfs_root; - struct dentry *dfs_wear_report; -}; - /* * A union to represent flash memory contents and flash buffer. */ @@ -370,8 +365,6 @@ struct nandsim { void *file_buf; struct page *held_pages[NS_MAX_HELD_PAGES]; int held_cnt; - - struct nandsim_debug_info dbg; }; /* @@ -524,39 +517,23 @@ static const struct file_operations dfs_fops = { */ static int nandsim_debugfs_create(struct nandsim *dev) { - struct nandsim_debug_info *dbg = &dev->dbg; + struct dentry *root = nsmtd->dbg.dfs_dir; struct dentry *dent; if (!IS_ENABLED(CONFIG_DEBUG_FS)) return 0; - dent = debugfs_create_dir("nandsim", NULL); - if (!dent) { - NS_ERR("cannot create \"nandsim\" debugfs directory\n"); - return -ENODEV; - } - dbg->dfs_root = dent; + if (IS_ERR_OR_NULL(root)) + return -1; - dent = debugfs_create_file("wear_report", S_IRUSR, - dbg->dfs_root, dev, &dfs_fops); - if (!dent) - goto out_remove; - dbg->dfs_wear_report = dent; + dent = debugfs_create_file("nandsim_wear_report", S_IRUSR, + root, dev, &dfs_fops); + if (IS_ERR_OR_NULL(dent)) { + NS_ERR("cannot create \"nandsim_wear_report\" debugfs entry\n"); + return -1; + } return 0; - -out_remove: - debugfs_remove_recursive(dbg->dfs_root); - return -ENODEV; -} - -/** - * nandsim_debugfs_remove - destroy all debugfs files - */ -static void nandsim_debugfs_remove(struct nandsim *ns) -{ - if (IS_ENABLED(CONFIG_DEBUG_FS)) - debugfs_remove_recursive(ns->dbg.dfs_root); } /* @@ -2352,9 +2329,6 @@ static int __init ns_init_module(void) if ((retval = setup_wear_reporting(nsmtd)) != 0) goto err_exit; - if ((retval = nandsim_debugfs_create(nand)) != 0) - goto err_exit; - if ((retval = init_nandsim(nsmtd)) != 0) goto err_exit; @@ -2370,10 +2344,12 @@ static int __init ns_init_module(void) if (retval != 0) goto err_exit; + if ((retval = nandsim_debugfs_create(nand)) != 0) + goto err_exit; + return 0; err_exit: - nandsim_debugfs_remove(nand); free_nandsim(nand); nand_release(nsmtd); for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i) @@ -2396,7 +2372,6 @@ static void __exit ns_cleanup_module(void) struct nandsim *ns = nand_get_controller_data(chip); int i; - nandsim_debugfs_remove(ns); free_nandsim(ns); /* Free nandsim private resources */ nand_release(nsmtd); /* Unregister driver */ for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i) diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 28e6118362f7..d8a806894937 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -22,7 +22,7 @@ * */ #include <linux/module.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> #include <linux/mtd/ndfc.h> diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 8f64011d32ef..7bb4d2ea9342 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -19,7 +19,7 @@ #include <linux/err.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #define REG_FMICSR 0x00 diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 084934a9f19c..54540c8fa1a2 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -18,7 +18,7 @@ #include <linux/jiffies.h> #include <linux/sched.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/omap-dma.h> #include <linux/io.h> diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 209170ed2b76..5a5aa1f07d07 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -15,7 +15,7 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/clk.h> #include <linux/err.h> @@ -54,13 +54,16 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { struct nand_chip *chip = mtd_to_nand(mtd); void __iomem *io_base = chip->IO_ADDR_R; +#if __LINUX_ARM_ARCH__ >= 5 uint64_t *buf64; +#endif int i = 0; while (len && (unsigned long)buf & 7) { *buf++ = readb(io_base); len--; } +#if __LINUX_ARM_ARCH__ >= 5 buf64 = (uint64_t *)buf; while (i < len/8) { /* @@ -74,6 +77,10 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) buf64[i++] = x; } i *= 8; +#else + readsl(io_base, buf, len/4); + i = len / 4 * 4; +#endif while (i < len) buf[i++] = readb(io_base); } diff --git a/drivers/mtd/nand/oxnas_nand.c b/drivers/mtd/nand/oxnas_nand.c index 1b207aac840c..d649d5944826 100644 --- a/drivers/mtd/nand/oxnas_nand.c +++ b/drivers/mtd/nand/oxnas_nand.c @@ -21,7 +21,7 @@ #include <linux/clk.h> #include <linux/reset.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/of.h> @@ -112,14 +112,19 @@ static int oxnas_nand_probe(struct platform_device *pdev) if (count > 1) return -EINVAL; - clk_prepare_enable(oxnas->clk); + err = clk_prepare_enable(oxnas->clk); + if (err) + return err; + device_reset_optional(&pdev->dev); for_each_child_of_node(np, nand_np) { chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; + if (!chip) { + err = -ENOMEM; + goto err_clk_unprepare; + } chip->controller = &oxnas->base; @@ -139,12 +144,12 @@ static int oxnas_nand_probe(struct platform_device *pdev) /* Scan to find existence of the device */ err = nand_scan(mtd, 1); if (err) - return err; + goto err_clk_unprepare; err = mtd_device_register(mtd, NULL, 0); if (err) { nand_release(mtd); - return err; + goto err_clk_unprepare; } oxnas->chips[nchips] = chip; @@ -152,12 +157,18 @@ static int oxnas_nand_probe(struct platform_device *pdev) } /* Exit if no chips found */ - if (!nchips) - return -ENODEV; + if (!nchips) { + err = -ENODEV; + goto err_clk_unprepare; + } platform_set_drvdata(pdev, oxnas); return 0; + +err_clk_unprepare: + clk_disable_unprepare(oxnas->clk); + return err; } static int oxnas_nand_remove(struct platform_device *pdev) diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 074b8b01289e..a47a7e4bd25a 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -25,7 +25,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/of_address.h> #include <linux/of_irq.h> diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 791de3e4bbb6..925a1323604d 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -15,7 +15,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> struct plat_nand_data { diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 74dae4bbdac8..85cff68643e0 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -21,7 +21,7 @@ #include <linux/delay.h> #include <linux/clk.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/io.h> #include <linux/iopoll.h> diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c index 88af7145a51a..3baddfc997d1 100644 --- a/drivers/mtd/nand/qcom_nandc.c +++ b/drivers/mtd/nand/qcom_nandc.c @@ -17,7 +17,7 @@ #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/module.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/of.h> #include <linux/of_device.h> @@ -53,6 +53,8 @@ #define NAND_VERSION 0xf08 #define NAND_READ_LOCATION_0 0xf20 #define NAND_READ_LOCATION_1 0xf24 +#define NAND_READ_LOCATION_2 0xf28 +#define NAND_READ_LOCATION_3 0xf2c /* dummy register offsets, used by write_reg_dma */ #define NAND_DEV_CMD1_RESTORE 0xdead @@ -109,7 +111,11 @@ #define READ_ADDR 0 /* NAND_DEV_CMD_VLD bits */ -#define READ_START_VLD 0 +#define READ_START_VLD BIT(0) +#define READ_STOP_VLD BIT(1) +#define WRITE_START_VLD BIT(2) +#define ERASE_START_VLD BIT(3) +#define SEQ_READ_START_VLD BIT(4) /* NAND_EBI2_ECC_BUF_CFG bits */ #define NUM_STEPS 0 @@ -131,6 +137,11 @@ #define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) #define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) +/* NAND_READ_LOCATION_n bits */ +#define READ_LOCATION_OFFSET 0 +#define READ_LOCATION_SIZE 16 +#define READ_LOCATION_LAST 31 + /* Version Mask */ #define NAND_VERSION_MAJOR_MASK 0xf0000000 #define NAND_VERSION_MAJOR_SHIFT 28 @@ -148,6 +159,13 @@ #define FETCH_ID 0xb #define RESET_DEVICE 0xd +/* Default Value for NAND_DEV_CMD_VLD */ +#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ + ERASE_START_VLD | SEQ_READ_START_VLD) + +/* NAND_CTRL bits */ +#define BAM_MODE_EN BIT(0) + /* * the NAND controller performs reads/writes with ECC in 516 byte chunks. * the driver calls the chunks 'step' or 'codeword' interchangeably @@ -169,11 +187,81 @@ #define ECC_BCH_4BIT BIT(2) #define ECC_BCH_8BIT BIT(3) +#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \ +nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ + ((offset) << READ_LOCATION_OFFSET) | \ + ((size) << READ_LOCATION_SIZE) | \ + ((is_last) << READ_LOCATION_LAST)) + +/* + * Returns the actual register address for all NAND_DEV_ registers + * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) + */ +#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) + +#define QPIC_PER_CW_CMD_SGL 32 +#define QPIC_PER_CW_DATA_SGL 8 + +/* + * Flags used in DMA descriptor preparation helper functions + * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) + */ +/* Don't set the EOT in current tx BAM sgl */ +#define NAND_BAM_NO_EOT BIT(0) +/* Set the NWD flag in current BAM sgl */ +#define NAND_BAM_NWD BIT(1) +/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ +#define NAND_BAM_NEXT_SGL BIT(2) +/* + * Erased codeword status is being used two times in single transfer so this + * flag will determine the current value of erased codeword status register + */ +#define NAND_ERASED_CW_SET BIT(4) + +/* + * This data type corresponds to the BAM transaction which will be used for all + * NAND transfers. + * @cmd_sgl - sgl for NAND BAM command pipe + * @data_sgl - sgl for NAND BAM consumer/producer pipe + * @cmd_sgl_pos - current index in command sgl. + * @cmd_sgl_start - start index in command sgl. + * @tx_sgl_pos - current index in data sgl for tx. + * @tx_sgl_start - start index in data sgl for tx. + * @rx_sgl_pos - current index in data sgl for rx. + * @rx_sgl_start - start index in data sgl for rx. + */ +struct bam_transaction { + struct scatterlist *cmd_sgl; + struct scatterlist *data_sgl; + u32 cmd_sgl_pos; + u32 cmd_sgl_start; + u32 tx_sgl_pos; + u32 tx_sgl_start; + u32 rx_sgl_pos; + u32 rx_sgl_start; +}; + +/* + * This data type corresponds to the nand dma descriptor + * @list - list for desc_info + * @dir - DMA transfer direction + * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by + * ADM + * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM + * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM + * @dma_desc - low level DMA engine descriptor + */ struct desc_info { struct list_head node; enum dma_data_direction dir; - struct scatterlist sgl; + union { + struct scatterlist adm_sgl; + struct { + struct scatterlist *bam_sgl; + int sgl_cnt; + }; + }; struct dma_async_tx_descriptor *dma_desc; }; @@ -202,6 +290,13 @@ struct nandc_regs { __le32 orig_vld; __le32 ecc_buf_cfg; + __le32 read_location0; + __le32 read_location1; + __le32 read_location2; + __le32 read_location3; + + __le32 erased_cw_detect_cfg_clr; + __le32 erased_cw_detect_cfg_set; }; /* @@ -226,14 +321,17 @@ struct nandc_regs { * by upper layers directly * @buf_size/count/start: markers for chip->read_buf/write_buf functions * @reg_read_buf: local buffer for reading back registers via DMA + * @reg_read_dma: contains dma address for register read buffer * @reg_read_pos: marker for data read in reg_read_buf * * @regs: a contiguous chunk of memory for DMA register * writes. contains the register values to be * written to controller * @cmd1/vld: some fixed controller register values - * @ecc_modes: supported ECC modes by the current controller, + * @props: properties of current NAND controller, * initialized via DT match data + * @max_cwperpage: maximum QPIC codewords required. calculated + * from all connected NAND devices pagesize */ struct qcom_nand_controller { struct nand_hw_control controller; @@ -247,23 +345,39 @@ struct qcom_nand_controller { struct clk *core_clk; struct clk *aon_clk; - struct dma_chan *chan; - unsigned int cmd_crci; - unsigned int data_crci; + union { + /* will be used only by QPIC for BAM DMA */ + struct { + struct dma_chan *tx_chan; + struct dma_chan *rx_chan; + struct dma_chan *cmd_chan; + }; + + /* will be used only by EBI2 for ADM DMA */ + struct { + struct dma_chan *chan; + unsigned int cmd_crci; + unsigned int data_crci; + }; + }; + struct list_head desc_list; + struct bam_transaction *bam_txn; u8 *data_buffer; int buf_size; int buf_count; int buf_start; + unsigned int max_cwperpage; __le32 *reg_read_buf; + dma_addr_t reg_read_dma; int reg_read_pos; struct nandc_regs *regs; u32 cmd1, vld; - u32 ecc_modes; + const struct qcom_nandc_props *props; }; /* @@ -316,6 +430,78 @@ struct qcom_nand_host { u32 clrreadstatus; }; +/* + * This data type corresponds to the NAND controller properties which varies + * among different NAND controllers. + * @ecc_modes - ecc mode for NAND + * @is_bam - whether NAND controller is using BAM + * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset + */ +struct qcom_nandc_props { + u32 ecc_modes; + bool is_bam; + u32 dev_cmd_reg_start; +}; + +/* Frees the BAM transaction memory */ +static void free_bam_transaction(struct qcom_nand_controller *nandc) +{ + struct bam_transaction *bam_txn = nandc->bam_txn; + + devm_kfree(nandc->dev, bam_txn); +} + +/* Allocates and Initializes the BAM transaction */ +static struct bam_transaction * +alloc_bam_transaction(struct qcom_nand_controller *nandc) +{ + struct bam_transaction *bam_txn; + size_t bam_txn_size; + unsigned int num_cw = nandc->max_cwperpage; + void *bam_txn_buf; + + bam_txn_size = + sizeof(*bam_txn) + num_cw * + ((sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + + (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); + + bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL); + if (!bam_txn_buf) + return NULL; + + bam_txn = bam_txn_buf; + bam_txn_buf += sizeof(*bam_txn); + + bam_txn->cmd_sgl = bam_txn_buf; + bam_txn_buf += + sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; + + bam_txn->data_sgl = bam_txn_buf; + + return bam_txn; +} + +/* Clears the BAM transaction indexes */ +static void clear_bam_transaction(struct qcom_nand_controller *nandc) +{ + struct bam_transaction *bam_txn = nandc->bam_txn; + + if (!nandc->props->is_bam) + return; + + bam_txn->cmd_sgl_pos = 0; + bam_txn->cmd_sgl_start = 0; + bam_txn->tx_sgl_pos = 0; + bam_txn->tx_sgl_start = 0; + bam_txn->rx_sgl_pos = 0; + bam_txn->rx_sgl_start = 0; + + sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * + QPIC_PER_CW_CMD_SGL); + sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * + QPIC_PER_CW_DATA_SGL); +} + static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) { return container_of(chip, struct qcom_nand_host, chip); @@ -339,6 +525,24 @@ static inline void nandc_write(struct qcom_nand_controller *nandc, int offset, iowrite32(val, nandc->base + offset); } +static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc, + bool is_cpu) +{ + if (!nandc->props->is_bam) + return; + + if (is_cpu) + dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, + MAX_REG_RD * + sizeof(*nandc->reg_read_buf), + DMA_FROM_DEVICE); + else + dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, + MAX_REG_RD * + sizeof(*nandc->reg_read_buf), + DMA_FROM_DEVICE); +} + static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) { switch (offset) { @@ -372,6 +576,14 @@ static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) return ®s->orig_vld; case NAND_EBI2_ECC_BUF_CFG: return ®s->ecc_buf_cfg; + case NAND_READ_LOCATION_0: + return ®s->read_location0; + case NAND_READ_LOCATION_1: + return ®s->read_location1; + case NAND_READ_LOCATION_2: + return ®s->read_location2; + case NAND_READ_LOCATION_3: + return ®s->read_location3; default: return NULL; } @@ -446,11 +658,119 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + + if (read) + nandc_set_read_loc(nandc, 0, 0, host->use_ecc ? + host->cw_data : host->cw_size, 1); +} + +/* + * Maps the scatter gather list for DMA transfer and forms the DMA descriptor + * for BAM. This descriptor will be added in the NAND DMA descriptor queue + * which will be submitted to DMA engine. + */ +static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, + struct dma_chan *chan, + unsigned long flags) +{ + struct desc_info *desc; + struct scatterlist *sgl; + unsigned int sgl_cnt; + int ret; + struct bam_transaction *bam_txn = nandc->bam_txn; + enum dma_transfer_direction dir_eng; + struct dma_async_tx_descriptor *dma_desc; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + if (chan == nandc->cmd_chan) { + sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; + sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; + bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; + dir_eng = DMA_MEM_TO_DEV; + desc->dir = DMA_TO_DEVICE; + } else if (chan == nandc->tx_chan) { + sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; + sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; + bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; + dir_eng = DMA_MEM_TO_DEV; + desc->dir = DMA_TO_DEVICE; + } else { + sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; + sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; + bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; + dir_eng = DMA_DEV_TO_MEM; + desc->dir = DMA_FROM_DEVICE; + } + + sg_mark_end(sgl + sgl_cnt - 1); + ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); + if (ret == 0) { + dev_err(nandc->dev, "failure in mapping desc\n"); + kfree(desc); + return -ENOMEM; + } + + desc->sgl_cnt = sgl_cnt; + desc->bam_sgl = sgl; + + dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, + flags); + + if (!dma_desc) { + dev_err(nandc->dev, "failure in prep desc\n"); + dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); + kfree(desc); + return -EINVAL; + } + + desc->dma_desc = dma_desc; + + list_add_tail(&desc->node, &nandc->desc_list); + + return 0; +} + +/* + * Prepares the data descriptor for BAM DMA which will be used for NAND + * data reads and writes. + */ +static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, + const void *vaddr, + int size, unsigned int flags) +{ + int ret; + struct bam_transaction *bam_txn = nandc->bam_txn; + + if (read) { + sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], + vaddr, size); + bam_txn->rx_sgl_pos++; + } else { + sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], + vaddr, size); + bam_txn->tx_sgl_pos++; + + /* + * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag + * is not set, form the DMA descriptor + */ + if (!(flags & NAND_BAM_NO_EOT)) { + ret = prepare_bam_async_desc(nandc, nandc->tx_chan, + DMA_PREP_INTERRUPT); + if (ret) + return ret; + } + } + + return 0; } -static int prep_dma_desc(struct qcom_nand_controller *nandc, bool read, - int reg_off, const void *vaddr, int size, - bool flow_control) +static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, + int reg_off, const void *vaddr, int size, + bool flow_control) { struct desc_info *desc; struct dma_async_tx_descriptor *dma_desc; @@ -463,7 +783,7 @@ static int prep_dma_desc(struct qcom_nand_controller *nandc, bool read, if (!desc) return -ENOMEM; - sgl = &desc->sgl; + sgl = &desc->adm_sgl; sg_init_one(sgl, vaddr, size); @@ -524,9 +844,10 @@ err: * * @first: offset of the first register in the contiguous block * @num_regs: number of registers to read + * @flags: flags to control DMA descriptor preparation */ static int read_reg_dma(struct qcom_nand_controller *nandc, int first, - int num_regs) + int num_regs, unsigned int flags) { bool flow_control = false; void *vaddr; @@ -535,11 +856,14 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first, if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) flow_control = true; + if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) + first = dev_cmd_reg_addr(nandc, first); + size = num_regs * sizeof(u32); vaddr = nandc->reg_read_buf + nandc->reg_read_pos; nandc->reg_read_pos += num_regs; - return prep_dma_desc(nandc, true, first, vaddr, size, flow_control); + return prep_adm_dma_desc(nandc, true, first, vaddr, size, flow_control); } /* @@ -548,9 +872,10 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first, * * @first: offset of the first register in the contiguous block * @num_regs: number of registers to write + * @flags: flags to control DMA descriptor preparation */ static int write_reg_dma(struct qcom_nand_controller *nandc, int first, - int num_regs) + int num_regs, unsigned int flags) { bool flow_control = false; struct nandc_regs *regs = nandc->regs; @@ -562,15 +887,26 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first, if (first == NAND_FLASH_CMD) flow_control = true; - if (first == NAND_DEV_CMD1_RESTORE) - first = NAND_DEV_CMD1; + if (first == NAND_ERASED_CW_DETECT_CFG) { + if (flags & NAND_ERASED_CW_SET) + vaddr = ®s->erased_cw_detect_cfg_set; + else + vaddr = ®s->erased_cw_detect_cfg_clr; + } + + if (first == NAND_EXEC_CMD) + flags |= NAND_BAM_NWD; + + if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) + first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); - if (first == NAND_DEV_CMD_VLD_RESTORE) - first = NAND_DEV_CMD_VLD; + if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) + first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); size = num_regs * sizeof(u32); - return prep_dma_desc(nandc, false, first, vaddr, size, flow_control); + return prep_adm_dma_desc(nandc, false, first, vaddr, size, + flow_control); } /* @@ -580,11 +916,15 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first, * @reg_off: offset within the controller's data buffer * @vaddr: virtual address of the buffer we want to write to * @size: DMA transaction size in bytes + * @flags: flags to control DMA descriptor preparation */ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, - const u8 *vaddr, int size) + const u8 *vaddr, int size, unsigned int flags) { - return prep_dma_desc(nandc, true, reg_off, vaddr, size, false); + if (nandc->props->is_bam) + return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); + + return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); } /* @@ -594,48 +934,84 @@ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, * @reg_off: offset within the controller's data buffer * @vaddr: virtual address of the buffer we want to read from * @size: DMA transaction size in bytes + * @flags: flags to control DMA descriptor preparation */ static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, - const u8 *vaddr, int size) + const u8 *vaddr, int size, unsigned int flags) { - return prep_dma_desc(nandc, false, reg_off, vaddr, size, false); + if (nandc->props->is_bam) + return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); + + return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); } /* - * helper to prepare dma descriptors to configure registers needed for reading a - * codeword/step in a page + * Helper to prepare DMA descriptors for configuring registers + * before reading a NAND page. */ -static void config_cw_read(struct qcom_nand_controller *nandc) +static void config_nand_page_read(struct qcom_nand_controller *nandc) { - write_reg_dma(nandc, NAND_FLASH_CMD, 3); - write_reg_dma(nandc, NAND_DEV0_CFG0, 3); - write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1); + write_reg_dma(nandc, NAND_ADDR0, 2, 0); + write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); + write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); + write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); + write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, + NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); +} - write_reg_dma(nandc, NAND_EXEC_CMD, 1); +/* + * Helper to prepare DMA descriptors for configuring registers + * before reading each codeword in NAND page. + */ +static void config_nand_cw_read(struct qcom_nand_controller *nandc) +{ + if (nandc->props->is_bam) + write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, + NAND_BAM_NEXT_SGL); - read_reg_dma(nandc, NAND_FLASH_STATUS, 2); - read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1); + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); + read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, + NAND_BAM_NEXT_SGL); } /* - * helpers to prepare dma descriptors used to configure registers needed for - * writing a codeword/step in a page + * Helper to prepare dma descriptors to configure registers needed for reading a + * single codeword in page */ -static void config_cw_write_pre(struct qcom_nand_controller *nandc) +static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc) { - write_reg_dma(nandc, NAND_FLASH_CMD, 3); - write_reg_dma(nandc, NAND_DEV0_CFG0, 3); - write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1); + config_nand_page_read(nandc); + config_nand_cw_read(nandc); } -static void config_cw_write_post(struct qcom_nand_controller *nandc) +/* + * Helper to prepare DMA descriptors used to configure registers needed for + * before writing a NAND page. + */ +static void config_nand_page_write(struct qcom_nand_controller *nandc) { - write_reg_dma(nandc, NAND_EXEC_CMD, 1); + write_reg_dma(nandc, NAND_ADDR0, 2, 0); + write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); + write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, + NAND_BAM_NEXT_SGL); +} - read_reg_dma(nandc, NAND_FLASH_STATUS, 1); +/* + * Helper to prepare DMA descriptors for configuring registers + * before writing each codeword in NAND page. + */ +static void config_nand_cw_write(struct qcom_nand_controller *nandc) +{ + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_FLASH_STATUS, 1); - write_reg_dma(nandc, NAND_READ_STATUS, 1); + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); + write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); } /* @@ -672,8 +1048,7 @@ static int nandc_param(struct qcom_nand_host *host) /* configure CMD1 and VLD for ONFI param probing */ nandc_set_reg(nandc, NAND_DEV_CMD_VLD, - (nandc->vld & ~(1 << READ_START_VLD)) - | 0 << READ_START_VLD); + (nandc->vld & ~READ_START_VLD)); nandc_set_reg(nandc, NAND_DEV_CMD1, (nandc->cmd1 & ~(0xFF << READ_ADDR)) | NAND_CMD_PARAM << READ_ADDR); @@ -682,21 +1057,22 @@ static int nandc_param(struct qcom_nand_host *host) nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1); nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); + nandc_set_read_loc(nandc, 0, 0, 512, 1); - write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1); - write_reg_dma(nandc, NAND_DEV_CMD1, 1); + write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); + write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); nandc->buf_count = 512; memset(nandc->data_buffer, 0xff, nandc->buf_count); - config_cw_read(nandc); + config_nand_single_cw_page_read(nandc); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, - nandc->buf_count); + nandc->buf_count, 0); /* restore CMD1 and VLD regs */ - write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1); - write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1); + write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); + write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); return 0; } @@ -718,14 +1094,14 @@ static int erase_block(struct qcom_nand_host *host, int page_addr) nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); - write_reg_dma(nandc, NAND_FLASH_CMD, 3); - write_reg_dma(nandc, NAND_DEV0_CFG0, 2); - write_reg_dma(nandc, NAND_EXEC_CMD, 1); + write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - read_reg_dma(nandc, NAND_FLASH_STATUS, 1); + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_FLASH_STATUS, 1); - write_reg_dma(nandc, NAND_READ_STATUS, 1); + write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); + write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); return 0; } @@ -742,13 +1118,14 @@ static int read_id(struct qcom_nand_host *host, int column) nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID); nandc_set_reg(nandc, NAND_ADDR0, column); nandc_set_reg(nandc, NAND_ADDR1, 0); - nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); + nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, + nandc->props->is_bam ? 0 : DM_EN); nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - write_reg_dma(nandc, NAND_FLASH_CMD, 4); - write_reg_dma(nandc, NAND_EXEC_CMD, 1); + write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - read_reg_dma(nandc, NAND_READ_ID, 1); + read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); return 0; } @@ -762,10 +1139,10 @@ static int reset(struct qcom_nand_host *host) nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE); nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - write_reg_dma(nandc, NAND_FLASH_CMD, 1); - write_reg_dma(nandc, NAND_EXEC_CMD, 1); + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - read_reg_dma(nandc, NAND_FLASH_STATUS, 1); + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); return 0; } @@ -775,12 +1152,43 @@ static int submit_descs(struct qcom_nand_controller *nandc) { struct desc_info *desc; dma_cookie_t cookie = 0; + struct bam_transaction *bam_txn = nandc->bam_txn; + int r; + + if (nandc->props->is_bam) { + if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { + r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); + if (r) + return r; + } + + if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { + r = prepare_bam_async_desc(nandc, nandc->tx_chan, + DMA_PREP_INTERRUPT); + if (r) + return r; + } + + if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { + r = prepare_bam_async_desc(nandc, nandc->cmd_chan, 0); + if (r) + return r; + } + } list_for_each_entry(desc, &nandc->desc_list, node) cookie = dmaengine_submit(desc->dma_desc); - if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) - return -ETIMEDOUT; + if (nandc->props->is_bam) { + dma_async_issue_pending(nandc->tx_chan); + dma_async_issue_pending(nandc->rx_chan); + + if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE) + return -ETIMEDOUT; + } else { + if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) + return -ETIMEDOUT; + } return 0; } @@ -791,7 +1199,14 @@ static void free_descs(struct qcom_nand_controller *nandc) list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { list_del(&desc->node); - dma_unmap_sg(nandc->dev, &desc->sgl, 1, desc->dir); + + if (nandc->props->is_bam) + dma_unmap_sg(nandc->dev, desc->bam_sgl, + desc->sgl_cnt, desc->dir); + else + dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, + desc->dir); + kfree(desc); } } @@ -800,8 +1215,7 @@ static void free_descs(struct qcom_nand_controller *nandc) static void clear_read_regs(struct qcom_nand_controller *nandc) { nandc->reg_read_pos = 0; - memset(nandc->reg_read_buf, 0, - MAX_REG_RD * sizeof(*nandc->reg_read_buf)); + nandc_read_buffer_sync(nandc, false); } static void pre_command(struct qcom_nand_host *host, int command) @@ -815,6 +1229,10 @@ static void pre_command(struct qcom_nand_host *host, int command) host->last_command = command; clear_read_regs(nandc); + + if (command == NAND_CMD_RESET || command == NAND_CMD_READID || + command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) + clear_bam_transaction(nandc); } /* @@ -831,6 +1249,7 @@ static void parse_erase_write_errors(struct qcom_nand_host *host, int command) int i; num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; + nandc_read_buffer_sync(nandc, true); for (i = 0; i < num_cw; i++) { u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]); @@ -852,6 +1271,7 @@ static void post_command(struct qcom_nand_host *host, int command) switch (command) { case NAND_CMD_READID: + nandc_read_buffer_sync(nandc, true); memcpy(nandc->data_buffer, nandc->reg_read_buf, nandc->buf_count); break; @@ -1015,6 +1435,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, int i; buf = (struct read_stats *)nandc->reg_read_buf; + nandc_read_buffer_sync(nandc, true); for (i = 0; i < ecc->steps; i++, buf++) { u32 flash, buffer, erased_cw; @@ -1102,6 +1523,8 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, struct nand_ecc_ctrl *ecc = &chip->ecc; int i, ret; + config_nand_page_read(nandc); + /* queue cmd descs for each codeword */ for (i = 0; i < ecc->steps; i++) { int data_size, oob_size; @@ -1115,11 +1538,24 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, oob_size = host->ecc_bytes_hw + host->spare_bytes; } - config_cw_read(nandc); + if (nandc->props->is_bam) { + if (data_buf && oob_buf) { + nandc_set_read_loc(nandc, 0, 0, data_size, 0); + nandc_set_read_loc(nandc, 1, data_size, + oob_size, 1); + } else if (data_buf) { + nandc_set_read_loc(nandc, 0, 0, data_size, 1); + } else { + nandc_set_read_loc(nandc, 0, data_size, + oob_size, 1); + } + } + + config_nand_cw_read(nandc); if (data_buf) read_data_dma(nandc, FLASH_BUF_ACC, data_buf, - data_size); + data_size, 0); /* * when ecc is enabled, the controller doesn't read the real @@ -1135,7 +1571,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, *oob_buf++ = 0xff; read_data_dma(nandc, FLASH_BUF_ACC + data_size, - oob_buf, oob_size); + oob_buf, oob_size, 0); } if (data_buf) @@ -1175,9 +1611,9 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, true); - config_cw_read(nandc); + config_nand_single_cw_page_read(nandc); - read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size); + read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); ret = submit_descs(nandc); if (ret) @@ -1200,6 +1636,7 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, data_buf = buf; oob_buf = oob_required ? chip->oob_poi : NULL; + clear_bam_transaction(nandc); ret = read_page_ecc(host, data_buf, oob_buf); if (ret) { dev_err(nandc->dev, "failure to read page\n"); @@ -1219,12 +1656,16 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, u8 *data_buf, *oob_buf; struct nand_ecc_ctrl *ecc = &chip->ecc; int i, ret; + int read_loc; data_buf = buf; oob_buf = chip->oob_poi; host->use_ecc = false; + + clear_bam_transaction(nandc); update_rw_regs(host, ecc->steps, true); + config_nand_page_read(nandc); for (i = 0; i < ecc->steps; i++) { int data_size1, data_size2, oob_size1, oob_size2; @@ -1243,21 +1684,35 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, oob_size2 = host->ecc_bytes_hw + host->spare_bytes; } - config_cw_read(nandc); + if (nandc->props->is_bam) { + read_loc = 0; + nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0); + read_loc += data_size1; + + nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0); + read_loc += oob_size1; + + nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0); + read_loc += data_size2; - read_data_dma(nandc, reg_off, data_buf, data_size1); + nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); + } + + config_nand_cw_read(nandc); + + read_data_dma(nandc, reg_off, data_buf, data_size1, 0); reg_off += data_size1; data_buf += data_size1; - read_data_dma(nandc, reg_off, oob_buf, oob_size1); + read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); reg_off += oob_size1; oob_buf += oob_size1; - read_data_dma(nandc, reg_off, data_buf, data_size2); + read_data_dma(nandc, reg_off, data_buf, data_size2, 0); reg_off += data_size2; data_buf += data_size2; - read_data_dma(nandc, reg_off, oob_buf, oob_size2); + read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); oob_buf += oob_size2; } @@ -1280,6 +1735,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int ret; clear_read_regs(nandc); + clear_bam_transaction(nandc); host->use_ecc = true; set_address(host, 0, page); @@ -1303,12 +1759,14 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, int i, ret; clear_read_regs(nandc); + clear_bam_transaction(nandc); data_buf = (u8 *)buf; oob_buf = chip->oob_poi; host->use_ecc = true; update_rw_regs(host, ecc->steps, false); + config_nand_page_write(nandc); for (i = 0; i < ecc->steps; i++) { int data_size, oob_size; @@ -1322,9 +1780,9 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, oob_size = ecc->bytes; } - config_cw_write_pre(nandc); - write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size); + write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, + i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0); /* * when ECC is enabled, we don't really need to write anything @@ -1337,10 +1795,10 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, oob_buf += host->bbm_size; write_data_dma(nandc, FLASH_BUF_ACC + data_size, - oob_buf, oob_size); + oob_buf, oob_size, 0); } - config_cw_write_post(nandc); + config_nand_cw_write(nandc); data_buf += data_size; oob_buf += oob_size; @@ -1367,12 +1825,14 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd, int i, ret; clear_read_regs(nandc); + clear_bam_transaction(nandc); data_buf = (u8 *)buf; oob_buf = chip->oob_poi; host->use_ecc = false; update_rw_regs(host, ecc->steps, false); + config_nand_page_write(nandc); for (i = 0; i < ecc->steps; i++) { int data_size1, data_size2, oob_size1, oob_size2; @@ -1391,24 +1851,25 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd, oob_size2 = host->ecc_bytes_hw + host->spare_bytes; } - config_cw_write_pre(nandc); - - write_data_dma(nandc, reg_off, data_buf, data_size1); + write_data_dma(nandc, reg_off, data_buf, data_size1, + NAND_BAM_NO_EOT); reg_off += data_size1; data_buf += data_size1; - write_data_dma(nandc, reg_off, oob_buf, oob_size1); + write_data_dma(nandc, reg_off, oob_buf, oob_size1, + NAND_BAM_NO_EOT); reg_off += oob_size1; oob_buf += oob_size1; - write_data_dma(nandc, reg_off, data_buf, data_size2); + write_data_dma(nandc, reg_off, data_buf, data_size2, + NAND_BAM_NO_EOT); reg_off += data_size2; data_buf += data_size2; - write_data_dma(nandc, reg_off, oob_buf, oob_size2); + write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); oob_buf += oob_size2; - config_cw_write_post(nandc); + config_nand_cw_write(nandc); } ret = submit_descs(nandc); @@ -1441,11 +1902,13 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, host->use_ecc = true; + clear_bam_transaction(nandc); ret = copy_last_cw(host, page); if (ret) return ret; clear_read_regs(nandc); + clear_bam_transaction(nandc); /* calculate the data and oob size for the last codeword/step */ data_size = ecc->size - ((ecc->steps - 1) << 2); @@ -1458,10 +1921,10 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false); - config_cw_write_pre(nandc); - write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, - data_size + oob_size); - config_cw_write_post(nandc); + config_nand_page_write(nandc); + write_data_dma(nandc, FLASH_BUF_ACC, + nandc->data_buffer, data_size + oob_size, 0); + config_nand_cw_write(nandc); ret = submit_descs(nandc); @@ -1498,6 +1961,7 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) */ host->use_ecc = false; + clear_bam_transaction(nandc); ret = copy_last_cw(host, page); if (ret) goto err; @@ -1528,6 +1992,7 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) int page, ret, status = 0; clear_read_regs(nandc); + clear_bam_transaction(nandc); /* * to mark the BBM as bad, we flash the entire last codeword with 0s. @@ -1543,9 +2008,10 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false); - config_cw_write_pre(nandc); - write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, host->cw_size); - config_cw_write_post(nandc); + config_nand_page_write(nandc); + write_data_dma(nandc, FLASH_BUF_ACC, + nandc->data_buffer, host->cw_size, 0); + config_nand_cw_write(nandc); ret = submit_descs(nandc); @@ -1794,7 +2260,7 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) * uses lesser bytes for ECC. If RS is used, the ECC bytes is * always 10 bytes */ - if (nandc->ecc_modes & ECC_BCH_4BIT) { + if (nandc->props->ecc_modes & ECC_BCH_4BIT) { /* BCH */ host->bch_enabled = true; ecc_mode = 0; @@ -1842,6 +2308,8 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); cwperpage = mtd->writesize / ecc->size; + nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, + cwperpage); /* * DATA_UD_BYTES varies based on whether the read/write command protects @@ -1893,7 +2361,7 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) | wide_bus << WIDE_FLASH | 1 << DEV0_CFG1_ECC_DISABLE; - host->ecc_bch_cfg = host->bch_enabled << ECC_CFG_ECC_DISABLE + host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE | 0 << ECC_SW_RESET | host->cw_data << ECC_NUM_DATA_BYTES | 1 << ECC_FORCE_CLK_OPEN @@ -1904,6 +2372,10 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) host->clrflashstatus = FS_READY_BSY_N; host->clrreadstatus = 0xc0; + nandc->regs->erased_cw_detect_cfg_clr = + cpu_to_le32(CLR_ERASED_PAGE_DET); + nandc->regs->erased_cw_detect_cfg_set = + cpu_to_le32(SET_ERASED_PAGE_DET); dev_dbg(nandc->dev, "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", @@ -1948,10 +2420,55 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) if (!nandc->reg_read_buf) return -ENOMEM; - nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); - if (!nandc->chan) { - dev_err(nandc->dev, "failed to request slave channel\n"); - return -ENODEV; + if (nandc->props->is_bam) { + nandc->reg_read_dma = + dma_map_single(nandc->dev, nandc->reg_read_buf, + MAX_REG_RD * + sizeof(*nandc->reg_read_buf), + DMA_FROM_DEVICE); + if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { + dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); + return -EIO; + } + + nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx"); + if (!nandc->tx_chan) { + dev_err(nandc->dev, "failed to request tx channel\n"); + return -ENODEV; + } + + nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx"); + if (!nandc->rx_chan) { + dev_err(nandc->dev, "failed to request rx channel\n"); + return -ENODEV; + } + + nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd"); + if (!nandc->cmd_chan) { + dev_err(nandc->dev, "failed to request cmd channel\n"); + return -ENODEV; + } + + /* + * Initially allocate BAM transaction to read ONFI param page. + * After detecting all the devices, this BAM transaction will + * be freed and the next BAM tranasction will be allocated with + * maximum codeword size + */ + nandc->max_cwperpage = 1; + nandc->bam_txn = alloc_bam_transaction(nandc); + if (!nandc->bam_txn) { + dev_err(nandc->dev, + "failed to allocate bam transaction\n"); + return -ENOMEM; + } + } else { + nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); + if (!nandc->chan) { + dev_err(nandc->dev, + "failed to request slave channel\n"); + return -ENODEV; + } } INIT_LIST_HEAD(&nandc->desc_list); @@ -1964,21 +2481,48 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) { - dma_release_channel(nandc->chan); + if (nandc->props->is_bam) { + if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) + dma_unmap_single(nandc->dev, nandc->reg_read_dma, + MAX_REG_RD * + sizeof(*nandc->reg_read_buf), + DMA_FROM_DEVICE); + + if (nandc->tx_chan) + dma_release_channel(nandc->tx_chan); + + if (nandc->rx_chan) + dma_release_channel(nandc->rx_chan); + + if (nandc->cmd_chan) + dma_release_channel(nandc->cmd_chan); + } else { + if (nandc->chan) + dma_release_channel(nandc->chan); + } } /* one time setup of a few nand controller registers */ static int qcom_nandc_setup(struct qcom_nand_controller *nandc) { + u32 nand_ctrl; + /* kill onenand */ nandc_write(nandc, SFLASHC_BURST_CFG, 0); + nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), + NAND_DEV_CMD_VLD_VAL); - /* enable ADM DMA */ - nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); + /* enable ADM or BAM DMA */ + if (nandc->props->is_bam) { + nand_ctrl = nandc_read(nandc, NAND_CTRL); + nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); + } else { + nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); + } /* save the original values of these registers */ - nandc->cmd1 = nandc_read(nandc, NAND_DEV_CMD1); - nandc->vld = nandc_read(nandc, NAND_DEV_CMD_VLD); + nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1)); + nandc->vld = NAND_DEV_CMD_VLD_VAL; return 0; } @@ -2034,14 +2578,77 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc, return ret; ret = qcom_nand_host_setup(host); - if (ret) - return ret; + + return ret; +} + +static int qcom_nand_mtd_register(struct qcom_nand_controller *nandc, + struct qcom_nand_host *host, + struct device_node *dn) +{ + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; ret = nand_scan_tail(mtd); if (ret) return ret; - return mtd_device_register(mtd, NULL, 0); + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + nand_cleanup(mtd_to_nand(mtd)); + + return ret; +} + +static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc) +{ + struct device *dev = nandc->dev; + struct device_node *dn = dev->of_node, *child; + struct qcom_nand_host *host, *tmp; + int ret; + + for_each_available_child_of_node(dn, child) { + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) { + of_node_put(child); + return -ENOMEM; + } + + ret = qcom_nand_host_init(nandc, host, child); + if (ret) { + devm_kfree(dev, host); + continue; + } + + list_add_tail(&host->node, &nandc->host_list); + } + + if (list_empty(&nandc->host_list)) + return -ENODEV; + + if (nandc->props->is_bam) { + free_bam_transaction(nandc); + nandc->bam_txn = alloc_bam_transaction(nandc); + if (!nandc->bam_txn) { + dev_err(nandc->dev, + "failed to allocate bam transaction\n"); + return -ENOMEM; + } + } + + list_for_each_entry_safe(host, tmp, &nandc->host_list, node) { + ret = qcom_nand_mtd_register(nandc, host, child); + if (ret) { + list_del(&host->node); + devm_kfree(dev, host); + } + } + + if (list_empty(&nandc->host_list)) + return -ENODEV; + + return 0; } /* parse custom DT properties here */ @@ -2051,16 +2658,20 @@ static int qcom_nandc_parse_dt(struct platform_device *pdev) struct device_node *np = nandc->dev->of_node; int ret; - ret = of_property_read_u32(np, "qcom,cmd-crci", &nandc->cmd_crci); - if (ret) { - dev_err(nandc->dev, "command CRCI unspecified\n"); - return ret; - } + if (!nandc->props->is_bam) { + ret = of_property_read_u32(np, "qcom,cmd-crci", + &nandc->cmd_crci); + if (ret) { + dev_err(nandc->dev, "command CRCI unspecified\n"); + return ret; + } - ret = of_property_read_u32(np, "qcom,data-crci", &nandc->data_crci); - if (ret) { - dev_err(nandc->dev, "data CRCI unspecified\n"); - return ret; + ret = of_property_read_u32(np, "qcom,data-crci", + &nandc->data_crci); + if (ret) { + dev_err(nandc->dev, "data CRCI unspecified\n"); + return ret; + } } return 0; @@ -2069,10 +2680,8 @@ static int qcom_nandc_parse_dt(struct platform_device *pdev) static int qcom_nandc_probe(struct platform_device *pdev) { struct qcom_nand_controller *nandc; - struct qcom_nand_host *host; const void *dev_data; struct device *dev = &pdev->dev; - struct device_node *dn = dev->of_node, *child; struct resource *res; int ret; @@ -2089,7 +2698,7 @@ static int qcom_nandc_probe(struct platform_device *pdev) return -ENODEV; } - nandc->ecc_modes = (unsigned long)dev_data; + nandc->props = dev_data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); nandc->base = devm_ioremap_resource(dev, res); @@ -2112,7 +2721,7 @@ static int qcom_nandc_probe(struct platform_device *pdev) ret = qcom_nandc_alloc(nandc); if (ret) - return ret; + goto err_core_clk; ret = clk_prepare_enable(nandc->core_clk); if (ret) @@ -2126,35 +2735,12 @@ static int qcom_nandc_probe(struct platform_device *pdev) if (ret) goto err_setup; - for_each_available_child_of_node(dn, child) { - if (of_device_is_compatible(child, "qcom,nandcs")) { - host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); - if (!host) { - of_node_put(child); - ret = -ENOMEM; - goto err_cs_init; - } - - ret = qcom_nand_host_init(nandc, host, child); - if (ret) { - devm_kfree(dev, host); - continue; - } - - list_add_tail(&host->node, &nandc->host_list); - } - } - - if (list_empty(&nandc->host_list)) { - ret = -ENODEV; - goto err_cs_init; - } + ret = qcom_probe_nand_devices(nandc); + if (ret) + goto err_setup; return 0; -err_cs_init: - list_for_each_entry(host, &nandc->host_list, node) - nand_release(nand_to_mtd(&host->chip)); err_setup: clk_disable_unprepare(nandc->aon_clk); err_aon_clk: @@ -2181,15 +2767,40 @@ static int qcom_nandc_remove(struct platform_device *pdev) return 0; } -#define EBI2_NANDC_ECC_MODES (ECC_RS_4BIT | ECC_BCH_8BIT) +static const struct qcom_nandc_props ipq806x_nandc_props = { + .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), + .is_bam = false, + .dev_cmd_reg_start = 0x0, +}; + +static const struct qcom_nandc_props ipq4019_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), + .is_bam = true, + .dev_cmd_reg_start = 0x0, +}; + +static const struct qcom_nandc_props ipq8074_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), + .is_bam = true, + .dev_cmd_reg_start = 0x7000, +}; /* * data will hold a struct pointer containing more differences once we support * more controller variants */ static const struct of_device_id qcom_nandc_of_match[] = { - { .compatible = "qcom,ipq806x-nand", - .data = (void *)EBI2_NANDC_ECC_MODES, + { + .compatible = "qcom,ipq806x-nand", + .data = &ipq806x_nandc_props, + }, + { + .compatible = "qcom,ipq4019-nand", + .data = &ipq4019_nandc_props, + }, + { + .compatible = "qcom,ipq8074-nand", + .data = &ipq8074_nandc_props, }, {} }; diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/r852.h index d042ddb71a8b..8713c57f6207 100644 --- a/drivers/mtd/nand/r852.h +++ b/drivers/mtd/nand/r852.h @@ -10,7 +10,7 @@ #include <linux/pci.h> #include <linux/completion.h> #include <linux/workqueue.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/spinlock.h> diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 9e0c849607b9..4c383eeec6f6 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -43,7 +43,7 @@ #include <linux/of_device.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 891ac7b99305..e7f3c98487e6 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -38,7 +38,7 @@ #include <linux/string.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/mtd/sh_flctl.h> @@ -411,7 +411,7 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, dma_addr = dma_map_single(chan->device->dev, buf, len, dir); - if (dma_addr) + if (!dma_mapping_error(chan->device->dev, dma_addr)) desc = dmaengine_prep_slave_single(chan, dma_addr, len, tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -1141,8 +1141,8 @@ static int flctl_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "failed to get flste irq data\n"); - return -ENXIO; + dev_err(&pdev->dev, "failed to get flste irq data: %d\n", irq); + return irq; } ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED, diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 064ca1757589..f59c455d9f51 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -17,7 +17,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> #include <linux/mtd/sharpsl.h> @@ -183,7 +183,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev) /* Register the partitions */ mtd->name = "sharpsl-nand"; - err = mtd_device_parse_register(mtd, NULL, NULL, + err = mtd_device_parse_register(mtd, data->part_parsers, NULL, data->partitions, data->nr_partitions); if (err) goto err_add; diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index 5939dff253c2..c378705c6e2b 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -7,7 +7,7 @@ * published by the Free Software Foundation. */ #include <linux/kernel.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/module.h> #include <linux/sizes.h> #include "sm_common.h" diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index 72369bd079af..575997d0ef8a 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -13,7 +13,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/of_address.h> #include <linux/of_platform.h> diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 6abd142b1324..82244be3e766 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -31,7 +31,7 @@ #include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/clk.h> #include <linux/delay.h> @@ -2212,7 +2212,7 @@ static int sunxi_nfc_probe(struct platform_device *pdev) if (ret) goto out_ahb_clk_unprepare; - nfc->reset = devm_reset_control_get_optional(dev, "ahb"); + nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb"); if (IS_ERR(nfc->reset)) { ret = PTR_ERR(nfc->reset); goto out_mod_clk_unprepare; diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c index 9d40b793b1c4..766906f03943 100644 --- a/drivers/mtd/nand/tango_nand.c +++ b/drivers/mtd/nand/tango_nand.c @@ -11,7 +11,7 @@ #include <linux/clk.h> #include <linux/iopoll.h> #include <linux/module.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> #include <linux/platform_device.h> diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index fc5e773f8b60..84dbf32332e1 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -34,7 +34,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> #include <linux/slab.h> @@ -440,7 +440,9 @@ static int tmio_probe(struct platform_device *dev) goto err_irq; /* Register the partitions */ - retval = mtd_device_parse_register(mtd, NULL, NULL, + retval = mtd_device_parse_register(mtd, + data ? data->part_parsers : NULL, + NULL, data ? data->partition : NULL, data ? data->num_partitions : 0); if (!retval) diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 0a14fda2e41b..b567d212fe7d 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -16,7 +16,7 @@ #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> #include <linux/io.h> diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 744ab10e8962..8037d4b48a05 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -31,10 +31,9 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/partitions.h> #include <linux/of_device.h> -#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -814,12 +813,14 @@ static int vf610_nfc_suspend(struct device *dev) static int vf610_nfc_resume(struct device *dev) { + int err; + struct mtd_info *mtd = dev_get_drvdata(dev); struct vf610_nfc *nfc = mtd_to_nfc(mtd); - pinctrl_pm_select_default_state(dev); - - clk_prepare_enable(nfc->clk); + err = clk_prepare_enable(nfc->clk); + if (err) + return err; vf610_nfc_preinit_controller(nfc); vf610_nfc_init_controller(nfc); diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/xway_nand.c index ddee4005248c..9926b4e3d69d 100644 --- a/drivers/mtd/nand/xway_nand.c +++ b/drivers/mtd/nand/xway_nand.c @@ -7,7 +7,7 @@ * Copyright © 2016 Hauke Mehrtens <hauke@hauke-m.de> */ -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/of_gpio.h> #include <linux/of_platform.h> diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index e21161353e76..1f1a61168b3d 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -34,7 +34,7 @@ #include <linux/kmod.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nftl.h> #include <linux/mtd/blktrans.h> diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index a5dfbfbebfca..184c8fbfe465 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -25,7 +25,7 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/nftl.h> #define SECTORSIZE 512 diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 2861c7079d7b..6bdf4e525677 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -50,8 +50,8 @@ static int parse_ofpart_partitions(struct mtd_info *master, * when using another parser), so don't be louder than * KERN_DEBUG */ - pr_debug("%s: 'partitions' subnode not found on %s. Trying to parse direct subnodes as partitions.\n", - master->name, mtd_node->full_name); + pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", + master->name, mtd_node); ofpart_node = mtd_node; dedicated = false; } else if (!of_device_is_compatible(ofpart_node, "fixed-partitions")) { @@ -87,9 +87,9 @@ static int parse_ofpart_partitions(struct mtd_info *master, reg = of_get_property(pp, "reg", &len); if (!reg) { if (dedicated) { - pr_debug("%s: ofpart partition %s (%s) missing reg property.\n", - master->name, pp->full_name, - mtd_node->full_name); + pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n", + master->name, pp, + mtd_node); goto ofpart_fail; } else { nr_parts--; @@ -100,9 +100,9 @@ static int parse_ofpart_partitions(struct mtd_info *master, a_cells = of_n_addr_cells(pp); s_cells = of_n_size_cells(pp); if (len / 4 != a_cells + s_cells) { - pr_debug("%s: ofpart partition %s (%s) error parsing reg property.\n", - master->name, pp->full_name, - mtd_node->full_name); + pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", + master->name, pp, + mtd_node); goto ofpart_fail; } @@ -131,8 +131,8 @@ static int parse_ofpart_partitions(struct mtd_info *master, return nr_parts; ofpart_fail: - pr_err("%s: error parsing ofpart partition %s (%s)\n", - master->name, pp->full_name, mtd_node->full_name); + pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n", + master->name, pp, mtd_node); ret = -EINVAL; ofpart_none: of_node_put(pp); @@ -166,8 +166,7 @@ static int parse_ofoldpart_partitions(struct mtd_info *master, if (!part) return 0; /* No partitions found */ - pr_warn("Device tree uses obsolete partition map binding: %s\n", - dp->full_name); + pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp); nr_parts = plen / sizeof(part[0]); diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 293c8a4d1e49..69c638dd0484 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -89,6 +89,22 @@ config SPI_NXP_SPIFI config SPI_INTEL_SPI tristate +config SPI_INTEL_SPI_PCI + tristate "Intel PCH/PCU SPI flash PCI driver" if EXPERT + depends on X86 && PCI + select SPI_INTEL_SPI + help + This enables PCI support for the Intel PCH/PCU SPI controller in + master mode. This controller is present in modern Intel hardware + and is used to hold BIOS and other persistent settings. Using + this driver it is possible to upgrade BIOS directly from Linux. + + Say N here unless you know what you are doing. Overwriting the + SPI flash may render the system unbootable. + + To compile this driver as a module, choose M here: the module + will be called intel-spi-pci. + config SPI_INTEL_SPI_PLATFORM tristate "Intel PCH/PCU SPI flash platform driver" if EXPERT depends on X86 diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 285aab86c7ca..7d84c5108e17 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -7,5 +7,6 @@ obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o +obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o -obj-$(CONFIG_SPI_STM32_QUADSPI) += stm32-quadspi.o
\ No newline at end of file +obj-$(CONFIG_SPI_STM32_QUADSPI) += stm32-quadspi.o diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c index 0106357421bd..8d3cbe27efb6 100644 --- a/drivers/mtd/spi-nor/aspeed-smc.c +++ b/drivers/mtd/spi-nor/aspeed-smc.c @@ -621,19 +621,18 @@ static void aspeed_smc_chip_set_type(struct aspeed_smc_chip *chip, int type) } /* - * The AST2500 FMC flash controller should be strapped by hardware, or - * autodetected, but the AST2500 SPI flash needs to be set. + * The first chip of the AST2500 FMC flash controller is strapped by + * hardware, or autodetected, but other chips need to be set. Enforce + * the 4B setting for all chips. */ static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip) { struct aspeed_smc_controller *controller = chip->controller; u32 reg; - if (chip->controller->info == &spi_2500_info) { - reg = readl(controller->regs + CE_CONTROL_REG); - reg |= 1 << chip->cs; - writel(reg, controller->regs + CE_CONTROL_REG); - } + reg = readl(controller->regs + CE_CONTROL_REG); + reg |= 1 << chip->cs; + writel(reg, controller->regs + CE_CONTROL_REG); } /* diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c index ba76fa8f2031..6c5708bacad8 100644 --- a/drivers/mtd/spi-nor/atmel-quadspi.c +++ b/drivers/mtd/spi-nor/atmel-quadspi.c @@ -35,7 +35,6 @@ #include <linux/io.h> #include <linux/gpio.h> -#include <linux/pinctrl/consumer.h> /* QSPI register offsets */ #define QSPI_CR 0x0000 /* Control Register */ diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c index d1106832b9d5..04f9fb5cd9b6 100644 --- a/drivers/mtd/spi-nor/hisi-sfc.c +++ b/drivers/mtd/spi-nor/hisi-sfc.c @@ -355,16 +355,16 @@ static int hisi_spi_nor_register(struct device_node *np, ret = of_property_read_u32(np, "reg", &priv->chipselect); if (ret) { - dev_err(dev, "There's no reg property for %s\n", - np->full_name); + dev_err(dev, "There's no reg property for %pOF\n", + np); return ret; } ret = of_property_read_u32(np, "spi-max-frequency", &priv->clkrate); if (ret) { - dev_err(dev, "There's no spi-max-frequency property for %s\n", - np->full_name); + dev_err(dev, "There's no spi-max-frequency property for %pOF\n", + np); return ret; } priv->host = host; diff --git a/drivers/mtd/spi-nor/intel-spi-pci.c b/drivers/mtd/spi-nor/intel-spi-pci.c new file mode 100644 index 000000000000..e82652335ede --- /dev/null +++ b/drivers/mtd/spi-nor/intel-spi-pci.c @@ -0,0 +1,82 @@ +/* + * Intel PCH/PCU SPI flash PCI driver. + * + * Copyright (C) 2016, Intel Corporation + * Author: Mika Westerberg <mika.westerberg@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "intel-spi.h" + +#define BCR 0xdc +#define BCR_WPD BIT(0) + +static const struct intel_spi_boardinfo bxt_info = { + .type = INTEL_SPI_BXT, +}; + +static int intel_spi_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct intel_spi_boardinfo *info; + struct intel_spi *ispi; + u32 bcr; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + info = devm_kmemdup(&pdev->dev, (void *)id->driver_data, sizeof(*info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + /* Try to make the chip read/write */ + pci_read_config_dword(pdev, BCR, &bcr); + if (!(bcr & BCR_WPD)) { + bcr |= BCR_WPD; + pci_write_config_dword(pdev, BCR, bcr); + pci_read_config_dword(pdev, BCR, &bcr); + } + info->writeable = !!(bcr & BCR_WPD); + + ispi = intel_spi_probe(&pdev->dev, &pdev->resource[0], info); + if (IS_ERR(ispi)) + return PTR_ERR(ispi); + + pci_set_drvdata(pdev, ispi); + return 0; +} + +static void intel_spi_pci_remove(struct pci_dev *pdev) +{ + intel_spi_remove(pci_get_drvdata(pdev)); +} + +static const struct pci_device_id intel_spi_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info }, + { }, +}; +MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids); + +static struct pci_driver intel_spi_pci_driver = { + .name = "intel-spi", + .id_table = intel_spi_pci_ids, + .probe = intel_spi_pci_probe, + .remove = intel_spi_pci_remove, +}; + +module_pci_driver(intel_spi_pci_driver); + +MODULE_DESCRIPTION("Intel PCH/PCU SPI flash PCI driver"); +MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c index 8a20ec4991c8..c258c7adf1c5 100644 --- a/drivers/mtd/spi-nor/mtk-quadspi.c +++ b/drivers/mtd/spi-nor/mtk-quadspi.c @@ -24,7 +24,6 @@ #include <linux/mutex.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1413828ff1fb..cf1d4a15e10a 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -17,6 +17,7 @@ #include <linux/mutex.h> #include <linux/math64.h> #include <linux/sizes.h> +#include <linux/slab.h> #include <linux/mtd/mtd.h> #include <linux/of_platform.h> @@ -86,6 +87,8 @@ struct flash_info { * to support memory size above 128Mib. */ #define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */ +#define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ +#define USE_CLSR BIT(14) /* use CLSR command */ }; #define JEDEC_MFR(info) ((info)->id[0]) @@ -306,8 +309,18 @@ static inline int spi_nor_sr_ready(struct spi_nor *nor) int sr = read_sr(nor); if (sr < 0) return sr; - else - return !(sr & SR_WIP); + + if (nor->flags & SNOR_F_USE_CLSR && sr & (SR_E_ERR | SR_P_ERR)) { + if (sr & SR_E_ERR) + dev_err(nor->dev, "Erase Error occurred\n"); + else + dev_err(nor->dev, "Programming Error occurred\n"); + + nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0); + return -EIO; + } + + return !(sr & SR_WIP); } static inline int spi_nor_fsr_ready(struct spi_nor *nor) @@ -1041,15 +1054,15 @@ static const struct flash_info spi_nor_ids[] = { */ { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, - { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, USE_CLSR) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, + { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, - { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, + { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, + { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, @@ -1079,6 +1092,7 @@ static const struct flash_info spi_nor_ids[] = { { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) }, { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, + { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, /* ST Microelectronics -- newer production may have feature updates */ { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, @@ -1380,6 +1394,16 @@ write_err: return ret; } +/** + * macronix_quad_enable() - set QE bit in Status Register. + * @nor: pointer to a 'struct spi_nor' + * + * Set the Quad Enable (QE) bit in the Status Register. + * + * bit 6 of the Status Register is the QE bit for Macronix like QSPI memories. + * + * Return: 0 on success, -errno otherwise. + */ static int macronix_quad_enable(struct spi_nor *nor) { int ret, val; @@ -1413,22 +1437,13 @@ static int macronix_quad_enable(struct spi_nor *nor) * second byte will be written to the configuration register. * Return negative if error occurred. */ -static int write_sr_cr(struct spi_nor *nor, u16 val) -{ - nor->cmd_buf[0] = val & 0xff; - nor->cmd_buf[1] = (val >> 8); - - return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2); -} - -static int spansion_quad_enable(struct spi_nor *nor) +static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) { int ret; - int quad_en = CR_QUAD_EN_SPAN << 8; write_enable(nor); - ret = write_sr_cr(nor, quad_en); + ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2); if (ret < 0) { dev_err(nor->dev, "error while writing configuration register\n"); @@ -1442,6 +1457,41 @@ static int spansion_quad_enable(struct spi_nor *nor) return ret; } + return 0; +} + +/** + * spansion_quad_enable() - set QE bit in Configuraiton Register. + * @nor: pointer to a 'struct spi_nor' + * + * Set the Quad Enable (QE) bit in the Configuration Register. + * This function is kept for legacy purpose because it has been used for a + * long time without anybody complaining but it should be considered as + * deprecated and maybe buggy. + * First, this function doesn't care about the previous values of the Status + * and Configuration Registers when it sets the QE bit (bit 1) in the + * Configuration Register: all other bits are cleared, which may have unwanted + * side effects like removing some block protections. + * Secondly, it uses the Read Configuration Register (35h) instruction though + * some very old and few memories don't support this instruction. If a pull-up + * resistor is present on the MISO/IO1 line, we might still be able to pass the + * "read back" test because the QSPI memory doesn't recognize the command, + * so leaves the MISO/IO1 line state unchanged, hence read_cr() returns 0xFF. + * + * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI + * memories. + * + * Return: 0 on success, -errno otherwise. + */ +static int spansion_quad_enable(struct spi_nor *nor) +{ + u8 sr_cr[2] = {0, CR_QUAD_EN_SPAN}; + int ret; + + ret = write_sr_cr(nor, sr_cr); + if (ret) + return ret; + /* read back and check it */ ret = read_cr(nor); if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { @@ -1452,6 +1502,140 @@ static int spansion_quad_enable(struct spi_nor *nor) return 0; } +/** + * spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register. + * @nor: pointer to a 'struct spi_nor' + * + * Set the Quad Enable (QE) bit in the Configuration Register. + * This function should be used with QSPI memories not supporting the Read + * Configuration Register (35h) instruction. + * + * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI + * memories. + * + * Return: 0 on success, -errno otherwise. + */ +static int spansion_no_read_cr_quad_enable(struct spi_nor *nor) +{ + u8 sr_cr[2]; + int ret; + + /* Keep the current value of the Status Register. */ + ret = read_sr(nor); + if (ret < 0) { + dev_err(nor->dev, "error while reading status register\n"); + return -EINVAL; + } + sr_cr[0] = ret; + sr_cr[1] = CR_QUAD_EN_SPAN; + + return write_sr_cr(nor, sr_cr); +} + +/** + * spansion_read_cr_quad_enable() - set QE bit in Configuration Register. + * @nor: pointer to a 'struct spi_nor' + * + * Set the Quad Enable (QE) bit in the Configuration Register. + * This function should be used with QSPI memories supporting the Read + * Configuration Register (35h) instruction. + * + * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI + * memories. + * + * Return: 0 on success, -errno otherwise. + */ +static int spansion_read_cr_quad_enable(struct spi_nor *nor) +{ + struct device *dev = nor->dev; + u8 sr_cr[2]; + int ret; + + /* Check current Quad Enable bit value. */ + ret = read_cr(nor); + if (ret < 0) { + dev_err(dev, "error while reading configuration register\n"); + return -EINVAL; + } + + if (ret & CR_QUAD_EN_SPAN) + return 0; + + sr_cr[1] = ret | CR_QUAD_EN_SPAN; + + /* Keep the current value of the Status Register. */ + ret = read_sr(nor); + if (ret < 0) { + dev_err(dev, "error while reading status register\n"); + return -EINVAL; + } + sr_cr[0] = ret; + + ret = write_sr_cr(nor, sr_cr); + if (ret) + return ret; + + /* Read back and check it. */ + ret = read_cr(nor); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +/** + * sr2_bit7_quad_enable() - set QE bit in Status Register 2. + * @nor: pointer to a 'struct spi_nor' + * + * Set the Quad Enable (QE) bit in the Status Register 2. + * + * This is one of the procedures to set the QE bit described in the SFDP + * (JESD216 rev B) specification but no manufacturer using this procedure has + * been identified yet, hence the name of the function. + * + * Return: 0 on success, -errno otherwise. + */ +static int sr2_bit7_quad_enable(struct spi_nor *nor) +{ + u8 sr2; + int ret; + + /* Check current Quad Enable bit value. */ + ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1); + if (ret) + return ret; + if (sr2 & SR2_QUAD_EN_BIT7) + return 0; + + /* Update the Quad Enable bit. */ + sr2 |= SR2_QUAD_EN_BIT7; + + write_enable(nor); + + ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1); + if (ret < 0) { + dev_err(nor->dev, "error while writing status register 2\n"); + return -EINVAL; + } + + ret = spi_nor_wait_till_ready(nor); + if (ret < 0) { + dev_err(nor->dev, "timeout while writing status register 2\n"); + return ret; + } + + /* Read back and check it. */ + ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1); + if (!(ret > 0 && (sr2 & SR2_QUAD_EN_BIT7))) { + dev_err(nor->dev, "SR2 Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + static int spi_nor_check(struct spi_nor *nor) { if (!nor->dev || !nor->read || !nor->write || @@ -1591,6 +1775,560 @@ spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, pp->proto = proto; } +/* + * Serial Flash Discoverable Parameters (SFDP) parsing. + */ + +/** + * spi_nor_read_sfdp() - read Serial Flash Discoverable Parameters. + * @nor: pointer to a 'struct spi_nor' + * @addr: offset in the SFDP area to start reading data from + * @len: number of bytes to read + * @buf: buffer where the SFDP data are copied into + * + * Whatever the actual numbers of bytes for address and dummy cycles are + * for (Fast) Read commands, the Read SFDP (5Ah) instruction is always + * followed by a 3-byte address and 8 dummy clock cycles. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr, + size_t len, void *buf) +{ + u8 addr_width, read_opcode, read_dummy; + int ret; + + read_opcode = nor->read_opcode; + addr_width = nor->addr_width; + read_dummy = nor->read_dummy; + + nor->read_opcode = SPINOR_OP_RDSFDP; + nor->addr_width = 3; + nor->read_dummy = 8; + + while (len) { + ret = nor->read(nor, addr, len, (u8 *)buf); + if (!ret || ret > len) { + ret = -EIO; + goto read_err; + } + if (ret < 0) + goto read_err; + + buf += ret; + addr += ret; + len -= ret; + } + ret = 0; + +read_err: + nor->read_opcode = read_opcode; + nor->addr_width = addr_width; + nor->read_dummy = read_dummy; + + return ret; +} + +struct sfdp_parameter_header { + u8 id_lsb; + u8 minor; + u8 major; + u8 length; /* in double words */ + u8 parameter_table_pointer[3]; /* byte address */ + u8 id_msb; +}; + +#define SFDP_PARAM_HEADER_ID(p) (((p)->id_msb << 8) | (p)->id_lsb) +#define SFDP_PARAM_HEADER_PTP(p) \ + (((p)->parameter_table_pointer[2] << 16) | \ + ((p)->parameter_table_pointer[1] << 8) | \ + ((p)->parameter_table_pointer[0] << 0)) + +#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ +#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ + +#define SFDP_SIGNATURE 0x50444653U +#define SFDP_JESD216_MAJOR 1 +#define SFDP_JESD216_MINOR 0 +#define SFDP_JESD216A_MINOR 5 +#define SFDP_JESD216B_MINOR 6 + +struct sfdp_header { + u32 signature; /* Ox50444653U <=> "SFDP" */ + u8 minor; + u8 major; + u8 nph; /* 0-base number of parameter headers */ + u8 unused; + + /* Basic Flash Parameter Table. */ + struct sfdp_parameter_header bfpt_header; +}; + +/* Basic Flash Parameter Table */ + +/* + * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs. + * They are indexed from 1 but C arrays are indexed from 0. + */ +#define BFPT_DWORD(i) ((i) - 1) +#define BFPT_DWORD_MAX 16 + +/* The first version of JESB216 defined only 9 DWORDs. */ +#define BFPT_DWORD_MAX_JESD216 9 + +/* 1st DWORD. */ +#define BFPT_DWORD1_FAST_READ_1_1_2 BIT(16) +#define BFPT_DWORD1_ADDRESS_BYTES_MASK GENMASK(18, 17) +#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY (0x0UL << 17) +#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4 (0x1UL << 17) +#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY (0x2UL << 17) +#define BFPT_DWORD1_DTR BIT(19) +#define BFPT_DWORD1_FAST_READ_1_2_2 BIT(20) +#define BFPT_DWORD1_FAST_READ_1_4_4 BIT(21) +#define BFPT_DWORD1_FAST_READ_1_1_4 BIT(22) + +/* 5th DWORD. */ +#define BFPT_DWORD5_FAST_READ_2_2_2 BIT(0) +#define BFPT_DWORD5_FAST_READ_4_4_4 BIT(4) + +/* 11th DWORD. */ +#define BFPT_DWORD11_PAGE_SIZE_SHIFT 4 +#define BFPT_DWORD11_PAGE_SIZE_MASK GENMASK(7, 4) + +/* 15th DWORD. */ + +/* + * (from JESD216 rev B) + * Quad Enable Requirements (QER): + * - 000b: Device does not have a QE bit. Device detects 1-1-4 and 1-4-4 + * reads based on instruction. DQ3/HOLD# functions are hold during + * instruction phase. + * - 001b: QE is bit 1 of status register 2. It is set via Write Status with + * two data bytes where bit 1 of the second byte is one. + * [...] + * Writing only one byte to the status register has the side-effect of + * clearing status register 2, including the QE bit. The 100b code is + * used if writing one byte to the status register does not modify + * status register 2. + * - 010b: QE is bit 6 of status register 1. It is set via Write Status with + * one data byte where bit 6 is one. + * [...] + * - 011b: QE is bit 7 of status register 2. It is set via Write status + * register 2 instruction 3Eh with one data byte where bit 7 is one. + * [...] + * The status register 2 is read using instruction 3Fh. + * - 100b: QE is bit 1 of status register 2. It is set via Write Status with + * two data bytes where bit 1 of the second byte is one. + * [...] + * In contrast to the 001b code, writing one byte to the status + * register does not modify status register 2. + * - 101b: QE is bit 1 of status register 2. Status register 1 is read using + * Read Status instruction 05h. Status register2 is read using + * instruction 35h. QE is set via Writ Status instruction 01h with + * two data bytes where bit 1 of the second byte is one. + * [...] + */ +#define BFPT_DWORD15_QER_MASK GENMASK(22, 20) +#define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */ +#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20) +#define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */ +#define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20) +#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) +#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ + +struct sfdp_bfpt { + u32 dwords[BFPT_DWORD_MAX]; +}; + +/* Fast Read settings. */ + +static inline void +spi_nor_set_read_settings_from_bfpt(struct spi_nor_read_command *read, + u16 half, + enum spi_nor_protocol proto) +{ + read->num_mode_clocks = (half >> 5) & 0x07; + read->num_wait_states = (half >> 0) & 0x1f; + read->opcode = (half >> 8) & 0xff; + read->proto = proto; +} + +struct sfdp_bfpt_read { + /* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */ + u32 hwcaps; + + /* + * The <supported_bit> bit in <supported_dword> BFPT DWORD tells us + * whether the Fast Read x-y-z command is supported. + */ + u32 supported_dword; + u32 supported_bit; + + /* + * The half-word at offset <setting_shift> in <setting_dword> BFPT DWORD + * encodes the op code, the number of mode clocks and the number of wait + * states to be used by Fast Read x-y-z command. + */ + u32 settings_dword; + u32 settings_shift; + + /* The SPI protocol for this Fast Read x-y-z command. */ + enum spi_nor_protocol proto; +}; + +static const struct sfdp_bfpt_read sfdp_bfpt_reads[] = { + /* Fast Read 1-1-2 */ + { + SNOR_HWCAPS_READ_1_1_2, + BFPT_DWORD(1), BIT(16), /* Supported bit */ + BFPT_DWORD(4), 0, /* Settings */ + SNOR_PROTO_1_1_2, + }, + + /* Fast Read 1-2-2 */ + { + SNOR_HWCAPS_READ_1_2_2, + BFPT_DWORD(1), BIT(20), /* Supported bit */ + BFPT_DWORD(4), 16, /* Settings */ + SNOR_PROTO_1_2_2, + }, + + /* Fast Read 2-2-2 */ + { + SNOR_HWCAPS_READ_2_2_2, + BFPT_DWORD(5), BIT(0), /* Supported bit */ + BFPT_DWORD(6), 16, /* Settings */ + SNOR_PROTO_2_2_2, + }, + + /* Fast Read 1-1-4 */ + { + SNOR_HWCAPS_READ_1_1_4, + BFPT_DWORD(1), BIT(22), /* Supported bit */ + BFPT_DWORD(3), 16, /* Settings */ + SNOR_PROTO_1_1_4, + }, + + /* Fast Read 1-4-4 */ + { + SNOR_HWCAPS_READ_1_4_4, + BFPT_DWORD(1), BIT(21), /* Supported bit */ + BFPT_DWORD(3), 0, /* Settings */ + SNOR_PROTO_1_4_4, + }, + + /* Fast Read 4-4-4 */ + { + SNOR_HWCAPS_READ_4_4_4, + BFPT_DWORD(5), BIT(4), /* Supported bit */ + BFPT_DWORD(7), 16, /* Settings */ + SNOR_PROTO_4_4_4, + }, +}; + +struct sfdp_bfpt_erase { + /* + * The half-word at offset <shift> in DWORD <dwoard> encodes the + * op code and erase sector size to be used by Sector Erase commands. + */ + u32 dword; + u32 shift; +}; + +static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = { + /* Erase Type 1 in DWORD8 bits[15:0] */ + {BFPT_DWORD(8), 0}, + + /* Erase Type 2 in DWORD8 bits[31:16] */ + {BFPT_DWORD(8), 16}, + + /* Erase Type 3 in DWORD9 bits[15:0] */ + {BFPT_DWORD(9), 0}, + + /* Erase Type 4 in DWORD9 bits[31:16] */ + {BFPT_DWORD(9), 16}, +}; + +static int spi_nor_hwcaps_read2cmd(u32 hwcaps); + +/** + * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table. + * @nor: pointer to a 'struct spi_nor' + * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing + * the Basic Flash Parameter Table length and version + * @params: pointer to the 'struct spi_nor_flash_parameter' to be + * filled + * + * The Basic Flash Parameter Table is the main and only mandatory table as + * defined by the SFDP (JESD216) specification. + * It provides us with the total size (memory density) of the data array and + * the number of address bytes for Fast Read, Page Program and Sector Erase + * commands. + * For Fast READ commands, it also gives the number of mode clock cycles and + * wait states (regrouped in the number of dummy clock cycles) for each + * supported instruction op code. + * For Page Program, the page size is now available since JESD216 rev A, however + * the supported instruction op codes are still not provided. + * For Sector Erase commands, this table stores the supported instruction op + * codes and the associated sector sizes. + * Finally, the Quad Enable Requirements (QER) are also available since JESD216 + * rev A. The QER bits encode the manufacturer dependent procedure to be + * executed to set the Quad Enable (QE) bit in some internal register of the + * Quad SPI memory. Indeed the QE bit, when it exists, must be set before + * sending any Quad SPI command to the memory. Actually, setting the QE bit + * tells the memory to reassign its WP# and HOLD#/RESET# pins to functions IO2 + * and IO3 hence enabling 4 (Quad) I/O lines. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_bfpt(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + struct spi_nor_flash_parameter *params) +{ + struct mtd_info *mtd = &nor->mtd; + struct sfdp_bfpt bfpt; + size_t len; + int i, cmd, err; + u32 addr; + u16 half; + + /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */ + if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) + return -EINVAL; + + /* Read the Basic Flash Parameter Table. */ + len = min_t(size_t, sizeof(bfpt), + bfpt_header->length * sizeof(u32)); + addr = SFDP_PARAM_HEADER_PTP(bfpt_header); + memset(&bfpt, 0, sizeof(bfpt)); + err = spi_nor_read_sfdp(nor, addr, len, &bfpt); + if (err < 0) + return err; + + /* Fix endianness of the BFPT DWORDs. */ + for (i = 0; i < BFPT_DWORD_MAX; i++) + bfpt.dwords[i] = le32_to_cpu(bfpt.dwords[i]); + + /* Number of address bytes. */ + switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { + case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: + nor->addr_width = 3; + break; + + case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: + nor->addr_width = 4; + break; + + default: + break; + } + + /* Flash Memory Density (in bits). */ + params->size = bfpt.dwords[BFPT_DWORD(2)]; + if (params->size & BIT(31)) { + params->size &= ~BIT(31); + params->size = 1ULL << params->size; + } else { + params->size++; + } + params->size >>= 3; /* Convert to bytes. */ + + /* Fast Read settings. */ + for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_reads); i++) { + const struct sfdp_bfpt_read *rd = &sfdp_bfpt_reads[i]; + struct spi_nor_read_command *read; + + if (!(bfpt.dwords[rd->supported_dword] & rd->supported_bit)) { + params->hwcaps.mask &= ~rd->hwcaps; + continue; + } + + params->hwcaps.mask |= rd->hwcaps; + cmd = spi_nor_hwcaps_read2cmd(rd->hwcaps); + read = ¶ms->reads[cmd]; + half = bfpt.dwords[rd->settings_dword] >> rd->settings_shift; + spi_nor_set_read_settings_from_bfpt(read, half, rd->proto); + } + + /* Sector Erase settings. */ + for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) { + const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i]; + u32 erasesize; + u8 opcode; + + half = bfpt.dwords[er->dword] >> er->shift; + erasesize = half & 0xff; + + /* erasesize == 0 means this Erase Type is not supported. */ + if (!erasesize) + continue; + + erasesize = 1U << erasesize; + opcode = (half >> 8) & 0xff; +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + if (erasesize == SZ_4K) { + nor->erase_opcode = opcode; + mtd->erasesize = erasesize; + break; + } +#endif + if (!mtd->erasesize || mtd->erasesize < erasesize) { + nor->erase_opcode = opcode; + mtd->erasesize = erasesize; + } + } + + /* Stop here if not JESD216 rev A or later. */ + if (bfpt_header->length < BFPT_DWORD_MAX) + return 0; + + /* Page size: this field specifies 'N' so the page size = 2^N bytes. */ + params->page_size = bfpt.dwords[BFPT_DWORD(11)]; + params->page_size &= BFPT_DWORD11_PAGE_SIZE_MASK; + params->page_size >>= BFPT_DWORD11_PAGE_SIZE_SHIFT; + params->page_size = 1U << params->page_size; + + /* Quad Enable Requirements. */ + switch (bfpt.dwords[BFPT_DWORD(15)] & BFPT_DWORD15_QER_MASK) { + case BFPT_DWORD15_QER_NONE: + params->quad_enable = NULL; + break; + + case BFPT_DWORD15_QER_SR2_BIT1_BUGGY: + case BFPT_DWORD15_QER_SR2_BIT1_NO_RD: + params->quad_enable = spansion_no_read_cr_quad_enable; + break; + + case BFPT_DWORD15_QER_SR1_BIT6: + params->quad_enable = macronix_quad_enable; + break; + + case BFPT_DWORD15_QER_SR2_BIT7: + params->quad_enable = sr2_bit7_quad_enable; + break; + + case BFPT_DWORD15_QER_SR2_BIT1: + params->quad_enable = spansion_read_cr_quad_enable; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/** + * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. + * @nor: pointer to a 'struct spi_nor' + * @params: pointer to the 'struct spi_nor_flash_parameter' to be + * filled + * + * The Serial Flash Discoverable Parameters are described by the JEDEC JESD216 + * specification. This is a standard which tends to supported by almost all + * (Q)SPI memory manufacturers. Those hard-coded tables allow us to learn at + * runtime the main parameters needed to perform basic SPI flash operations such + * as Fast Read, Page Program or Sector Erase commands. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_sfdp(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + const struct sfdp_parameter_header *param_header, *bfpt_header; + struct sfdp_parameter_header *param_headers = NULL; + struct sfdp_header header; + struct device *dev = nor->dev; + size_t psize; + int i, err; + + /* Get the SFDP header. */ + err = spi_nor_read_sfdp(nor, 0, sizeof(header), &header); + if (err < 0) + return err; + + /* Check the SFDP header version. */ + if (le32_to_cpu(header.signature) != SFDP_SIGNATURE || + header.major != SFDP_JESD216_MAJOR || + header.minor < SFDP_JESD216_MINOR) + return -EINVAL; + + /* + * Verify that the first and only mandatory parameter header is a + * Basic Flash Parameter Table header as specified in JESD216. + */ + bfpt_header = &header.bfpt_header; + if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID || + bfpt_header->major != SFDP_JESD216_MAJOR) + return -EINVAL; + + /* + * Allocate memory then read all parameter headers with a single + * Read SFDP command. These parameter headers will actually be parsed + * twice: a first time to get the latest revision of the basic flash + * parameter table, then a second time to handle the supported optional + * tables. + * Hence we read the parameter headers once for all to reduce the + * processing time. Also we use kmalloc() instead of devm_kmalloc() + * because we don't need to keep these parameter headers: the allocated + * memory is always released with kfree() before exiting this function. + */ + if (header.nph) { + psize = header.nph * sizeof(*param_headers); + + param_headers = kmalloc(psize, GFP_KERNEL); + if (!param_headers) + return -ENOMEM; + + err = spi_nor_read_sfdp(nor, sizeof(header), + psize, param_headers); + if (err < 0) { + dev_err(dev, "failed to read SFDP parameter headers\n"); + goto exit; + } + } + + /* + * Check other parameter headers to get the latest revision of + * the basic flash parameter table. + */ + for (i = 0; i < header.nph; i++) { + param_header = ¶m_headers[i]; + + if (SFDP_PARAM_HEADER_ID(param_header) == SFDP_BFPT_ID && + param_header->major == SFDP_JESD216_MAJOR && + (param_header->minor > bfpt_header->minor || + (param_header->minor == bfpt_header->minor && + param_header->length > bfpt_header->length))) + bfpt_header = param_header; + } + + err = spi_nor_parse_bfpt(nor, bfpt_header, params); + if (err) + goto exit; + + /* Parse other parameter headers. */ + for (i = 0; i < header.nph; i++) { + param_header = ¶m_headers[i]; + + switch (SFDP_PARAM_HEADER_ID(param_header)) { + case SFDP_SECTOR_MAP_ID: + dev_info(dev, "non-uniform erase sector maps are not supported yet.\n"); + break; + + default: + break; + } + + if (err) + goto exit; + } + +exit: + kfree(param_headers); + return err; +} + static int spi_nor_init_params(struct spi_nor *nor, const struct flash_info *info, struct spi_nor_flash_parameter *params) @@ -1646,11 +2384,28 @@ static int spi_nor_init_params(struct spi_nor *nor, break; default: + /* Kept only for backward compatibility purpose. */ params->quad_enable = spansion_quad_enable; break; } } + /* Override the parameters with data read from SFDP tables. */ + nor->addr_width = 0; + nor->mtd.erasesize = 0; + if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) && + !(info->flags & SPI_NOR_SKIP_SFDP)) { + struct spi_nor_flash_parameter sfdp_params; + + memcpy(&sfdp_params, params, sizeof(sfdp_params)); + if (spi_nor_parse_sfdp(nor, &sfdp_params)) { + nor->addr_width = 0; + nor->mtd.erasesize = 0; + } else { + memcpy(params, &sfdp_params, sizeof(*params)); + } + } + return 0; } @@ -1762,6 +2517,10 @@ static int spi_nor_select_erase(struct spi_nor *nor, { struct mtd_info *mtd = &nor->mtd; + /* Do nothing if already configured from SFDP. */ + if (mtd->erasesize) + return 0; + #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS /* prefer "small sector" erase if possible */ if (info->flags & SECT_4K) { @@ -1960,6 +2719,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, nor->flags |= SNOR_F_HAS_SR_TB; if (info->flags & NO_CHIP_ERASE) nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; + if (info->flags & USE_CLSR) + nor->flags |= SNOR_F_USE_CLSR; if (info->flags & SPI_NOR_NO_ERASE) mtd->flags |= MTD_NO_ERASE; @@ -1994,9 +2755,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (ret) return ret; - if (info->addr_width) + if (nor->addr_width) { + /* already configured from SFDP */ + } else if (info->addr_width) { nor->addr_width = info->addr_width; - else if (mtd->size > 0x1000000) { + } else if (mtd->size > 0x1000000) { /* enable 4-byte addressing if the device exceeds 16MiB */ nor->addr_width = 4; if (JEDEC_MFR(info) == SNOR_MFR_SPANSION || diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c index 41b13d1cdcc4..95f0bf95f095 100644 --- a/drivers/mtd/ssfdc.c +++ b/drivers/mtd/ssfdc.c @@ -16,7 +16,7 @@ #include <linux/slab.h> #include <linux/hdreg.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/mtd/blktrans.h> struct ssfdcr_record { diff --git a/drivers/mtd/tests/nandbiterrs.c b/drivers/mtd/tests/nandbiterrs.c index f26dec896afa..5f03b8c885a9 100644 --- a/drivers/mtd/tests/nandbiterrs.c +++ b/drivers/mtd/tests/nandbiterrs.c @@ -47,7 +47,7 @@ #include <linux/moduleparam.h> #include <linux/mtd/mtd.h> #include <linux/err.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/slab.h> #include "mtd_test.h" diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index a3c90fe5de00..73ca8879ada7 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -1180,9 +1180,10 @@ static int au1000_probe(struct platform_device *pdev) /* Allocate the data buffers * Snooping works fine with eth on all au1xxx */ - aup->vaddr = (u32)dma_alloc_noncoherent(NULL, MAX_BUF_SIZE * - (NUM_TX_BUFFS + NUM_RX_BUFFS), - &aup->dma_addr, 0); + aup->vaddr = (u32)dma_alloc_attrs(NULL, MAX_BUF_SIZE * + (NUM_TX_BUFFS + NUM_RX_BUFFS), + &aup->dma_addr, 0, + DMA_ATTR_NON_CONSISTENT); if (!aup->vaddr) { dev_err(&pdev->dev, "failed to allocate data buffers\n"); err = -ENOMEM; @@ -1361,8 +1362,9 @@ err_remap3: err_remap2: iounmap(aup->mac); err_remap1: - dma_free_noncoherent(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS), - (void *)aup->vaddr, aup->dma_addr); + dma_free_attrs(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS), + (void *)aup->vaddr, aup->dma_addr, + DMA_ATTR_NON_CONSISTENT); err_vaddr: free_netdev(dev); err_alloc: @@ -1394,9 +1396,9 @@ static int au1000_remove(struct platform_device *pdev) if (aup->tx_db_inuse[i]) au1000_ReleaseDB(aup, aup->tx_db_inuse[i]); - dma_free_noncoherent(NULL, MAX_BUF_SIZE * - (NUM_TX_BUFFS + NUM_RX_BUFFS), - (void *)aup->vaddr, aup->dma_addr); + dma_free_attrs(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS), + (void *)aup->vaddr, aup->dma_addr, + DMA_ATTR_NON_CONSISTENT); iounmap(aup->macdma); iounmap(aup->mac); diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index 16fe776ddbe5..50222b7b81f3 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -65,7 +65,7 @@ MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds"); */ static int debug; module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "dm9000 debug level (0-4)"); +MODULE_PARM_DESC(debug, "dm9000 debug level (0-6)"); /* DM9000 register address locking. * diff --git a/drivers/net/ethernet/dec/tulip/tulip.h b/drivers/net/ethernet/dec/tulip/tulip.h index 38431a155f09..06660dbc44b7 100644 --- a/drivers/net/ethernet/dec/tulip/tulip.h +++ b/drivers/net/ethernet/dec/tulip/tulip.h @@ -515,7 +515,7 @@ void comet_timer(unsigned long data); extern int tulip_debug; extern const char * const medianame[]; extern const char tulip_media_cap[]; -extern struct tulip_chip_table tulip_tbl[]; +extern const struct tulip_chip_table tulip_tbl[]; void oom_timer(unsigned long data); extern u8 t21040_csr13[]; diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 84394b43c0a1..851b6d1f5a42 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -138,7 +138,7 @@ static void tulip_timer(unsigned long data) * It is indexed via the values in 'enum chips' */ -struct tulip_chip_table tulip_tbl[] = { +const struct tulip_chip_table tulip_tbl[] = { { }, /* placeholder for array, slot unused currently */ { }, /* placeholder for array, slot unused currently */ diff --git a/drivers/net/ethernet/i825xx/lasi_82596.c b/drivers/net/ethernet/i825xx/lasi_82596.c index aa22e108f09b..b69c622ba8b2 100644 --- a/drivers/net/ethernet/i825xx/lasi_82596.c +++ b/drivers/net/ethernet/i825xx/lasi_82596.c @@ -96,8 +96,6 @@ #define OPT_SWAP_PORT 0x0001 /* Need to wordswp on the MPU port */ -#define DMA_ALLOC dma_alloc_noncoherent -#define DMA_FREE dma_free_noncoherent #define DMA_WBACK(ndev, addr, len) \ do { dma_cache_sync((ndev)->dev.parent, (void *)addr, len, DMA_TO_DEVICE); } while (0) @@ -200,8 +198,8 @@ static int __exit lan_remove_chip(struct parisc_device *pdev) struct i596_private *lp = netdev_priv(dev); unregister_netdev (dev); - DMA_FREE(&pdev->dev, sizeof(struct i596_private), - (void *)lp->dma, lp->dma_addr); + dma_free_attrs(&pdev->dev, sizeof(struct i596_private), lp->dma, + lp->dma_addr, DMA_ATTR_NON_CONSISTENT); free_netdev (dev); return 0; } diff --git a/drivers/net/ethernet/i825xx/lib82596.c b/drivers/net/ethernet/i825xx/lib82596.c index 8449c58f01fd..f00a1dc2128c 100644 --- a/drivers/net/ethernet/i825xx/lib82596.c +++ b/drivers/net/ethernet/i825xx/lib82596.c @@ -1063,8 +1063,9 @@ static int i82596_probe(struct net_device *dev) if (!dev->base_addr || !dev->irq) return -ENODEV; - dma = (struct i596_dma *) DMA_ALLOC(dev->dev.parent, - sizeof(struct i596_dma), &lp->dma_addr, GFP_KERNEL); + dma = dma_alloc_attrs(dev->dev.parent, sizeof(struct i596_dma), + &lp->dma_addr, GFP_KERNEL, + DMA_ATTR_NON_CONSISTENT); if (!dma) { printk(KERN_ERR "%s: Couldn't get shared memory\n", __FILE__); return -ENOMEM; @@ -1085,8 +1086,8 @@ static int i82596_probe(struct net_device *dev) i = register_netdev(dev); if (i) { - DMA_FREE(dev->dev.parent, sizeof(struct i596_dma), - (void *)dma, lp->dma_addr); + dma_free_attrs(dev->dev.parent, sizeof(struct i596_dma), + dma, lp->dma_addr, DMA_ATTR_NON_CONSISTENT); return i; } diff --git a/drivers/net/ethernet/i825xx/sni_82596.c b/drivers/net/ethernet/i825xx/sni_82596.c index 2af7f77345fb..b2c04a789744 100644 --- a/drivers/net/ethernet/i825xx/sni_82596.c +++ b/drivers/net/ethernet/i825xx/sni_82596.c @@ -23,8 +23,6 @@ static const char sni_82596_string[] = "snirm_82596"; -#define DMA_ALLOC dma_alloc_coherent -#define DMA_FREE dma_free_coherent #define DMA_WBACK(priv, addr, len) do { } while (0) #define DMA_INV(priv, addr, len) do { } while (0) #define DMA_WBACK_INV(priv, addr, len) do { } while (0) @@ -152,8 +150,8 @@ static int sni_82596_driver_remove(struct platform_device *pdev) struct i596_private *lp = netdev_priv(dev); unregister_netdev(dev); - DMA_FREE(dev->dev.parent, sizeof(struct i596_private), - lp->dma, lp->dma_addr); + dma_free_attrs(dev->dev.parent, sizeof(struct i596_private), lp->dma, + lp->dma_addr, DMA_ATTR_NON_CONSISTENT); iounmap(lp->ca); iounmap(lp->mpu_port); free_netdev (dev); diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 8a835e82256a..eef35bf3e849 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -4193,7 +4193,7 @@ static struct pci_driver skge_driver = { .driver.pm = SKGE_PM_OPS, }; -static struct dmi_system_id skge_32bit_dma_boards[] = { +static const struct dmi_system_id skge_32bit_dma_boards[] = { { .ident = "Gigabyte nForce boards", .matches = { diff --git a/drivers/net/ethernet/seeq/sgiseeq.c b/drivers/net/ethernet/seeq/sgiseeq.c index 70347720fdf9..573691bc3b71 100644 --- a/drivers/net/ethernet/seeq/sgiseeq.c +++ b/drivers/net/ethernet/seeq/sgiseeq.c @@ -737,8 +737,8 @@ static int sgiseeq_probe(struct platform_device *pdev) sp = netdev_priv(dev); /* Make private data page aligned */ - sr = dma_alloc_noncoherent(&pdev->dev, sizeof(*sp->srings), - &sp->srings_dma, GFP_KERNEL); + sr = dma_alloc_attrs(&pdev->dev, sizeof(*sp->srings), &sp->srings_dma, + GFP_KERNEL, DMA_ATTR_NON_CONSISTENT); if (!sr) { printk(KERN_ERR "Sgiseeq: Page alloc failed, aborting.\n"); err = -ENOMEM; @@ -813,8 +813,8 @@ static int sgiseeq_remove(struct platform_device *pdev) struct sgiseeq_private *sp = netdev_priv(dev); unregister_netdev(dev); - dma_free_noncoherent(&pdev->dev, sizeof(*sp->srings), sp->srings, - sp->srings_dma); + dma_free_attrs(&pdev->dev, sizeof(*sp->srings), sp->srings, + sp->srings_dma, DMA_ATTR_NON_CONSISTENT); free_netdev(dev); return 0; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index eb96a6913235..437d36289786 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -2145,7 +2145,6 @@ static void netcp_delete_interface(struct netcp_device *netcp_device, of_node_put(netcp->node_interface); unregister_netdev(ndev); - netif_napi_del(&netcp->rx_napi); free_netdev(ndev); } diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index acd29d60174a..83e6f76eb965 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -2598,7 +2598,7 @@ static struct platform_driver rhine_driver_platform = { } }; -static struct dmi_system_id rhine_dmi_table[] __initdata = { +static const struct dmi_system_id rhine_dmi_table[] __initconst = { { .ident = "EPIA-M", .matches = { diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index b6f9fa670168..2df7b62c1a36 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -399,7 +399,8 @@ error: } /* Put PHYs in RESET to save power */ - gpiod_set_value_cansleep(bus->reset_gpiod, 1); + if (bus->reset_gpiod) + gpiod_set_value_cansleep(bus->reset_gpiod, 1); device_del(&bus->dev); return err; @@ -424,7 +425,8 @@ void mdiobus_unregister(struct mii_bus *bus) } /* Put PHYs in RESET to save power */ - gpiod_set_value_cansleep(bus->reset_gpiod, 1); + if (bus->reset_gpiod) + gpiod_set_value_cansleep(bus->reset_gpiod, 1); device_del(&bus->dev); } diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index fb2cf4342f48..baee371bf767 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -58,11 +58,11 @@ enum { }; static const char *gpio_of_names[] = { - "moddef0", + "mod-def0", "los", "tx-fault", "tx-disable", - "rate-select", + "rate-select0", }; static const enum gpiod_flags gpio_flags[] = { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index f1b60740e020..53ae30259989 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -159,7 +159,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_firmware_capabilities(ifp); memset(&gscan_cfg, 0, sizeof(gscan_cfg)); - if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID) + if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID && + drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID) brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN, "pfn_gscan_cfg", &gscan_cfg, sizeof(gscan_cfg)); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 887f6d8fc8a7..279248cd9cfb 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -378,6 +378,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG = (__force iwl_ucode_tlv_capa_t)80, IWL_UCODE_TLV_CAPA_LQM_SUPPORT = (__force iwl_ucode_tlv_capa_t)81, IWL_UCODE_TLV_CAPA_TX_POWER_ACK = (__force iwl_ucode_tlv_capa_t)84, + IWL_UCODE_TLV_CAPA_LED_CMD_SUPPORT = (__force iwl_ucode_tlv_capa_t)86, IWL_UCODE_TLV_CAPA_MLME_OFFLOAD = (__force iwl_ucode_tlv_capa_t)96, NUM_IWL_UCODE_TLV_CAPA diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c index 005e2e7278a5..b27269504a62 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c @@ -92,7 +92,8 @@ static void iwl_mvm_send_led_fw_cmd(struct iwl_mvm *mvm, bool on) static void iwl_mvm_led_set(struct iwl_mvm *mvm, bool on) { - if (mvm->cfg->device_family >= IWL_DEVICE_FAMILY_8000) { + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_LED_CMD_SUPPORT)) { iwl_mvm_send_led_fw_cmd(mvm, on); return; } diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index c8852acc1462..6467ffac9811 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1362,8 +1362,6 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, txi->control.rates, ARRAY_SIZE(txi->control.rates)); - txi->rate_driver_data[0] = channel; - if (skb->len >= 24 + 8 && ieee80211_is_probe_resp(hdr->frame_control)) { /* fake header transmission time */ diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 31965f0ef69d..e8f07573aed9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1183,7 +1183,10 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, } /* fixed internal switch S1->WiFi, S0->BT */ - btcoexist->btc_write_4byte(btcoexist, 0x948, 0x0); + if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); + else + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); switch (antpos_type) { case BTC_ANT_WIFI_AT_MAIN: diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c index c1eacd8352a2..b5e9877d935c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c @@ -173,6 +173,16 @@ static u8 halbtc_get_wifi_central_chnl(struct btc_coexist *btcoexist) u8 rtl_get_hwpg_single_ant_path(struct rtl_priv *rtlpriv) { + struct rtl_mod_params *mod_params = rtlpriv->cfg->mod_params; + + /* override ant_num / ant_path */ + if (mod_params->ant_sel) { + rtlpriv->btcoexist.btc_info.ant_num = + (mod_params->ant_sel == 1 ? ANT_X2 : ANT_X1); + + rtlpriv->btcoexist.btc_info.single_ant_path = + (mod_params->ant_sel == 1 ? 0 : 1); + } return rtlpriv->btcoexist.btc_info.single_ant_path; } @@ -183,6 +193,7 @@ u8 rtl_get_hwpg_bt_type(struct rtl_priv *rtlpriv) u8 rtl_get_hwpg_ant_num(struct rtl_priv *rtlpriv) { + struct rtl_mod_params *mod_params = rtlpriv->cfg->mod_params; u8 num; if (rtlpriv->btcoexist.btc_info.ant_num == ANT_X2) @@ -190,6 +201,10 @@ u8 rtl_get_hwpg_ant_num(struct rtl_priv *rtlpriv) else num = 1; + /* override ant_num / ant_path */ + if (mod_params->ant_sel) + num = (mod_params->ant_sel == 1 ? ANT_X2 : ANT_X1) + 1; + return num; } @@ -876,7 +891,7 @@ bool exhalbtc_bind_bt_coex_withadapter(void *adapter) { struct btc_coexist *btcoexist = &gl_bt_coexist; struct rtl_priv *rtlpriv = adapter; - u8 ant_num = 2, chip_type, single_ant_path = 0; + u8 ant_num = 2, chip_type; if (btcoexist->binded) return false; @@ -911,12 +926,6 @@ bool exhalbtc_bind_bt_coex_withadapter(void *adapter) ant_num = rtl_get_hwpg_ant_num(rtlpriv); exhalbtc_set_ant_num(rtlpriv, BT_COEX_ANT_TYPE_PG, ant_num); - /* set default antenna position to main port */ - btcoexist->board_info.btdm_ant_pos = BTC_ANTENNA_AT_MAIN_PORT; - - single_ant_path = rtl_get_hwpg_single_ant_path(rtlpriv); - exhalbtc_set_single_ant_path(single_ant_path); - if (rtl_get_hwpg_package_type(rtlpriv) == 0) btcoexist->board_info.tfbga_package = false; else if (rtl_get_hwpg_package_type(rtlpriv) == 1) diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index 60491641a8d6..d5612bd1cc81 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -31,6 +31,16 @@ enum log_ent_request { LOG_OLD_ENT }; +static struct device *to_dev(struct arena_info *arena) +{ + return &arena->nd_btt->dev; +} + +static u64 adjust_initial_offset(struct nd_btt *nd_btt, u64 offset) +{ + return offset + nd_btt->initial_offset; +} + static int arena_read_bytes(struct arena_info *arena, resource_size_t offset, void *buf, size_t n, unsigned long flags) { @@ -38,7 +48,7 @@ static int arena_read_bytes(struct arena_info *arena, resource_size_t offset, struct nd_namespace_common *ndns = nd_btt->ndns; /* arena offsets may be shifted from the base of the device */ - offset += arena->nd_btt->initial_offset; + offset = adjust_initial_offset(nd_btt, offset); return nvdimm_read_bytes(ndns, offset, buf, n, flags); } @@ -49,7 +59,7 @@ static int arena_write_bytes(struct arena_info *arena, resource_size_t offset, struct nd_namespace_common *ndns = nd_btt->ndns; /* arena offsets may be shifted from the base of the device */ - offset += arena->nd_btt->initial_offset; + offset = adjust_initial_offset(nd_btt, offset); return nvdimm_write_bytes(ndns, offset, buf, n, flags); } @@ -62,8 +72,10 @@ static int btt_info_write(struct arena_info *arena, struct btt_sb *super) * We rely on that to make sure rw_bytes does error clearing * correctly, so make sure that is the case. */ - WARN_ON_ONCE(!IS_ALIGNED(arena->infooff, 512)); - WARN_ON_ONCE(!IS_ALIGNED(arena->info2off, 512)); + dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->infooff, 512), + "arena->infooff: %#llx is unaligned\n", arena->infooff); + dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->info2off, 512), + "arena->info2off: %#llx is unaligned\n", arena->info2off); ret = arena_write_bytes(arena, arena->info2off, super, sizeof(struct btt_sb), 0); @@ -76,7 +88,6 @@ static int btt_info_write(struct arena_info *arena, struct btt_sb *super) static int btt_info_read(struct arena_info *arena, struct btt_sb *super) { - WARN_ON(!super); return arena_read_bytes(arena, arena->infooff, super, sizeof(struct btt_sb), 0); } @@ -92,7 +103,10 @@ static int __btt_map_write(struct arena_info *arena, u32 lba, __le32 mapping, { u64 ns_off = arena->mapoff + (lba * MAP_ENT_SIZE); - WARN_ON(lba >= arena->external_nlba); + if (unlikely(lba >= arena->external_nlba)) + dev_err_ratelimited(to_dev(arena), + "%s: lba %#x out of range (max: %#x)\n", + __func__, lba, arena->external_nlba); return arena_write_bytes(arena, ns_off, &mapping, MAP_ENT_SIZE, flags); } @@ -106,7 +120,7 @@ static int btt_map_write(struct arena_info *arena, u32 lba, u32 mapping, * This 'mapping' is supposed to be just the LBA mapping, without * any flags set, so strip the flag bits. */ - mapping &= MAP_LBA_MASK; + mapping = ent_lba(mapping); ze = (z_flag << 1) + e_flag; switch (ze) { @@ -131,7 +145,8 @@ static int btt_map_write(struct arena_info *arena, u32 lba, u32 mapping, * construed as a valid 'normal' case, but we decide not to, * to avoid confusion */ - WARN_ONCE(1, "Invalid use of Z and E flags\n"); + dev_err_ratelimited(to_dev(arena), + "Invalid use of Z and E flags\n"); return -EIO; } @@ -147,7 +162,10 @@ static int btt_map_read(struct arena_info *arena, u32 lba, u32 *mapping, u32 raw_mapping, postmap, ze, z_flag, e_flag; u64 ns_off = arena->mapoff + (lba * MAP_ENT_SIZE); - WARN_ON(lba >= arena->external_nlba); + if (unlikely(lba >= arena->external_nlba)) + dev_err_ratelimited(to_dev(arena), + "%s: lba %#x out of range (max: %#x)\n", + __func__, lba, arena->external_nlba); ret = arena_read_bytes(arena, ns_off, &in, MAP_ENT_SIZE, rwb_flags); if (ret) @@ -155,10 +173,10 @@ static int btt_map_read(struct arena_info *arena, u32 lba, u32 *mapping, raw_mapping = le32_to_cpu(in); - z_flag = (raw_mapping & MAP_TRIM_MASK) >> MAP_TRIM_SHIFT; - e_flag = (raw_mapping & MAP_ERR_MASK) >> MAP_ERR_SHIFT; + z_flag = ent_z_flag(raw_mapping); + e_flag = ent_e_flag(raw_mapping); ze = (z_flag << 1) + e_flag; - postmap = raw_mapping & MAP_LBA_MASK; + postmap = ent_lba(raw_mapping); /* Reuse the {z,e}_flag variables for *trim and *error */ z_flag = 0; @@ -195,7 +213,6 @@ static int btt_map_read(struct arena_info *arena, u32 lba, u32 *mapping, static int btt_log_read_pair(struct arena_info *arena, u32 lane, struct log_entry *ent) { - WARN_ON(!ent); return arena_read_bytes(arena, arena->logoff + (2 * lane * LOG_ENT_SIZE), ent, 2 * LOG_ENT_SIZE, 0); @@ -299,11 +316,6 @@ static int btt_log_get_old(struct log_entry *ent) return old; } -static struct device *to_dev(struct arena_info *arena) -{ - return &arena->nd_btt->dev; -} - /* * This function copies the desired (old/new) log entry into ent if * it is not NULL. It returns the sub-slot number (0 or 1) @@ -381,7 +393,9 @@ static int btt_flog_write(struct arena_info *arena, u32 lane, u32 sub, arena->freelist[lane].sub = 1 - arena->freelist[lane].sub; if (++(arena->freelist[lane].seq) == 4) arena->freelist[lane].seq = 1; - arena->freelist[lane].block = le32_to_cpu(ent->old_map); + if (ent_e_flag(ent->old_map)) + arena->freelist[lane].has_err = 1; + arena->freelist[lane].block = le32_to_cpu(ent_lba(ent->old_map)); return ret; } @@ -407,12 +421,14 @@ static int btt_map_init(struct arena_info *arena) * make sure rw_bytes does error clearing correctly, so make sure that * is the case. */ - WARN_ON_ONCE(!IS_ALIGNED(arena->mapoff, 512)); + dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->mapoff, 512), + "arena->mapoff: %#llx is unaligned\n", arena->mapoff); while (mapsize) { size_t size = min(mapsize, chunk_size); - WARN_ON_ONCE(size < 512); + dev_WARN_ONCE(to_dev(arena), size < 512, + "chunk size: %#zx is unaligned\n", size); ret = arena_write_bytes(arena, arena->mapoff + offset, zerobuf, size, 0); if (ret) @@ -449,12 +465,14 @@ static int btt_log_init(struct arena_info *arena) * make sure rw_bytes does error clearing correctly, so make sure that * is the case. */ - WARN_ON_ONCE(!IS_ALIGNED(arena->logoff, 512)); + dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->logoff, 512), + "arena->logoff: %#llx is unaligned\n", arena->logoff); while (logsize) { size_t size = min(logsize, chunk_size); - WARN_ON_ONCE(size < 512); + dev_WARN_ONCE(to_dev(arena), size < 512, + "chunk size: %#zx is unaligned\n", size); ret = arena_write_bytes(arena, arena->logoff + offset, zerobuf, size, 0); if (ret) @@ -480,6 +498,40 @@ static int btt_log_init(struct arena_info *arena) return ret; } +static u64 to_namespace_offset(struct arena_info *arena, u64 lba) +{ + return arena->dataoff + ((u64)lba * arena->internal_lbasize); +} + +static int arena_clear_freelist_error(struct arena_info *arena, u32 lane) +{ + int ret = 0; + + if (arena->freelist[lane].has_err) { + void *zero_page = page_address(ZERO_PAGE(0)); + u32 lba = arena->freelist[lane].block; + u64 nsoff = to_namespace_offset(arena, lba); + unsigned long len = arena->sector_size; + + mutex_lock(&arena->err_lock); + + while (len) { + unsigned long chunk = min(len, PAGE_SIZE); + + ret = arena_write_bytes(arena, nsoff, zero_page, + chunk, 0); + if (ret) + break; + len -= chunk; + nsoff += chunk; + if (len == 0) + arena->freelist[lane].has_err = 0; + } + mutex_unlock(&arena->err_lock); + } + return ret; +} + static int btt_freelist_init(struct arena_info *arena) { int old, new, ret; @@ -505,6 +557,17 @@ static int btt_freelist_init(struct arena_info *arena) arena->freelist[i].seq = nd_inc_seq(le32_to_cpu(log_new.seq)); arena->freelist[i].block = le32_to_cpu(log_new.old_map); + /* + * FIXME: if error clearing fails during init, we want to make + * the BTT read-only + */ + if (ent_e_flag(log_new.old_map)) { + ret = arena_clear_freelist_error(arena, i); + if (ret) + dev_err_ratelimited(to_dev(arena), + "Unable to clear known errors\n"); + } + /* This implies a newly created or untouched flog entry */ if (log_new.old_map == log_new.new_map) continue; @@ -525,7 +588,6 @@ static int btt_freelist_init(struct arena_info *arena) if (ret) return ret; } - } return 0; @@ -566,6 +628,7 @@ static struct arena_info *alloc_arena(struct btt *btt, size_t size, if (!arena) return NULL; arena->nd_btt = btt->nd_btt; + arena->sector_size = btt->sector_size; if (!size) return arena; @@ -694,6 +757,7 @@ static int discover_arenas(struct btt *btt) arena->external_lba_start = cur_nlba; parse_arena_meta(arena, super, cur_off); + mutex_init(&arena->err_lock); ret = btt_freelist_init(arena); if (ret) goto out; @@ -904,11 +968,6 @@ static void unlock_map(struct arena_info *arena, u32 premap) spin_unlock(&arena->map_locks[idx].lock); } -static u64 to_namespace_offset(struct arena_info *arena, u64 lba) -{ - return arena->dataoff + ((u64)lba * arena->internal_lbasize); -} - static int btt_data_read(struct arena_info *arena, struct page *page, unsigned int off, u32 lba, u32 len) { @@ -1032,6 +1091,7 @@ static int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip, */ while (1) { u32 new_map; + int new_t, new_e; if (t_flag) { zero_fill_data(page, off, cur_len); @@ -1050,20 +1110,29 @@ static int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip, */ barrier(); - ret = btt_map_read(arena, premap, &new_map, &t_flag, - &e_flag, NVDIMM_IO_ATOMIC); + ret = btt_map_read(arena, premap, &new_map, &new_t, + &new_e, NVDIMM_IO_ATOMIC); if (ret) goto out_rtt; - if (postmap == new_map) + if ((postmap == new_map) && (t_flag == new_t) && + (e_flag == new_e)) break; postmap = new_map; + t_flag = new_t; + e_flag = new_e; } ret = btt_data_read(arena, page, off, postmap, cur_len); - if (ret) + if (ret) { + int rc; + + /* Media error - set the e_flag */ + rc = btt_map_write(arena, premap, postmap, 0, 1, + NVDIMM_IO_ATOMIC); goto out_rtt; + } if (bip) { ret = btt_rw_integrity(btt, bip, arena, postmap, READ); @@ -1088,6 +1157,21 @@ static int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip, return ret; } +/* + * Normally, arena_{read,write}_bytes will take care of the initial offset + * adjustment, but in the case of btt_is_badblock, where we query is_bad_pmem, + * we need the final, raw namespace offset here + */ +static bool btt_is_badblock(struct btt *btt, struct arena_info *arena, + u32 postmap) +{ + u64 nsoff = adjust_initial_offset(arena->nd_btt, + to_namespace_offset(arena, postmap)); + sector_t phys_sector = nsoff >> 9; + + return is_bad_pmem(btt->phys_bb, phys_sector, arena->internal_lbasize); +} + static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip, sector_t sector, struct page *page, unsigned int off, unsigned int len) @@ -1100,7 +1184,9 @@ static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip, while (len) { u32 cur_len; + int e_flag; + retry: lane = nd_region_acquire_lane(btt->nd_region); ret = lba_to_arena(btt, sector, &premap, &arena); @@ -1113,6 +1199,21 @@ static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip, goto out_lane; } + if (btt_is_badblock(btt, arena, arena->freelist[lane].block)) + arena->freelist[lane].has_err = 1; + + if (mutex_is_locked(&arena->err_lock) + || arena->freelist[lane].has_err) { + nd_region_release_lane(btt->nd_region, lane); + + ret = arena_clear_freelist_error(arena, lane); + if (ret) + return ret; + + /* OK to acquire a different lane/free block */ + goto retry; + } + new_postmap = arena->freelist[lane].block; /* Wait if the new block is being read from */ @@ -1138,7 +1239,7 @@ static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip, } lock_map(arena, premap); - ret = btt_map_read(arena, premap, &old_postmap, NULL, NULL, + ret = btt_map_read(arena, premap, &old_postmap, NULL, &e_flag, NVDIMM_IO_ATOMIC); if (ret) goto out_map; @@ -1146,6 +1247,8 @@ static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip, ret = -EIO; goto out_map; } + if (e_flag) + set_e_flag(old_postmap); log.lba = cpu_to_le32(premap); log.old_map = cpu_to_le32(old_postmap); @@ -1156,13 +1259,20 @@ static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip, if (ret) goto out_map; - ret = btt_map_write(arena, premap, new_postmap, 0, 0, 0); + ret = btt_map_write(arena, premap, new_postmap, 0, 0, + NVDIMM_IO_ATOMIC); if (ret) goto out_map; unlock_map(arena, premap); nd_region_release_lane(btt->nd_region, lane); + if (e_flag) { + ret = arena_clear_freelist_error(arena, lane); + if (ret) + return ret; + } + len -= cur_len; off += cur_len; sector += btt->sector_size >> SECTOR_SHIFT; @@ -1211,11 +1321,13 @@ static blk_qc_t btt_make_request(struct request_queue *q, struct bio *bio) bio_for_each_segment(bvec, bio, iter) { unsigned int len = bvec.bv_len; - BUG_ON(len > PAGE_SIZE); - /* Make sure len is in multiples of sector size. */ - /* XXX is this right? */ - BUG_ON(len < btt->sector_size); - BUG_ON(len % btt->sector_size); + if (len > PAGE_SIZE || len < btt->sector_size || + len % btt->sector_size) { + dev_err_ratelimited(&btt->nd_btt->dev, + "unaligned bio segment (len: %d)\n", len); + bio->bi_status = BLK_STS_IOERR; + break; + } err = btt_do_bvec(btt, bip, bvec.bv_page, len, bvec.bv_offset, op_is_write(bio_op(bio)), iter.bi_sector); @@ -1345,6 +1457,7 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize, { int ret; struct btt *btt; + struct nd_namespace_io *nsio; struct device *dev = &nd_btt->dev; btt = devm_kzalloc(dev, sizeof(struct btt), GFP_KERNEL); @@ -1358,6 +1471,8 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize, INIT_LIST_HEAD(&btt->arena_list); mutex_init(&btt->init_lock); btt->nd_region = nd_region; + nsio = to_nd_namespace_io(&nd_btt->ndns->dev); + btt->phys_bb = &nsio->bb; ret = discover_arenas(btt); if (ret) { @@ -1431,6 +1546,8 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns) } btt_sb = devm_kzalloc(&nd_btt->dev, sizeof(*btt_sb), GFP_KERNEL); + if (!btt_sb) + return -ENOMEM; /* * If this returns < 0, that is ok as it just means there wasn't diff --git a/drivers/nvdimm/btt.h b/drivers/nvdimm/btt.h index 888e862907a0..578c2057524d 100644 --- a/drivers/nvdimm/btt.h +++ b/drivers/nvdimm/btt.h @@ -15,6 +15,7 @@ #ifndef _LINUX_BTT_H #define _LINUX_BTT_H +#include <linux/badblocks.h> #include <linux/types.h> #define BTT_SIG_LEN 16 @@ -38,6 +39,11 @@ #define IB_FLAG_ERROR 0x00000001 #define IB_FLAG_ERROR_MASK 0x00000001 +#define ent_lba(ent) (ent & MAP_LBA_MASK) +#define ent_e_flag(ent) (!!(ent & MAP_ERR_MASK)) +#define ent_z_flag(ent) (!!(ent & MAP_TRIM_MASK)) +#define set_e_flag(ent) (ent |= MAP_ERR_MASK) + enum btt_init_state { INIT_UNCHECKED = 0, INIT_NOTFOUND, @@ -78,6 +84,7 @@ struct free_entry { u32 block; u8 sub; u8 seq; + u8 has_err; }; struct aligned_lock { @@ -104,6 +111,7 @@ struct aligned_lock { * handle incoming writes. * @version_major: Metadata layout version major. * @version_minor: Metadata layout version minor. + * @sector_size: The Linux sector size - 512 or 4096 * @nextoff: Offset in bytes to the start of the next arena. * @infooff: Offset in bytes to the info block of this arena. * @dataoff: Offset in bytes to the data area of this arena. @@ -131,6 +139,7 @@ struct arena_info { u32 nfree; u16 version_major; u16 version_minor; + u32 sector_size; /* Byte offsets to the different on-media structures */ u64 nextoff; u64 infooff; @@ -147,6 +156,7 @@ struct arena_info { struct dentry *debugfs_dir; /* Arena flags */ u32 flags; + struct mutex err_lock; }; /** @@ -181,6 +191,7 @@ struct btt { struct mutex init_lock; int init_state; int num_arenas; + struct badblocks *phys_bb; }; bool nd_btt_arena_is_valid(struct nd_btt *nd_btt, struct btt_sb *super); diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c index 3e359d282f8e..d58925295aa7 100644 --- a/drivers/nvdimm/btt_devs.c +++ b/drivers/nvdimm/btt_devs.c @@ -61,7 +61,7 @@ static ssize_t sector_size_show(struct device *dev, { struct nd_btt *nd_btt = to_nd_btt(dev); - return nd_sector_size_show(nd_btt->lbasize, btt_lbasize_supported, buf); + return nd_size_select_show(nd_btt->lbasize, btt_lbasize_supported, buf); } static ssize_t sector_size_store(struct device *dev, @@ -72,7 +72,7 @@ static ssize_t sector_size_store(struct device *dev, device_lock(dev); nvdimm_bus_lock(dev); - rc = nd_sector_size_store(dev, buf, &nd_btt->lbasize, + rc = nd_size_select_store(dev, buf, &nd_btt->lbasize, btt_lbasize_supported); dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, rc, buf, buf[len - 1] == '\n' ? "" : "\n"); diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 937fafa1886a..baf283986a7e 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -11,6 +11,7 @@ * General Public License for more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/sched/mm.h> #include <linux/vmalloc.h> #include <linux/uaccess.h> #include <linux/module.h> @@ -234,6 +235,7 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, struct nd_cmd_clear_error clear_err; struct nd_cmd_ars_cap ars_cap; u32 clear_err_unit, mask; + unsigned int noio_flag; int cmd_rc, rc; if (!nvdimm_bus) @@ -250,8 +252,10 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, memset(&ars_cap, 0, sizeof(ars_cap)); ars_cap.address = phys; ars_cap.length = len; + noio_flag = memalloc_noio_save(); rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, &ars_cap, sizeof(ars_cap), &cmd_rc); + memalloc_noio_restore(noio_flag); if (rc < 0) return rc; if (cmd_rc < 0) @@ -266,8 +270,10 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, memset(&clear_err, 0, sizeof(clear_err)); clear_err.address = phys; clear_err.length = len; + noio_flag = memalloc_noio_save(); rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CLEAR_ERROR, &clear_err, sizeof(clear_err), &cmd_rc); + memalloc_noio_restore(noio_flag); if (rc < 0) return rc; if (cmd_rc < 0) @@ -905,19 +911,20 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, int read_only, unsigned int ioctl_cmd, unsigned long arg) { struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; - size_t buf_len = 0, in_len = 0, out_len = 0; static char out_env[ND_CMD_MAX_ENVELOPE]; static char in_env[ND_CMD_MAX_ENVELOPE]; const struct nd_cmd_desc *desc = NULL; unsigned int cmd = _IOC_NR(ioctl_cmd); - unsigned int func = cmd; - void __user *p = (void __user *) arg; struct device *dev = &nvdimm_bus->dev; - struct nd_cmd_pkg pkg; + void __user *p = (void __user *) arg; const char *cmd_name, *dimm_name; + u32 in_len = 0, out_len = 0; + unsigned int func = cmd; unsigned long cmd_mask; - void *buf; + struct nd_cmd_pkg pkg; int rc, i, cmd_rc; + u64 buf_len = 0; + void *buf; if (nvdimm) { desc = nd_cmd_dimm_desc(cmd); @@ -977,13 +984,9 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, if (cmd == ND_CMD_CALL) { func = pkg.nd_command; - dev_dbg(dev, "%s:%s, idx: %llu, in: %zu, out: %zu, len %zu\n", + dev_dbg(dev, "%s:%s, idx: %llu, in: %u, out: %u, len %llu\n", __func__, dimm_name, pkg.nd_command, in_len, out_len, buf_len); - - for (i = 0; i < ARRAY_SIZE(pkg.nd_reserved2); i++) - if (pkg.nd_reserved2[i]) - return -EINVAL; } /* process an output envelope */ @@ -1007,9 +1010,9 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, out_len += out_size; } - buf_len = out_len + in_len; + buf_len = (u64) out_len + (u64) in_len; if (buf_len > ND_IOCTL_MAX_BUFLEN) { - dev_dbg(dev, "%s:%s cmd: %s buf_len: %zu > %d\n", __func__, + dev_dbg(dev, "%s:%s cmd: %s buf_len: %llu > %d\n", __func__, dimm_name, cmd_name, buf_len, ND_IOCTL_MAX_BUFLEN); return -EINVAL; diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c index 47770460f3d3..b2fc29b8279b 100644 --- a/drivers/nvdimm/claim.c +++ b/drivers/nvdimm/claim.c @@ -280,18 +280,11 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns, } if (unlikely(is_bad_pmem(&nsio->bb, sector, sz_align))) { - /* - * FIXME: nsio_rw_bytes() may be called from atomic - * context in the btt case and the ACPI DSM path for - * clearing the error takes sleeping locks and allocates - * memory. An explicit error clearing path, and support - * for tracking badblocks in BTT metadata is needed to - * work around this collision. - */ if (IS_ALIGNED(offset, 512) && IS_ALIGNED(size, 512) && !(flags & NVDIMM_IO_ATOMIC)) { long cleared; + might_sleep(); cleared = nvdimm_clear_poison(&ndns->dev, nsio->res.start + offset, size); if (cleared < size) diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index 75bc08c6838c..bb71f0cf8f5d 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -277,14 +277,14 @@ int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf, return 0; } -ssize_t nd_sector_size_show(unsigned long current_lbasize, +ssize_t nd_size_select_show(unsigned long current_size, const unsigned long *supported, char *buf) { ssize_t len = 0; int i; for (i = 0; supported[i]; i++) - if (current_lbasize == supported[i]) + if (current_size == supported[i]) len += sprintf(buf + len, "[%ld] ", supported[i]); else len += sprintf(buf + len, "%ld ", supported[i]); @@ -292,8 +292,8 @@ ssize_t nd_sector_size_show(unsigned long current_lbasize, return len; } -ssize_t nd_sector_size_store(struct device *dev, const char *buf, - unsigned long *current_lbasize, const unsigned long *supported) +ssize_t nd_size_select_store(struct device *dev, const char *buf, + unsigned long *current_size, const unsigned long *supported) { unsigned long lbasize; int rc, i; @@ -310,7 +310,7 @@ ssize_t nd_sector_size_store(struct device *dev, const char *buf, break; if (supported[i]) { - *current_lbasize = lbasize; + *current_size = lbasize; return 0; } else { return -EINVAL; diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c index 87796f840777..9c5f108910e3 100644 --- a/drivers/nvdimm/label.c +++ b/drivers/nvdimm/label.c @@ -45,12 +45,14 @@ unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd) return ndd->nslabel_size; } -size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd) +int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd) { - u32 index_span; + return ndd->nsarea.config_size / (sizeof_namespace_label(ndd) + 1); +} - if (ndd->nsindex_size) - return ndd->nsindex_size; +size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd) +{ + u32 nslot, space, size; /* * The minimum index space is 512 bytes, with that amount of @@ -60,16 +62,16 @@ size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd) * starts to waste space at larger config_sizes, but it's * unlikely we'll ever see anything but 128K. */ - index_span = ndd->nsarea.config_size / (sizeof_namespace_label(ndd) + 1); - index_span /= NSINDEX_ALIGN * 2; - ndd->nsindex_size = index_span * NSINDEX_ALIGN; - - return ndd->nsindex_size; -} - -int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd) -{ - return ndd->nsarea.config_size / (sizeof_namespace_label(ndd) + 1); + nslot = nvdimm_num_label_slots(ndd); + space = ndd->nsarea.config_size - nslot * sizeof_namespace_label(ndd); + size = ALIGN(sizeof(struct nd_namespace_index) + DIV_ROUND_UP(nslot, 8), + NSINDEX_ALIGN) * 2; + if (size <= space) + return size / 2; + + dev_err(ndd->dev, "label area (%d) too small to host (%d byte) labels\n", + ndd->nsarea.config_size, sizeof_namespace_label(ndd)); + return 0; } static int __nd_label_validate(struct nvdimm_drvdata *ndd) diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 5f1c6756e57c..1427a386a033 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -1313,14 +1313,14 @@ static ssize_t sector_size_show(struct device *dev, if (is_namespace_blk(dev)) { struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); - return nd_sector_size_show(nsblk->lbasize, + return nd_size_select_show(nsblk->lbasize, blk_lbasize_supported, buf); } if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); - return nd_sector_size_show(nspm->lbasize, + return nd_size_select_show(nspm->lbasize, pmem_lbasize_supported, buf); } return -ENXIO; @@ -1352,7 +1352,7 @@ static ssize_t sector_size_store(struct device *dev, if (to_ndns(dev)->claim) rc = -EBUSY; if (rc >= 0) - rc = nd_sector_size_store(dev, buf, lbasize, supported); + rc = nd_size_select_store(dev, buf, lbasize, supported); if (rc >= 0) rc = nd_namespace_label_update(nd_region, dev); dev_dbg(dev, "%s: result: %zd %s: %s%s", __func__, diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index a87f793f2945..9c758a91372b 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -42,7 +42,7 @@ struct nd_poison { struct nvdimm_drvdata { struct device *dev; - int nsindex_size, nslabel_size; + int nslabel_size; struct nd_cmd_get_config_size nsarea; void *data; int ns_current, ns_next; @@ -134,6 +134,7 @@ struct nd_mapping { struct nvdimm *nvdimm; u64 start; u64 size; + int position; struct list_head labels; struct mutex lock; /* @@ -233,10 +234,10 @@ void nd_device_unregister(struct device *dev, enum nd_async_mode mode); void nd_device_notify(struct device *dev, enum nvdimm_event event); int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf, size_t len); -ssize_t nd_sector_size_show(unsigned long current_lbasize, +ssize_t nd_size_select_show(unsigned long current_size, const unsigned long *supported, char *buf); -ssize_t nd_sector_size_store(struct device *dev, const char *buf, - unsigned long *current_lbasize, const unsigned long *supported); +ssize_t nd_size_select_store(struct device *dev, const char *buf, + unsigned long *current_size, const unsigned long *supported); int __init nvdimm_init(void); int __init nd_region_init(void); int __init nd_label_init(void); @@ -285,6 +286,13 @@ static inline struct device *nd_btt_create(struct nd_region *nd_region) struct nd_pfn *to_nd_pfn(struct device *dev); #if IS_ENABLED(CONFIG_NVDIMM_PFN) + +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +#define PFN_DEFAULT_ALIGNMENT HPAGE_PMD_SIZE +#else +#define PFN_DEFAULT_ALIGNMENT PAGE_SIZE +#endif + int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns); bool is_nd_pfn(struct device *dev); struct device *nd_pfn_create(struct nd_region *nd_region); diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 5fcb6f5b22a2..9576c444f0ab 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -111,24 +111,27 @@ static ssize_t align_show(struct device *dev, return sprintf(buf, "%ld\n", nd_pfn->align); } -static ssize_t __align_store(struct nd_pfn *nd_pfn, const char *buf) +static const unsigned long *nd_pfn_supported_alignments(void) { - unsigned long val; - int rc; - - rc = kstrtoul(buf, 0, &val); - if (rc) - return rc; - - if (!is_power_of_2(val) || val < PAGE_SIZE || val > SZ_1G) - return -EINVAL; + /* + * This needs to be a non-static variable because the *_SIZE + * macros aren't always constants. + */ + const unsigned long supported_alignments[] = { + PAGE_SIZE, +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + HPAGE_PMD_SIZE, +#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD + HPAGE_PUD_SIZE, +#endif +#endif + 0, + }; + static unsigned long data[ARRAY_SIZE(supported_alignments)]; - if (nd_pfn->dev.driver) - return -EBUSY; - else - nd_pfn->align = val; + memcpy(data, supported_alignments, sizeof(data)); - return 0; + return data; } static ssize_t align_store(struct device *dev, @@ -139,7 +142,8 @@ static ssize_t align_store(struct device *dev, device_lock(dev); nvdimm_bus_lock(dev); - rc = __align_store(nd_pfn, buf); + rc = nd_size_select_store(dev, buf, &nd_pfn->align, + nd_pfn_supported_alignments()); dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, rc, buf, buf[len - 1] == '\n' ? "" : "\n"); nvdimm_bus_unlock(dev); @@ -260,6 +264,13 @@ static ssize_t size_show(struct device *dev, } static DEVICE_ATTR_RO(size); +static ssize_t supported_alignments_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return nd_size_select_show(0, nd_pfn_supported_alignments(), buf); +} +static DEVICE_ATTR_RO(supported_alignments); + static struct attribute *nd_pfn_attributes[] = { &dev_attr_mode.attr, &dev_attr_namespace.attr, @@ -267,6 +278,7 @@ static struct attribute *nd_pfn_attributes[] = { &dev_attr_align.attr, &dev_attr_resource.attr, &dev_attr_size.attr, + &dev_attr_supported_alignments.attr, NULL, }; @@ -290,7 +302,7 @@ struct device *nd_pfn_devinit(struct nd_pfn *nd_pfn, return NULL; nd_pfn->mode = PFN_MODE_NONE; - nd_pfn->align = HPAGE_SIZE; + nd_pfn->align = PFN_DEFAULT_ALIGNMENT; dev = &nd_pfn->dev; device_initialize(&nd_pfn->dev); if (ndns && !__nd_attach_ndns(&nd_pfn->dev, ndns, &nd_pfn->ndns)) { @@ -638,11 +650,12 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) / PAGE_SIZE); if (nd_pfn->mode == PFN_MODE_PMEM) { /* - * vmemmap_populate_hugepages() allocates the memmap array in - * HPAGE_SIZE chunks. + * The altmap should be padded out to the block size used + * when populating the vmemmap. This *should* be equal to + * PMD_SIZE for most architectures. */ offset = ALIGN(start + SZ_8K + 64 * npfns + dax_label_reserve, - max(nd_pfn->align, HPAGE_SIZE)) - start; + max(nd_pfn->align, PMD_SIZE)) - start; } else if (nd_pfn->mode == PFN_MODE_RAM) offset = ALIGN(start + SZ_8K + dax_label_reserve, nd_pfn->align) - start; diff --git a/drivers/nvdimm/pmem.h b/drivers/nvdimm/pmem.h index 5434321cad67..c5917f040fa7 100644 --- a/drivers/nvdimm/pmem.h +++ b/drivers/nvdimm/pmem.h @@ -5,20 +5,6 @@ #include <linux/pfn_t.h> #include <linux/fs.h> -#ifdef CONFIG_ARCH_HAS_PMEM_API -#define ARCH_MEMREMAP_PMEM MEMREMAP_WB -void arch_wb_cache_pmem(void *addr, size_t size); -void arch_invalidate_pmem(void *addr, size_t size); -#else -#define ARCH_MEMREMAP_PMEM MEMREMAP_WT -static inline void arch_wb_cache_pmem(void *addr, size_t size) -{ -} -static inline void arch_invalidate_pmem(void *addr, size_t size) -{ -} -#endif - /* this definition is in it's own header for tools/testing/nvdimm to consume */ struct pmem_device { /* One contiguous memory region per device */ diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 5954cfbea3fc..829d760f651c 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -723,8 +723,9 @@ static ssize_t mappingN(struct device *dev, char *buf, int n) nd_mapping = &nd_region->mapping[n]; nvdimm = nd_mapping->nvdimm; - return sprintf(buf, "%s,%llu,%llu\n", dev_name(&nvdimm->dev), - nd_mapping->start, nd_mapping->size); + return sprintf(buf, "%s,%llu,%llu,%d\n", dev_name(&nvdimm->dev), + nd_mapping->start, nd_mapping->size, + nd_mapping->position); } #define REGION_MAPPING(idx) \ @@ -965,6 +966,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, nd_region->mapping[i].nvdimm = nvdimm; nd_region->mapping[i].start = mapping->start; nd_region->mapping[i].size = mapping->size; + nd_region->mapping[i].position = mapping->position; INIT_LIST_HEAD(&nd_region->mapping[i].labels); mutex_init(&nd_region->mapping[i].lock); diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index c596dd3c58b1..acc816b67582 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -76,6 +76,11 @@ static DEFINE_SPINLOCK(dev_list_lock); static struct class *nvme_class; +static __le32 nvme_get_log_dw10(u8 lid, size_t size) +{ + return cpu_to_le32((((size / 4) - 1) << 16) | lid); +} + int nvme_reset_ctrl(struct nvme_ctrl *ctrl) { if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING)) @@ -108,7 +113,16 @@ static blk_status_t nvme_error_status(struct request *req) case NVME_SC_WRITE_FAULT: case NVME_SC_READ_ERROR: case NVME_SC_UNWRITTEN_BLOCK: + case NVME_SC_ACCESS_DENIED: + case NVME_SC_READ_ONLY: return BLK_STS_MEDIUM; + case NVME_SC_GUARD_CHECK: + case NVME_SC_APPTAG_CHECK: + case NVME_SC_REFTAG_CHECK: + case NVME_SC_INVALID_PI: + return BLK_STS_PROTECTION; + case NVME_SC_RESERVATION_CONFLICT: + return BLK_STS_NEXUS; default: return BLK_STS_IOERR; } @@ -162,9 +176,10 @@ bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl, enum nvme_ctrl_state new_state) { enum nvme_ctrl_state old_state; + unsigned long flags; bool changed = false; - spin_lock_irq(&ctrl->lock); + spin_lock_irqsave(&ctrl->lock, flags); old_state = ctrl->state; switch (new_state) { @@ -225,7 +240,7 @@ bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl, if (changed) ctrl->state = new_state; - spin_unlock_irq(&ctrl->lock); + spin_unlock_irqrestore(&ctrl->lock, flags); return changed; } @@ -307,7 +322,7 @@ static int nvme_toggle_streams(struct nvme_ctrl *ctrl, bool enable) memset(&c, 0, sizeof(c)); c.directive.opcode = nvme_admin_directive_send; - c.directive.nsid = cpu_to_le32(0xffffffff); + c.directive.nsid = cpu_to_le32(NVME_NSID_ALL); c.directive.doper = NVME_DIR_SND_ID_OP_ENABLE; c.directive.dtype = NVME_DIR_IDENTIFY; c.directive.tdtype = NVME_DIR_STREAMS; @@ -357,7 +372,7 @@ static int nvme_configure_directives(struct nvme_ctrl *ctrl) if (ret) return ret; - ret = nvme_get_stream_params(ctrl, &s, 0xffffffff); + ret = nvme_get_stream_params(ctrl, &s, NVME_NSID_ALL); if (ret) return ret; @@ -585,10 +600,44 @@ int nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, } EXPORT_SYMBOL_GPL(nvme_submit_sync_cmd); -int __nvme_submit_user_cmd(struct request_queue *q, struct nvme_command *cmd, - void __user *ubuffer, unsigned bufflen, - void __user *meta_buffer, unsigned meta_len, u32 meta_seed, - u32 *result, unsigned timeout) +static void *nvme_add_user_metadata(struct bio *bio, void __user *ubuf, + unsigned len, u32 seed, bool write) +{ + struct bio_integrity_payload *bip; + int ret = -ENOMEM; + void *buf; + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + goto out; + + ret = -EFAULT; + if (write && copy_from_user(buf, ubuf, len)) + goto out_free_meta; + + bip = bio_integrity_alloc(bio, GFP_KERNEL, 1); + if (IS_ERR(bip)) { + ret = PTR_ERR(bip); + goto out_free_meta; + } + + bip->bip_iter.bi_size = len; + bip->bip_iter.bi_sector = seed; + ret = bio_integrity_add_page(bio, virt_to_page(buf), len, + offset_in_page(buf)); + if (ret == len) + return buf; + ret = -ENOMEM; +out_free_meta: + kfree(buf); +out: + return ERR_PTR(ret); +} + +static int nvme_submit_user_cmd(struct request_queue *q, + struct nvme_command *cmd, void __user *ubuffer, + unsigned bufflen, void __user *meta_buffer, unsigned meta_len, + u32 meta_seed, u32 *result, unsigned timeout) { bool write = nvme_is_write(cmd); struct nvme_ns *ns = q->queuedata; @@ -610,46 +659,17 @@ int __nvme_submit_user_cmd(struct request_queue *q, struct nvme_command *cmd, if (ret) goto out; bio = req->bio; - - if (!disk) - goto submit; bio->bi_disk = disk; - - if (meta_buffer && meta_len) { - struct bio_integrity_payload *bip; - - meta = kmalloc(meta_len, GFP_KERNEL); - if (!meta) { - ret = -ENOMEM; + if (disk && meta_buffer && meta_len) { + meta = nvme_add_user_metadata(bio, meta_buffer, meta_len, + meta_seed, write); + if (IS_ERR(meta)) { + ret = PTR_ERR(meta); goto out_unmap; } - - if (write) { - if (copy_from_user(meta, meta_buffer, - meta_len)) { - ret = -EFAULT; - goto out_free_meta; - } - } - - bip = bio_integrity_alloc(bio, GFP_KERNEL, 1); - if (IS_ERR(bip)) { - ret = PTR_ERR(bip); - goto out_free_meta; - } - - bip->bip_iter.bi_size = meta_len; - bip->bip_iter.bi_sector = meta_seed; - - ret = bio_integrity_add_page(bio, virt_to_page(meta), - meta_len, offset_in_page(meta)); - if (ret != meta_len) { - ret = -ENOMEM; - goto out_free_meta; - } } } - submit: + blk_execute_rq(req->q, disk, req, 0); if (nvme_req(req)->flags & NVME_REQ_CANCELLED) ret = -EINTR; @@ -661,7 +681,6 @@ int __nvme_submit_user_cmd(struct request_queue *q, struct nvme_command *cmd, if (copy_to_user(meta_buffer, meta, meta_len)) ret = -EFAULT; } - out_free_meta: kfree(meta); out_unmap: if (bio) @@ -671,14 +690,6 @@ int __nvme_submit_user_cmd(struct request_queue *q, struct nvme_command *cmd, return ret; } -int nvme_submit_user_cmd(struct request_queue *q, struct nvme_command *cmd, - void __user *ubuffer, unsigned bufflen, u32 *result, - unsigned timeout) -{ - return __nvme_submit_user_cmd(q, cmd, ubuffer, bufflen, NULL, 0, 0, - result, timeout); -} - static void nvme_keep_alive_end_io(struct request *rq, blk_status_t status) { struct nvme_ctrl *ctrl = rq->end_io_data; @@ -768,7 +779,8 @@ static int nvme_identify_ctrl(struct nvme_ctrl *dev, struct nvme_id_ctrl **id) return error; } -static int nvme_identify_ns_descs(struct nvme_ns *ns, unsigned nsid) +static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl, unsigned nsid, + u8 *eui64, u8 *nguid, uuid_t *uuid) { struct nvme_command c = { }; int status; @@ -784,7 +796,7 @@ static int nvme_identify_ns_descs(struct nvme_ns *ns, unsigned nsid) if (!data) return -ENOMEM; - status = nvme_submit_sync_cmd(ns->ctrl->admin_q, &c, data, + status = nvme_submit_sync_cmd(ctrl->admin_q, &c, data, NVME_IDENTIFY_DATA_SIZE); if (status) goto free_data; @@ -798,33 +810,33 @@ static int nvme_identify_ns_descs(struct nvme_ns *ns, unsigned nsid) switch (cur->nidt) { case NVME_NIDT_EUI64: if (cur->nidl != NVME_NIDT_EUI64_LEN) { - dev_warn(ns->ctrl->device, + dev_warn(ctrl->device, "ctrl returned bogus length: %d for NVME_NIDT_EUI64\n", cur->nidl); goto free_data; } len = NVME_NIDT_EUI64_LEN; - memcpy(ns->eui, data + pos + sizeof(*cur), len); + memcpy(eui64, data + pos + sizeof(*cur), len); break; case NVME_NIDT_NGUID: if (cur->nidl != NVME_NIDT_NGUID_LEN) { - dev_warn(ns->ctrl->device, + dev_warn(ctrl->device, "ctrl returned bogus length: %d for NVME_NIDT_NGUID\n", cur->nidl); goto free_data; } len = NVME_NIDT_NGUID_LEN; - memcpy(ns->nguid, data + pos + sizeof(*cur), len); + memcpy(nguid, data + pos + sizeof(*cur), len); break; case NVME_NIDT_UUID: if (cur->nidl != NVME_NIDT_UUID_LEN) { - dev_warn(ns->ctrl->device, + dev_warn(ctrl->device, "ctrl returned bogus length: %d for NVME_NIDT_UUID\n", cur->nidl); goto free_data; } len = NVME_NIDT_UUID_LEN; - uuid_copy(&ns->uuid, data + pos + sizeof(*cur)); + uuid_copy(uuid, data + pos + sizeof(*cur)); break; default: /* Skip unnkown types */ @@ -849,9 +861,10 @@ static int nvme_identify_ns_list(struct nvme_ctrl *dev, unsigned nsid, __le32 *n return nvme_submit_sync_cmd(dev->admin_q, &c, ns_list, 0x1000); } -static int nvme_identify_ns(struct nvme_ctrl *dev, unsigned nsid, - struct nvme_id_ns **id) +static struct nvme_id_ns *nvme_identify_ns(struct nvme_ctrl *ctrl, + unsigned nsid) { + struct nvme_id_ns *id; struct nvme_command c = { }; int error; @@ -860,15 +873,18 @@ static int nvme_identify_ns(struct nvme_ctrl *dev, unsigned nsid, c.identify.nsid = cpu_to_le32(nsid); c.identify.cns = NVME_ID_CNS_NS; - *id = kmalloc(sizeof(struct nvme_id_ns), GFP_KERNEL); - if (!*id) - return -ENOMEM; + id = kmalloc(sizeof(*id), GFP_KERNEL); + if (!id) + return NULL; - error = nvme_submit_sync_cmd(dev->admin_q, &c, *id, - sizeof(struct nvme_id_ns)); - if (error) - kfree(*id); - return error; + error = nvme_submit_sync_cmd(ctrl->admin_q, &c, id, sizeof(*id)); + if (error) { + dev_warn(ctrl->device, "Identify namespace failed\n"); + kfree(id); + return NULL; + } + + return id; } static int nvme_set_features(struct nvme_ctrl *dev, unsigned fid, unsigned dword11, @@ -963,7 +979,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) c.rw.apptag = cpu_to_le16(io.apptag); c.rw.appmask = cpu_to_le16(io.appmask); - return __nvme_submit_user_cmd(ns->queue, &c, + return nvme_submit_user_cmd(ns->queue, &c, (void __user *)(uintptr_t)io.addr, length, metadata, meta_len, io.slba, NULL, 0); } @@ -1001,7 +1017,8 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns, status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c, (void __user *)(uintptr_t)cmd.addr, cmd.data_len, - &cmd.result, timeout); + (void __user *)(uintptr_t)cmd.metadata, cmd.metadata, + 0, &cmd.result, timeout); if (status >= 0) { if (put_user(cmd.result, &ucmd->result)) return -EFAULT; @@ -1159,32 +1176,21 @@ static void nvme_config_discard(struct nvme_ns *ns) blk_queue_max_write_zeroes_sectors(ns->queue, UINT_MAX); } -static int nvme_revalidate_ns(struct nvme_ns *ns, struct nvme_id_ns **id) +static void nvme_report_ns_ids(struct nvme_ctrl *ctrl, unsigned int nsid, + struct nvme_id_ns *id, u8 *eui64, u8 *nguid, uuid_t *uuid) { - if (nvme_identify_ns(ns->ctrl, ns->ns_id, id)) { - dev_warn(ns->ctrl->dev, "%s: Identify failure\n", __func__); - return -ENODEV; - } - - if ((*id)->ncap == 0) { - kfree(*id); - return -ENODEV; - } - - if (ns->ctrl->vs >= NVME_VS(1, 1, 0)) - memcpy(ns->eui, (*id)->eui64, sizeof(ns->eui)); - if (ns->ctrl->vs >= NVME_VS(1, 2, 0)) - memcpy(ns->nguid, (*id)->nguid, sizeof(ns->nguid)); - if (ns->ctrl->vs >= NVME_VS(1, 3, 0)) { + if (ctrl->vs >= NVME_VS(1, 1, 0)) + memcpy(eui64, id->eui64, sizeof(id->eui64)); + if (ctrl->vs >= NVME_VS(1, 2, 0)) + memcpy(nguid, id->nguid, sizeof(id->nguid)); + if (ctrl->vs >= NVME_VS(1, 3, 0)) { /* Don't treat error as fatal we potentially * already have a NGUID or EUI-64 */ - if (nvme_identify_ns_descs(ns, ns->ns_id)) - dev_warn(ns->ctrl->device, + if (nvme_identify_ns_descs(ctrl, nsid, eui64, nguid, uuid)) + dev_warn(ctrl->device, "%s: Identify Descriptors failed\n", __func__); } - - return 0; } static void __nvme_revalidate_disk(struct gendisk *disk, struct nvme_id_ns *id) @@ -1225,22 +1231,38 @@ static void __nvme_revalidate_disk(struct gendisk *disk, struct nvme_id_ns *id) static int nvme_revalidate_disk(struct gendisk *disk) { struct nvme_ns *ns = disk->private_data; - struct nvme_id_ns *id = NULL; - int ret; + struct nvme_ctrl *ctrl = ns->ctrl; + struct nvme_id_ns *id; + u8 eui64[8] = { 0 }, nguid[16] = { 0 }; + uuid_t uuid = uuid_null; + int ret = 0; if (test_bit(NVME_NS_DEAD, &ns->flags)) { set_capacity(disk, 0); return -ENODEV; } - ret = nvme_revalidate_ns(ns, &id); - if (ret) - return ret; + id = nvme_identify_ns(ctrl, ns->ns_id); + if (!id) + return -ENODEV; - __nvme_revalidate_disk(disk, id); - kfree(id); + if (id->ncap == 0) { + ret = -ENODEV; + goto out; + } - return 0; + nvme_report_ns_ids(ctrl, ns->ns_id, id, eui64, nguid, &uuid); + if (!uuid_equal(&ns->uuid, &uuid) || + memcmp(&ns->nguid, &nguid, sizeof(ns->nguid)) || + memcmp(&ns->eui, &eui64, sizeof(ns->eui))) { + dev_err(ctrl->device, + "identifiers changed for nsid %d\n", ns->ns_id); + ret = -ENODEV; + } + +out: + kfree(id); + return ret; } static char nvme_pr_type(enum pr_type type) @@ -1440,7 +1462,7 @@ int nvme_enable_ctrl(struct nvme_ctrl *ctrl, u64 cap) ctrl->ctrl_config = NVME_CC_CSS_NVM; ctrl->ctrl_config |= (page_shift - 12) << NVME_CC_MPS_SHIFT; - ctrl->ctrl_config |= NVME_CC_ARB_RR | NVME_CC_SHN_NONE; + ctrl->ctrl_config |= NVME_CC_AMS_RR | NVME_CC_SHN_NONE; ctrl->ctrl_config |= NVME_CC_IOSQES | NVME_CC_IOCQES; ctrl->ctrl_config |= NVME_CC_ENABLE; @@ -1453,7 +1475,7 @@ EXPORT_SYMBOL_GPL(nvme_enable_ctrl); int nvme_shutdown_ctrl(struct nvme_ctrl *ctrl) { - unsigned long timeout = jiffies + (shutdown_timeout * HZ); + unsigned long timeout = jiffies + (ctrl->shutdown_timeout * HZ); u32 csts; int ret; @@ -1502,6 +1524,23 @@ static void nvme_set_queue_limits(struct nvme_ctrl *ctrl, blk_queue_write_cache(q, vwc, vwc); } +static int nvme_configure_timestamp(struct nvme_ctrl *ctrl) +{ + __le64 ts; + int ret; + + if (!(ctrl->oncs & NVME_CTRL_ONCS_TIMESTAMP)) + return 0; + + ts = cpu_to_le64(ktime_to_ms(ktime_get_real())); + ret = nvme_set_features(ctrl, NVME_FEAT_TIMESTAMP, 0, &ts, sizeof(ts), + NULL); + if (ret) + dev_warn_once(ctrl->device, + "could not set timestamp (%d)\n", ret); + return ret; +} + static int nvme_configure_apst(struct nvme_ctrl *ctrl) { /* @@ -1804,6 +1843,20 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) ctrl->sgls = le32_to_cpu(id->sgls); ctrl->kas = le16_to_cpu(id->kas); + if (id->rtd3e) { + /* us -> s */ + u32 transition_time = le32_to_cpu(id->rtd3e) / 1000000; + + ctrl->shutdown_timeout = clamp_t(unsigned int, transition_time, + shutdown_timeout, 60); + + if (ctrl->shutdown_timeout != shutdown_timeout) + dev_warn(ctrl->device, + "Shutdown timeout set to %u seconds\n", + ctrl->shutdown_timeout); + } else + ctrl->shutdown_timeout = shutdown_timeout; + ctrl->npss = id->npss; ctrl->apsta = id->apsta; prev_apst_enabled = ctrl->apst_enabled; @@ -1844,6 +1897,8 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) ctrl->cntlid = le16_to_cpu(id->cntlid); ctrl->hmpre = le32_to_cpu(id->hmpre); ctrl->hmmin = le32_to_cpu(id->hmmin); + ctrl->hmminds = le32_to_cpu(id->hmminds); + ctrl->hmmaxd = le16_to_cpu(id->hmmaxd); } kfree(id); @@ -1856,6 +1911,10 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) ret = nvme_configure_apst(ctrl); if (ret < 0) return ret; + + ret = nvme_configure_timestamp(ctrl); + if (ret < 0) + return ret; ret = nvme_configure_directives(ctrl); if (ret < 0) @@ -2311,13 +2370,20 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid) sprintf(disk_name, "nvme%dn%d", ctrl->instance, ns->instance); - if (nvme_revalidate_ns(ns, &id)) + id = nvme_identify_ns(ctrl, nsid); + if (!id) goto out_free_queue; - if (nvme_nvm_ns_supported(ns, id) && - nvme_nvm_register(ns, disk_name, node)) { - dev_warn(ctrl->device, "%s: LightNVM init failure\n", __func__); + if (id->ncap == 0) goto out_free_id; + + nvme_report_ns_ids(ctrl, ns->ns_id, id, ns->eui, ns->nguid, &ns->uuid); + + if ((ctrl->quirks & NVME_QUIRK_LIGHTNVM) && id->vs[0] == 0x1) { + if (nvme_nvm_register(ns, disk_name, node)) { + dev_warn(ctrl->device, "LightNVM init failure\n"); + goto out_free_id; + } } disk = alloc_disk_node(0, node); @@ -2534,6 +2600,71 @@ static void nvme_async_event_work(struct work_struct *work) spin_unlock_irq(&ctrl->lock); } +static bool nvme_ctrl_pp_status(struct nvme_ctrl *ctrl) +{ + + u32 csts; + + if (ctrl->ops->reg_read32(ctrl, NVME_REG_CSTS, &csts)) + return false; + + if (csts == ~0) + return false; + + return ((ctrl->ctrl_config & NVME_CC_ENABLE) && (csts & NVME_CSTS_PP)); +} + +static void nvme_get_fw_slot_info(struct nvme_ctrl *ctrl) +{ + struct nvme_command c = { }; + struct nvme_fw_slot_info_log *log; + + log = kmalloc(sizeof(*log), GFP_KERNEL); + if (!log) + return; + + c.common.opcode = nvme_admin_get_log_page; + c.common.nsid = cpu_to_le32(NVME_NSID_ALL); + c.common.cdw10[0] = nvme_get_log_dw10(NVME_LOG_FW_SLOT, sizeof(*log)); + + if (!nvme_submit_sync_cmd(ctrl->admin_q, &c, log, sizeof(*log))) + dev_warn(ctrl->device, + "Get FW SLOT INFO log error\n"); + kfree(log); +} + +static void nvme_fw_act_work(struct work_struct *work) +{ + struct nvme_ctrl *ctrl = container_of(work, + struct nvme_ctrl, fw_act_work); + unsigned long fw_act_timeout; + + if (ctrl->mtfa) + fw_act_timeout = jiffies + + msecs_to_jiffies(ctrl->mtfa * 100); + else + fw_act_timeout = jiffies + + msecs_to_jiffies(admin_timeout * 1000); + + nvme_stop_queues(ctrl); + while (nvme_ctrl_pp_status(ctrl)) { + if (time_after(jiffies, fw_act_timeout)) { + dev_warn(ctrl->device, + "Fw activation timeout, reset controller\n"); + nvme_reset_ctrl(ctrl); + break; + } + msleep(100); + } + + if (ctrl->state != NVME_CTRL_LIVE) + return; + + nvme_start_queues(ctrl); + /* read FW slot informationi to clear the AER*/ + nvme_get_fw_slot_info(ctrl); +} + void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status, union nvme_result *res) { @@ -2560,6 +2691,9 @@ void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status, dev_info(ctrl->device, "rescanning\n"); nvme_queue_scan(ctrl); break; + case NVME_AER_NOTICE_FW_ACT_STARTING: + schedule_work(&ctrl->fw_act_work); + break; default: dev_warn(ctrl->device, "async event result %08x\n", result); } @@ -2607,6 +2741,7 @@ void nvme_stop_ctrl(struct nvme_ctrl *ctrl) nvme_stop_keep_alive(ctrl); flush_work(&ctrl->async_event_work); flush_work(&ctrl->scan_work); + cancel_work_sync(&ctrl->fw_act_work); } EXPORT_SYMBOL_GPL(nvme_stop_ctrl); @@ -2670,6 +2805,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, ctrl->quirks = quirks; INIT_WORK(&ctrl->scan_work, nvme_scan_work); INIT_WORK(&ctrl->async_event_work, nvme_async_event_work); + INIT_WORK(&ctrl->fw_act_work, nvme_fw_act_work); ret = nvme_set_instance(ctrl); if (ret) diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index 5f5cd306f76d..47307752dc65 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -22,7 +22,7 @@ #include "fabrics.h" static LIST_HEAD(nvmf_transports); -static DEFINE_MUTEX(nvmf_transports_mutex); +static DECLARE_RWSEM(nvmf_transports_rwsem); static LIST_HEAD(nvmf_hosts); static DEFINE_MUTEX(nvmf_hosts_mutex); @@ -75,7 +75,7 @@ static struct nvmf_host *nvmf_host_default(void) kref_init(&host->ref); snprintf(host->nqn, NVMF_NQN_SIZE, - "nqn.2014-08.org.nvmexpress:NVMf:uuid:%pUb", &host->id); + "nqn.2014-08.org.nvmexpress:uuid:%pUb", &host->id); mutex_lock(&nvmf_hosts_mutex); list_add_tail(&host->list, &nvmf_hosts); @@ -495,9 +495,9 @@ int nvmf_register_transport(struct nvmf_transport_ops *ops) if (!ops->create_ctrl) return -EINVAL; - mutex_lock(&nvmf_transports_mutex); + down_write(&nvmf_transports_rwsem); list_add_tail(&ops->entry, &nvmf_transports); - mutex_unlock(&nvmf_transports_mutex); + up_write(&nvmf_transports_rwsem); return 0; } @@ -514,9 +514,9 @@ EXPORT_SYMBOL_GPL(nvmf_register_transport); */ void nvmf_unregister_transport(struct nvmf_transport_ops *ops) { - mutex_lock(&nvmf_transports_mutex); + down_write(&nvmf_transports_rwsem); list_del(&ops->entry); - mutex_unlock(&nvmf_transports_mutex); + up_write(&nvmf_transports_rwsem); } EXPORT_SYMBOL_GPL(nvmf_unregister_transport); @@ -525,7 +525,7 @@ static struct nvmf_transport_ops *nvmf_lookup_transport( { struct nvmf_transport_ops *ops; - lockdep_assert_held(&nvmf_transports_mutex); + lockdep_assert_held(&nvmf_transports_rwsem); list_for_each_entry(ops, &nvmf_transports, entry) { if (strcmp(ops->name, opts->transport) == 0) @@ -735,6 +735,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts, goto out; } if (uuid_parse(p, &hostid)) { + pr_err("Invalid hostid %s\n", p); ret = -EINVAL; goto out; } @@ -850,7 +851,7 @@ nvmf_create_ctrl(struct device *dev, const char *buf, size_t count) goto out_free_opts; opts->mask &= ~NVMF_REQUIRED_OPTS; - mutex_lock(&nvmf_transports_mutex); + down_read(&nvmf_transports_rwsem); ops = nvmf_lookup_transport(opts); if (!ops) { pr_info("no handler found for transport %s.\n", @@ -877,16 +878,16 @@ nvmf_create_ctrl(struct device *dev, const char *buf, size_t count) dev_warn(ctrl->device, "controller returned incorrect NQN: \"%s\".\n", ctrl->subnqn); - mutex_unlock(&nvmf_transports_mutex); + up_read(&nvmf_transports_rwsem); ctrl->ops->delete_ctrl(ctrl); return ERR_PTR(-EINVAL); } - mutex_unlock(&nvmf_transports_mutex); + up_read(&nvmf_transports_rwsem); return ctrl; out_unlock: - mutex_unlock(&nvmf_transports_mutex); + up_read(&nvmf_transports_rwsem); out_free_opts: nvmf_free_options(opts); return ERR_PTR(ret); diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 1438be649866..d2e882c0f496 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -220,6 +220,90 @@ static int __nvme_fc_del_ctrl(struct nvme_fc_ctrl *); static void __nvme_fc_delete_hw_queue(struct nvme_fc_ctrl *, struct nvme_fc_queue *, unsigned int); +static void +nvme_fc_free_lport(struct kref *ref) +{ + struct nvme_fc_lport *lport = + container_of(ref, struct nvme_fc_lport, ref); + unsigned long flags; + + WARN_ON(lport->localport.port_state != FC_OBJSTATE_DELETED); + WARN_ON(!list_empty(&lport->endp_list)); + + /* remove from transport list */ + spin_lock_irqsave(&nvme_fc_lock, flags); + list_del(&lport->port_list); + spin_unlock_irqrestore(&nvme_fc_lock, flags); + + /* let the LLDD know we've finished tearing it down */ + lport->ops->localport_delete(&lport->localport); + + ida_simple_remove(&nvme_fc_local_port_cnt, lport->localport.port_num); + ida_destroy(&lport->endp_cnt); + + put_device(lport->dev); + + kfree(lport); +} + +static void +nvme_fc_lport_put(struct nvme_fc_lport *lport) +{ + kref_put(&lport->ref, nvme_fc_free_lport); +} + +static int +nvme_fc_lport_get(struct nvme_fc_lport *lport) +{ + return kref_get_unless_zero(&lport->ref); +} + + +static struct nvme_fc_lport * +nvme_fc_attach_to_unreg_lport(struct nvme_fc_port_info *pinfo) +{ + struct nvme_fc_lport *lport; + unsigned long flags; + + spin_lock_irqsave(&nvme_fc_lock, flags); + + list_for_each_entry(lport, &nvme_fc_lport_list, port_list) { + if (lport->localport.node_name != pinfo->node_name || + lport->localport.port_name != pinfo->port_name) + continue; + + if (lport->localport.port_state != FC_OBJSTATE_DELETED) { + lport = ERR_PTR(-EEXIST); + goto out_done; + } + + if (!nvme_fc_lport_get(lport)) { + /* + * fails if ref cnt already 0. If so, + * act as if lport already deleted + */ + lport = NULL; + goto out_done; + } + + /* resume the lport */ + + lport->localport.port_role = pinfo->port_role; + lport->localport.port_id = pinfo->port_id; + lport->localport.port_state = FC_OBJSTATE_ONLINE; + + spin_unlock_irqrestore(&nvme_fc_lock, flags); + + return lport; + } + + lport = NULL; + +out_done: + spin_unlock_irqrestore(&nvme_fc_lock, flags); + + return lport; +} /** * nvme_fc_register_localport - transport entry point called by an @@ -257,6 +341,28 @@ nvme_fc_register_localport(struct nvme_fc_port_info *pinfo, goto out_reghost_failed; } + /* + * look to see if there is already a localport that had been + * deregistered and in the process of waiting for all the + * references to fully be removed. If the references haven't + * expired, we can simply re-enable the localport. Remoteports + * and controller reconnections should resume naturally. + */ + newrec = nvme_fc_attach_to_unreg_lport(pinfo); + + /* found an lport, but something about its state is bad */ + if (IS_ERR(newrec)) { + ret = PTR_ERR(newrec); + goto out_reghost_failed; + + /* found existing lport, which was resumed */ + } else if (newrec) { + *portptr = &newrec->localport; + return 0; + } + + /* nothing found - allocate a new localport struct */ + newrec = kmalloc((sizeof(*newrec) + template->local_priv_sz), GFP_KERNEL); if (!newrec) { @@ -310,44 +416,6 @@ out_reghost_failed: } EXPORT_SYMBOL_GPL(nvme_fc_register_localport); -static void -nvme_fc_free_lport(struct kref *ref) -{ - struct nvme_fc_lport *lport = - container_of(ref, struct nvme_fc_lport, ref); - unsigned long flags; - - WARN_ON(lport->localport.port_state != FC_OBJSTATE_DELETED); - WARN_ON(!list_empty(&lport->endp_list)); - - /* remove from transport list */ - spin_lock_irqsave(&nvme_fc_lock, flags); - list_del(&lport->port_list); - spin_unlock_irqrestore(&nvme_fc_lock, flags); - - /* let the LLDD know we've finished tearing it down */ - lport->ops->localport_delete(&lport->localport); - - ida_simple_remove(&nvme_fc_local_port_cnt, lport->localport.port_num); - ida_destroy(&lport->endp_cnt); - - put_device(lport->dev); - - kfree(lport); -} - -static void -nvme_fc_lport_put(struct nvme_fc_lport *lport) -{ - kref_put(&lport->ref, nvme_fc_free_lport); -} - -static int -nvme_fc_lport_get(struct nvme_fc_lport *lport) -{ - return kref_get_unless_zero(&lport->ref); -} - /** * nvme_fc_unregister_localport - transport entry point called by an * LLDD to deregister/remove a previously @@ -2731,6 +2799,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, ret = blk_mq_alloc_tag_set(&ctrl->admin_tag_set); if (ret) goto out_free_queues; + ctrl->ctrl.admin_tagset = &ctrl->admin_tag_set; ctrl->ctrl.admin_q = blk_mq_init_queue(&ctrl->admin_tag_set); if (IS_ERR(ctrl->ctrl.admin_q)) { diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c index c1a28569e843..1f79e3f141e6 100644 --- a/drivers/nvme/host/lightnvm.c +++ b/drivers/nvme/host/lightnvm.c @@ -955,29 +955,3 @@ void nvme_nvm_unregister_sysfs(struct nvme_ns *ns) sysfs_remove_group(&disk_to_dev(ns->disk)->kobj, &nvm_dev_attr_group); } - -/* move to shared place when used in multiple places. */ -#define PCI_VENDOR_ID_CNEX 0x1d1d -#define PCI_DEVICE_ID_CNEX_WL 0x2807 -#define PCI_DEVICE_ID_CNEX_QEMU 0x1f1f - -int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id) -{ - struct nvme_ctrl *ctrl = ns->ctrl; - /* XXX: this is poking into PCI structures from generic code! */ - struct pci_dev *pdev = to_pci_dev(ctrl->dev); - - /* QEMU NVMe simulator - PCI ID + Vendor specific bit */ - if (pdev->vendor == PCI_VENDOR_ID_CNEX && - pdev->device == PCI_DEVICE_ID_CNEX_QEMU && - id->vs[0] == 0x1) - return 1; - - /* CNEX Labs - PCI ID + Vendor specific bit */ - if (pdev->vendor == PCI_VENDOR_ID_CNEX && - pdev->device == PCI_DEVICE_ID_CNEX_WL && - id->vs[0] == 0x1) - return 1; - - return 0; -} diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 8f2a168ddc01..d3f3c4447515 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -75,6 +75,11 @@ enum nvme_quirks { * The deepest sleep state should not be used. */ NVME_QUIRK_NO_DEEPEST_PS = (1 << 5), + + /* + * Supports the LighNVM command set if indicated in vs[1]. + */ + NVME_QUIRK_LIGHTNVM = (1 << 6), }; /* @@ -125,6 +130,7 @@ struct nvme_ctrl { struct kref kref; int instance; struct blk_mq_tag_set *tagset; + struct blk_mq_tag_set *admin_tagset; struct list_head namespaces; struct mutex namespaces_mutex; struct device *device; /* char device */ @@ -142,6 +148,7 @@ struct nvme_ctrl { u16 cntlid; u32 ctrl_config; + u16 mtfa; u32 queue_count; u64 cap; @@ -160,6 +167,7 @@ struct nvme_ctrl { u16 kas; u8 npss; u8 apsta; + unsigned int shutdown_timeout; unsigned int kato; bool subsystem; unsigned long quirks; @@ -167,13 +175,17 @@ struct nvme_ctrl { struct work_struct scan_work; struct work_struct async_event_work; struct delayed_work ka_work; + struct work_struct fw_act_work; /* Power saving configuration */ u64 ps_max_latency_us; bool apst_enabled; + /* PCIe only: */ u32 hmpre; u32 hmmin; + u32 hmminds; + u16 hmmaxd; /* Fabrics only */ u16 sqsize; @@ -207,13 +219,9 @@ struct nvme_ns { bool ext; u8 pi_type; unsigned long flags; - u16 noiob; - #define NVME_NS_REMOVING 0 #define NVME_NS_DEAD 1 - - u64 mode_select_num_blocks; - u32 mode_select_block_len; + u16 noiob; }; struct nvme_ctrl_ops { @@ -314,20 +322,12 @@ int nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, int __nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, union nvme_result *result, void *buffer, unsigned bufflen, unsigned timeout, int qid, int at_head, int flags); -int nvme_submit_user_cmd(struct request_queue *q, struct nvme_command *cmd, - void __user *ubuffer, unsigned bufflen, u32 *result, - unsigned timeout); -int __nvme_submit_user_cmd(struct request_queue *q, struct nvme_command *cmd, - void __user *ubuffer, unsigned bufflen, - void __user *meta_buffer, unsigned meta_len, u32 meta_seed, - u32 *result, unsigned timeout); int nvme_set_queue_count(struct nvme_ctrl *ctrl, int *count); void nvme_start_keep_alive(struct nvme_ctrl *ctrl); void nvme_stop_keep_alive(struct nvme_ctrl *ctrl); int nvme_reset_ctrl(struct nvme_ctrl *ctrl); #ifdef CONFIG_NVM -int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id); int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node); void nvme_nvm_unregister(struct nvme_ns *ns); int nvme_nvm_register_sysfs(struct nvme_ns *ns); @@ -346,10 +346,6 @@ static inline int nvme_nvm_register_sysfs(struct nvme_ns *ns) return 0; } static inline void nvme_nvm_unregister_sysfs(struct nvme_ns *ns) {}; -static inline int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id) -{ - return 0; -} static inline int nvme_nvm_ioctl(struct nvme_ns *ns, unsigned int cmd, unsigned long arg) { diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index ea892e732268..4a2121335f48 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -556,8 +556,10 @@ static blk_status_t nvme_setup_prps(struct nvme_dev *dev, struct request *req) int nprps, i; length -= (page_size - offset); - if (length <= 0) + if (length <= 0) { + iod->first_dma = 0; return BLK_STS_OK; + } dma_len -= (page_size - offset); if (dma_len) { @@ -667,7 +669,7 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req, if (blk_rq_map_integrity_sg(q, req->bio, &iod->meta_sg) != 1) goto out_unmap; - if (rq_data_dir(req)) + if (req_op(req) == REQ_OP_WRITE) nvme_dif_remap(req, nvme_dif_prep); if (!dma_map_sg(dev->dev, &iod->meta_sg, 1, dma_dir)) @@ -695,7 +697,7 @@ static void nvme_unmap_data(struct nvme_dev *dev, struct request *req) if (iod->nents) { dma_unmap_sg(dev->dev, iod->sg, iod->nents, dma_dir); if (blk_integrity_rq(req)) { - if (!rq_data_dir(req)) + if (req_op(req) == REQ_OP_READ) nvme_dif_remap(req, nvme_dif_complete); dma_unmap_sg(dev->dev, &iod->meta_sg, 1, dma_dir); } @@ -1377,6 +1379,7 @@ static int nvme_alloc_admin_tags(struct nvme_dev *dev) if (blk_mq_alloc_tag_set(&dev->admin_tagset)) return -ENOMEM; + dev->ctrl.admin_tagset = &dev->admin_tagset; dev->ctrl.admin_q = blk_mq_init_queue(&dev->admin_tagset); if (IS_ERR(dev->ctrl.admin_q)) { @@ -1609,21 +1612,23 @@ static void nvme_free_host_mem(struct nvme_dev *dev) dev->host_mem_descs = NULL; } -static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred) +static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred, + u32 chunk_size) { struct nvme_host_mem_buf_desc *descs; - u32 chunk_size, max_entries, len; + u32 max_entries, len; dma_addr_t descs_dma; int i = 0; void **bufs; u64 size = 0, tmp; - /* start big and work our way down */ - chunk_size = min(preferred, (u64)PAGE_SIZE << MAX_ORDER); -retry: tmp = (preferred + chunk_size - 1); do_div(tmp, chunk_size); max_entries = tmp; + + if (dev->ctrl.hmmaxd && dev->ctrl.hmmaxd < max_entries) + max_entries = dev->ctrl.hmmaxd; + descs = dma_zalloc_coherent(dev->dev, max_entries * sizeof(*descs), &descs_dma, GFP_KERNEL); if (!descs) @@ -1647,15 +1652,9 @@ retry: i++; } - if (!size || (min && size < min)) { - dev_warn(dev->ctrl.device, - "failed to allocate host memory buffer.\n"); + if (!size) goto out_free_bufs; - } - dev_info(dev->ctrl.device, - "allocated %lld MiB host memory buffer.\n", - size >> ilog2(SZ_1M)); dev->nr_host_mem_descs = i; dev->host_mem_size = size; dev->host_mem_descs = descs; @@ -1676,21 +1675,35 @@ out_free_descs: dma_free_coherent(dev->dev, max_entries * sizeof(*descs), descs, descs_dma); out: - /* try a smaller chunk size if we failed early */ - if (chunk_size >= PAGE_SIZE * 2 && (i == 0 || size < min)) { - chunk_size /= 2; - goto retry; - } dev->host_mem_descs = NULL; return -ENOMEM; } -static void nvme_setup_host_mem(struct nvme_dev *dev) +static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred) +{ + u32 chunk_size; + + /* start big and work our way down */ + for (chunk_size = min_t(u64, preferred, PAGE_SIZE * MAX_ORDER_NR_PAGES); + chunk_size >= max_t(u32, dev->ctrl.hmminds * 4096, PAGE_SIZE * 2); + chunk_size /= 2) { + if (!__nvme_alloc_host_mem(dev, preferred, chunk_size)) { + if (!min || dev->host_mem_size >= min) + return 0; + nvme_free_host_mem(dev); + } + } + + return -ENOMEM; +} + +static int nvme_setup_host_mem(struct nvme_dev *dev) { u64 max = (u64)max_host_mem_size_mb * SZ_1M; u64 preferred = (u64)dev->ctrl.hmpre * 4096; u64 min = (u64)dev->ctrl.hmmin * 4096; u32 enable_bits = NVME_HOST_MEM_ENABLE; + int ret = 0; preferred = min(preferred, max); if (min > max) { @@ -1698,7 +1711,7 @@ static void nvme_setup_host_mem(struct nvme_dev *dev) "min host memory (%lld MiB) above limit (%d MiB).\n", min >> ilog2(SZ_1M), max_host_mem_size_mb); nvme_free_host_mem(dev); - return; + return 0; } /* @@ -1712,12 +1725,21 @@ static void nvme_setup_host_mem(struct nvme_dev *dev) } if (!dev->host_mem_descs) { - if (nvme_alloc_host_mem(dev, min, preferred)) - return; + if (nvme_alloc_host_mem(dev, min, preferred)) { + dev_warn(dev->ctrl.device, + "failed to allocate host memory buffer.\n"); + return 0; /* controller must work without HMB */ + } + + dev_info(dev->ctrl.device, + "allocated %lld MiB host memory buffer.\n", + dev->host_mem_size >> ilog2(SZ_1M)); } - if (nvme_set_host_mem(dev, enable_bits)) + ret = nvme_set_host_mem(dev, enable_bits); + if (ret) nvme_free_host_mem(dev); + return ret; } static int nvme_setup_io_queues(struct nvme_dev *dev) @@ -2161,8 +2183,11 @@ static void nvme_reset_work(struct work_struct *work) "unable to allocate dma for dbbuf\n"); } - if (dev->ctrl.hmpre) - nvme_setup_host_mem(dev); + if (dev->ctrl.hmpre) { + result = nvme_setup_host_mem(dev); + if (result < 0) + goto out; + } result = nvme_setup_io_queues(dev); if (result) @@ -2494,6 +2519,10 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, { PCI_DEVICE(0x144d, 0xa822), /* Samsung PM1725a */ .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, + { PCI_DEVICE(0x1d1d, 0x1f1f), /* LighNVM qemu device */ + .driver_data = NVME_QUIRK_LIGHTNVM, }, + { PCI_DEVICE(0x1d1d, 0x2807), /* CNEX WL */ + .driver_data = NVME_QUIRK_LIGHTNVM, }, { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) }, { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001) }, { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2003) }, diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index bf42d31484d4..58983000964b 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -37,8 +37,6 @@ #define NVME_RDMA_CONNECT_TIMEOUT_MS 3000 /* 3 second */ -#define NVME_RDMA_MAX_SEGMENT_SIZE 0xffffff /* 24-bit SGL field */ - #define NVME_RDMA_MAX_SEGMENTS 256 #define NVME_RDMA_MAX_INLINE_SEGMENTS 1 @@ -152,6 +150,9 @@ static int nvme_rdma_cm_handler(struct rdma_cm_id *cm_id, struct rdma_cm_event *event); static void nvme_rdma_recv_done(struct ib_cq *cq, struct ib_wc *wc); +static const struct blk_mq_ops nvme_rdma_mq_ops; +static const struct blk_mq_ops nvme_rdma_admin_mq_ops; + /* XXX: really should move to a generic header sooner or later.. */ static inline void put_unaligned_le24(u32 val, u8 *p) { @@ -500,7 +501,7 @@ out_put_dev: return ret; } -static int nvme_rdma_init_queue(struct nvme_rdma_ctrl *ctrl, +static int nvme_rdma_alloc_queue(struct nvme_rdma_ctrl *ctrl, int idx, size_t queue_size) { struct nvme_rdma_queue *queue; @@ -558,54 +559,74 @@ out_destroy_cm_id: static void nvme_rdma_stop_queue(struct nvme_rdma_queue *queue) { + if (!test_and_clear_bit(NVME_RDMA_Q_LIVE, &queue->flags)) + return; + rdma_disconnect(queue->cm_id); ib_drain_qp(queue->qp); } static void nvme_rdma_free_queue(struct nvme_rdma_queue *queue) { + if (test_and_set_bit(NVME_RDMA_Q_DELETING, &queue->flags)) + return; + nvme_rdma_destroy_queue_ib(queue); rdma_destroy_id(queue->cm_id); } -static void nvme_rdma_stop_and_free_queue(struct nvme_rdma_queue *queue) +static void nvme_rdma_free_io_queues(struct nvme_rdma_ctrl *ctrl) { - if (test_and_set_bit(NVME_RDMA_Q_DELETING, &queue->flags)) - return; - nvme_rdma_stop_queue(queue); - nvme_rdma_free_queue(queue); + int i; + + for (i = 1; i < ctrl->ctrl.queue_count; i++) + nvme_rdma_free_queue(&ctrl->queues[i]); } -static void nvme_rdma_free_io_queues(struct nvme_rdma_ctrl *ctrl) +static void nvme_rdma_stop_io_queues(struct nvme_rdma_ctrl *ctrl) { int i; for (i = 1; i < ctrl->ctrl.queue_count; i++) - nvme_rdma_stop_and_free_queue(&ctrl->queues[i]); + nvme_rdma_stop_queue(&ctrl->queues[i]); } -static int nvme_rdma_connect_io_queues(struct nvme_rdma_ctrl *ctrl) +static int nvme_rdma_start_queue(struct nvme_rdma_ctrl *ctrl, int idx) +{ + int ret; + + if (idx) + ret = nvmf_connect_io_queue(&ctrl->ctrl, idx); + else + ret = nvmf_connect_admin_queue(&ctrl->ctrl); + + if (!ret) + set_bit(NVME_RDMA_Q_LIVE, &ctrl->queues[idx].flags); + else + dev_info(ctrl->ctrl.device, + "failed to connect queue: %d ret=%d\n", idx, ret); + return ret; +} + +static int nvme_rdma_start_io_queues(struct nvme_rdma_ctrl *ctrl) { int i, ret = 0; for (i = 1; i < ctrl->ctrl.queue_count; i++) { - ret = nvmf_connect_io_queue(&ctrl->ctrl, i); - if (ret) { - dev_info(ctrl->ctrl.device, - "failed to connect i/o queue: %d\n", ret); - goto out_free_queues; - } - set_bit(NVME_RDMA_Q_LIVE, &ctrl->queues[i].flags); + ret = nvme_rdma_start_queue(ctrl, i); + if (ret) + goto out_stop_queues; } return 0; -out_free_queues: - nvme_rdma_free_io_queues(ctrl); +out_stop_queues: + for (i--; i >= 1; i--) + nvme_rdma_stop_queue(&ctrl->queues[i]); return ret; } -static int nvme_rdma_init_io_queues(struct nvme_rdma_ctrl *ctrl) +static int nvme_rdma_alloc_io_queues(struct nvme_rdma_ctrl *ctrl) { struct nvmf_ctrl_options *opts = ctrl->ctrl.opts; struct ib_device *ibdev = ctrl->device->dev; @@ -634,32 +655,230 @@ static int nvme_rdma_init_io_queues(struct nvme_rdma_ctrl *ctrl) "creating %d I/O queues.\n", nr_io_queues); for (i = 1; i < ctrl->ctrl.queue_count; i++) { - ret = nvme_rdma_init_queue(ctrl, i, - ctrl->ctrl.opts->queue_size); - if (ret) { - dev_info(ctrl->ctrl.device, - "failed to initialize i/o queue: %d\n", ret); + ret = nvme_rdma_alloc_queue(ctrl, i, + ctrl->ctrl.sqsize + 1); + if (ret) goto out_free_queues; - } } return 0; out_free_queues: for (i--; i >= 1; i--) - nvme_rdma_stop_and_free_queue(&ctrl->queues[i]); + nvme_rdma_free_queue(&ctrl->queues[i]); return ret; } -static void nvme_rdma_destroy_admin_queue(struct nvme_rdma_ctrl *ctrl) +static void nvme_rdma_free_tagset(struct nvme_ctrl *nctrl, bool admin) +{ + struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl); + struct blk_mq_tag_set *set = admin ? + &ctrl->admin_tag_set : &ctrl->tag_set; + + blk_mq_free_tag_set(set); + nvme_rdma_dev_put(ctrl->device); +} + +static struct blk_mq_tag_set *nvme_rdma_alloc_tagset(struct nvme_ctrl *nctrl, + bool admin) +{ + struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl); + struct blk_mq_tag_set *set; + int ret; + + if (admin) { + set = &ctrl->admin_tag_set; + memset(set, 0, sizeof(*set)); + set->ops = &nvme_rdma_admin_mq_ops; + set->queue_depth = NVME_RDMA_AQ_BLKMQ_DEPTH; + set->reserved_tags = 2; /* connect + keep-alive */ + set->numa_node = NUMA_NO_NODE; + set->cmd_size = sizeof(struct nvme_rdma_request) + + SG_CHUNK_SIZE * sizeof(struct scatterlist); + set->driver_data = ctrl; + set->nr_hw_queues = 1; + set->timeout = ADMIN_TIMEOUT; + } else { + set = &ctrl->tag_set; + memset(set, 0, sizeof(*set)); + set->ops = &nvme_rdma_mq_ops; + set->queue_depth = nctrl->opts->queue_size; + set->reserved_tags = 1; /* fabric connect */ + set->numa_node = NUMA_NO_NODE; + set->flags = BLK_MQ_F_SHOULD_MERGE; + set->cmd_size = sizeof(struct nvme_rdma_request) + + SG_CHUNK_SIZE * sizeof(struct scatterlist); + set->driver_data = ctrl; + set->nr_hw_queues = nctrl->queue_count - 1; + set->timeout = NVME_IO_TIMEOUT; + } + + ret = blk_mq_alloc_tag_set(set); + if (ret) + goto out; + + /* + * We need a reference on the device as long as the tag_set is alive, + * as the MRs in the request structures need a valid ib_device. + */ + ret = nvme_rdma_dev_get(ctrl->device); + if (!ret) { + ret = -EINVAL; + goto out_free_tagset; + } + + return set; + +out_free_tagset: + blk_mq_free_tag_set(set); +out: + return ERR_PTR(ret); +} + +static void nvme_rdma_destroy_admin_queue(struct nvme_rdma_ctrl *ctrl, + bool remove) { nvme_rdma_free_qe(ctrl->queues[0].device->dev, &ctrl->async_event_sqe, sizeof(struct nvme_command), DMA_TO_DEVICE); - nvme_rdma_stop_and_free_queue(&ctrl->queues[0]); - blk_cleanup_queue(ctrl->ctrl.admin_q); - blk_mq_free_tag_set(&ctrl->admin_tag_set); - nvme_rdma_dev_put(ctrl->device); + nvme_rdma_stop_queue(&ctrl->queues[0]); + if (remove) { + blk_cleanup_queue(ctrl->ctrl.admin_q); + nvme_rdma_free_tagset(&ctrl->ctrl, true); + } + nvme_rdma_free_queue(&ctrl->queues[0]); +} + +static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl, + bool new) +{ + int error; + + error = nvme_rdma_alloc_queue(ctrl, 0, NVME_AQ_DEPTH); + if (error) + return error; + + ctrl->device = ctrl->queues[0].device; + + ctrl->max_fr_pages = min_t(u32, NVME_RDMA_MAX_SEGMENTS, + ctrl->device->dev->attrs.max_fast_reg_page_list_len); + + if (new) { + ctrl->ctrl.admin_tagset = nvme_rdma_alloc_tagset(&ctrl->ctrl, true); + if (IS_ERR(ctrl->ctrl.admin_tagset)) + goto out_free_queue; + + ctrl->ctrl.admin_q = blk_mq_init_queue(&ctrl->admin_tag_set); + if (IS_ERR(ctrl->ctrl.admin_q)) { + error = PTR_ERR(ctrl->ctrl.admin_q); + goto out_free_tagset; + } + } else { + error = blk_mq_reinit_tagset(&ctrl->admin_tag_set, + nvme_rdma_reinit_request); + if (error) + goto out_free_queue; + } + + error = nvme_rdma_start_queue(ctrl, 0); + if (error) + goto out_cleanup_queue; + + error = ctrl->ctrl.ops->reg_read64(&ctrl->ctrl, NVME_REG_CAP, + &ctrl->ctrl.cap); + if (error) { + dev_err(ctrl->ctrl.device, + "prop_get NVME_REG_CAP failed\n"); + goto out_cleanup_queue; + } + + ctrl->ctrl.sqsize = + min_t(int, NVME_CAP_MQES(ctrl->ctrl.cap), ctrl->ctrl.sqsize); + + error = nvme_enable_ctrl(&ctrl->ctrl, ctrl->ctrl.cap); + if (error) + goto out_cleanup_queue; + + ctrl->ctrl.max_hw_sectors = + (ctrl->max_fr_pages - 1) << (ilog2(SZ_4K) - 9); + + error = nvme_init_identify(&ctrl->ctrl); + if (error) + goto out_cleanup_queue; + + error = nvme_rdma_alloc_qe(ctrl->queues[0].device->dev, + &ctrl->async_event_sqe, sizeof(struct nvme_command), + DMA_TO_DEVICE); + if (error) + goto out_cleanup_queue; + + return 0; + +out_cleanup_queue: + if (new) + blk_cleanup_queue(ctrl->ctrl.admin_q); +out_free_tagset: + if (new) + nvme_rdma_free_tagset(&ctrl->ctrl, true); +out_free_queue: + nvme_rdma_free_queue(&ctrl->queues[0]); + return error; +} + +static void nvme_rdma_destroy_io_queues(struct nvme_rdma_ctrl *ctrl, + bool remove) +{ + nvme_rdma_stop_io_queues(ctrl); + if (remove) { + blk_cleanup_queue(ctrl->ctrl.connect_q); + nvme_rdma_free_tagset(&ctrl->ctrl, false); + } + nvme_rdma_free_io_queues(ctrl); +} + +static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new) +{ + int ret; + + ret = nvme_rdma_alloc_io_queues(ctrl); + if (ret) + return ret; + + if (new) { + ctrl->ctrl.tagset = nvme_rdma_alloc_tagset(&ctrl->ctrl, false); + if (IS_ERR(ctrl->ctrl.tagset)) + goto out_free_io_queues; + + ctrl->ctrl.connect_q = blk_mq_init_queue(&ctrl->tag_set); + if (IS_ERR(ctrl->ctrl.connect_q)) { + ret = PTR_ERR(ctrl->ctrl.connect_q); + goto out_free_tag_set; + } + } else { + ret = blk_mq_reinit_tagset(&ctrl->tag_set, + nvme_rdma_reinit_request); + if (ret) + goto out_free_io_queues; + + blk_mq_update_nr_hw_queues(&ctrl->tag_set, + ctrl->ctrl.queue_count - 1); + } + + ret = nvme_rdma_start_io_queues(ctrl); + if (ret) + goto out_cleanup_connect_q; + + return 0; + +out_cleanup_connect_q: + if (new) + blk_cleanup_queue(ctrl->ctrl.connect_q); +out_free_tag_set: + if (new) + nvme_rdma_free_tagset(&ctrl->ctrl, false); +out_free_io_queues: + nvme_rdma_free_io_queues(ctrl); + return ret; } static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl) @@ -708,47 +927,18 @@ static void nvme_rdma_reconnect_ctrl_work(struct work_struct *work) ++ctrl->ctrl.nr_reconnects; - if (ctrl->ctrl.queue_count > 1) { - nvme_rdma_free_io_queues(ctrl); - - ret = blk_mq_reinit_tagset(&ctrl->tag_set, - nvme_rdma_reinit_request); - if (ret) - goto requeue; - } - - nvme_rdma_stop_and_free_queue(&ctrl->queues[0]); - - ret = blk_mq_reinit_tagset(&ctrl->admin_tag_set, - nvme_rdma_reinit_request); - if (ret) - goto requeue; - - ret = nvme_rdma_init_queue(ctrl, 0, NVME_AQ_DEPTH); - if (ret) - goto requeue; - - ret = nvmf_connect_admin_queue(&ctrl->ctrl); - if (ret) - goto requeue; - - set_bit(NVME_RDMA_Q_LIVE, &ctrl->queues[0].flags); + if (ctrl->ctrl.queue_count > 1) + nvme_rdma_destroy_io_queues(ctrl, false); - ret = nvme_enable_ctrl(&ctrl->ctrl, ctrl->ctrl.cap); + nvme_rdma_destroy_admin_queue(ctrl, false); + ret = nvme_rdma_configure_admin_queue(ctrl, false); if (ret) goto requeue; if (ctrl->ctrl.queue_count > 1) { - ret = nvme_rdma_init_io_queues(ctrl); - if (ret) - goto requeue; - - ret = nvme_rdma_connect_io_queues(ctrl); + ret = nvme_rdma_configure_io_queues(ctrl, false); if (ret) goto requeue; - - blk_mq_update_nr_hw_queues(&ctrl->tag_set, - ctrl->ctrl.queue_count - 1); } changed = nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_LIVE); @@ -771,16 +961,15 @@ static void nvme_rdma_error_recovery_work(struct work_struct *work) { struct nvme_rdma_ctrl *ctrl = container_of(work, struct nvme_rdma_ctrl, err_work); - int i; nvme_stop_ctrl(&ctrl->ctrl); - for (i = 0; i < ctrl->ctrl.queue_count; i++) - clear_bit(NVME_RDMA_Q_LIVE, &ctrl->queues[i].flags); - - if (ctrl->ctrl.queue_count > 1) + if (ctrl->ctrl.queue_count > 1) { nvme_stop_queues(&ctrl->ctrl); + nvme_rdma_stop_io_queues(ctrl); + } blk_mq_quiesce_queue(ctrl->ctrl.admin_q); + nvme_rdma_stop_queue(&ctrl->queues[0]); /* We must take care of fastfail/requeue all our inflight requests */ if (ctrl->ctrl.queue_count > 1) @@ -865,7 +1054,7 @@ static void nvme_rdma_unmap_data(struct nvme_rdma_queue *queue, if (req->mr->need_inval) { res = nvme_rdma_inv_rkey(queue, req); - if (res < 0) { + if (unlikely(res < 0)) { dev_err(ctrl->ctrl.device, "Queueing INV WR for rkey %#x failed (%d)\n", req->mr->rkey, res); @@ -934,7 +1123,7 @@ static int nvme_rdma_map_sg_fr(struct nvme_rdma_queue *queue, * the block virtual boundary. */ nr = ib_map_mr_sg(req->mr, req->sg_table.sgl, count, NULL, SZ_4K); - if (nr < count) { + if (unlikely(nr < count)) { if (nr < 0) return nr; return -EINVAL; @@ -1070,7 +1259,7 @@ static int nvme_rdma_post_send(struct nvme_rdma_queue *queue, first = ≀ ret = ib_post_send(queue->qp, first, &bad_wr); - if (ret) { + if (unlikely(ret)) { dev_err(queue->ctrl->ctrl.device, "%s failed with error code %d\n", __func__, ret); } @@ -1096,7 +1285,7 @@ static int nvme_rdma_post_recv(struct nvme_rdma_queue *queue, wr.num_sge = 1; ret = ib_post_recv(queue->qp, &wr, &bad_wr); - if (ret) { + if (unlikely(ret)) { dev_err(queue->ctrl->ctrl.device, "%s failed with error code %d\n", __func__, ret); } @@ -1456,7 +1645,7 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx, blk_mq_start_request(rq); err = nvme_rdma_map_data(queue, rq, c); - if (err < 0) { + if (unlikely(err < 0)) { dev_err(queue->ctrl->ctrl.device, "Failed to map data (%d)\n", err); nvme_cleanup_cmd(rq); @@ -1470,7 +1659,7 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx, flush = true; err = nvme_rdma_post_send(queue, sqe, req->sge, req->num_sge, req->mr->need_inval ? &req->reg_wr.wr : NULL, flush); - if (err) { + if (unlikely(err)) { nvme_rdma_unmap_data(queue, rq); goto err; } @@ -1538,98 +1727,7 @@ static const struct blk_mq_ops nvme_rdma_admin_mq_ops = { .timeout = nvme_rdma_timeout, }; -static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl) -{ - int error; - - error = nvme_rdma_init_queue(ctrl, 0, NVME_AQ_DEPTH); - if (error) - return error; - - ctrl->device = ctrl->queues[0].device; - - /* - * We need a reference on the device as long as the tag_set is alive, - * as the MRs in the request structures need a valid ib_device. - */ - error = -EINVAL; - if (!nvme_rdma_dev_get(ctrl->device)) - goto out_free_queue; - - ctrl->max_fr_pages = min_t(u32, NVME_RDMA_MAX_SEGMENTS, - ctrl->device->dev->attrs.max_fast_reg_page_list_len); - - memset(&ctrl->admin_tag_set, 0, sizeof(ctrl->admin_tag_set)); - ctrl->admin_tag_set.ops = &nvme_rdma_admin_mq_ops; - ctrl->admin_tag_set.queue_depth = NVME_RDMA_AQ_BLKMQ_DEPTH; - ctrl->admin_tag_set.reserved_tags = 2; /* connect + keep-alive */ - ctrl->admin_tag_set.numa_node = NUMA_NO_NODE; - ctrl->admin_tag_set.cmd_size = sizeof(struct nvme_rdma_request) + - SG_CHUNK_SIZE * sizeof(struct scatterlist); - ctrl->admin_tag_set.driver_data = ctrl; - ctrl->admin_tag_set.nr_hw_queues = 1; - ctrl->admin_tag_set.timeout = ADMIN_TIMEOUT; - - error = blk_mq_alloc_tag_set(&ctrl->admin_tag_set); - if (error) - goto out_put_dev; - - ctrl->ctrl.admin_q = blk_mq_init_queue(&ctrl->admin_tag_set); - if (IS_ERR(ctrl->ctrl.admin_q)) { - error = PTR_ERR(ctrl->ctrl.admin_q); - goto out_free_tagset; - } - - error = nvmf_connect_admin_queue(&ctrl->ctrl); - if (error) - goto out_cleanup_queue; - - set_bit(NVME_RDMA_Q_LIVE, &ctrl->queues[0].flags); - - error = nvmf_reg_read64(&ctrl->ctrl, NVME_REG_CAP, - &ctrl->ctrl.cap); - if (error) { - dev_err(ctrl->ctrl.device, - "prop_get NVME_REG_CAP failed\n"); - goto out_cleanup_queue; - } - - ctrl->ctrl.sqsize = - min_t(int, NVME_CAP_MQES(ctrl->ctrl.cap), ctrl->ctrl.sqsize); - - error = nvme_enable_ctrl(&ctrl->ctrl, ctrl->ctrl.cap); - if (error) - goto out_cleanup_queue; - - ctrl->ctrl.max_hw_sectors = - (ctrl->max_fr_pages - 1) << (ilog2(SZ_4K) - 9); - - error = nvme_init_identify(&ctrl->ctrl); - if (error) - goto out_cleanup_queue; - - error = nvme_rdma_alloc_qe(ctrl->queues[0].device->dev, - &ctrl->async_event_sqe, sizeof(struct nvme_command), - DMA_TO_DEVICE); - if (error) - goto out_cleanup_queue; - - return 0; - -out_cleanup_queue: - blk_cleanup_queue(ctrl->ctrl.admin_q); -out_free_tagset: - /* disconnect and drain the queue before freeing the tagset */ - nvme_rdma_stop_queue(&ctrl->queues[0]); - blk_mq_free_tag_set(&ctrl->admin_tag_set); -out_put_dev: - nvme_rdma_dev_put(ctrl->device); -out_free_queue: - nvme_rdma_free_queue(&ctrl->queues[0]); - return error; -} - -static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl) +static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown) { cancel_work_sync(&ctrl->err_work); cancel_delayed_work_sync(&ctrl->reconnect_work); @@ -1638,33 +1736,26 @@ static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl) nvme_stop_queues(&ctrl->ctrl); blk_mq_tagset_busy_iter(&ctrl->tag_set, nvme_cancel_request, &ctrl->ctrl); - nvme_rdma_free_io_queues(ctrl); + nvme_rdma_destroy_io_queues(ctrl, shutdown); } - if (test_bit(NVME_RDMA_Q_LIVE, &ctrl->queues[0].flags)) + if (shutdown) nvme_shutdown_ctrl(&ctrl->ctrl); + else + nvme_disable_ctrl(&ctrl->ctrl, ctrl->ctrl.cap); blk_mq_quiesce_queue(ctrl->ctrl.admin_q); blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, nvme_cancel_request, &ctrl->ctrl); blk_mq_unquiesce_queue(ctrl->ctrl.admin_q); - nvme_rdma_destroy_admin_queue(ctrl); + nvme_rdma_destroy_admin_queue(ctrl, shutdown); } -static void __nvme_rdma_remove_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown) +static void nvme_rdma_remove_ctrl(struct nvme_rdma_ctrl *ctrl) { - nvme_stop_ctrl(&ctrl->ctrl); nvme_remove_namespaces(&ctrl->ctrl); - if (shutdown) - nvme_rdma_shutdown_ctrl(ctrl); - + nvme_rdma_shutdown_ctrl(ctrl, true); nvme_uninit_ctrl(&ctrl->ctrl); - if (ctrl->ctrl.tagset) { - blk_cleanup_queue(ctrl->ctrl.connect_q); - blk_mq_free_tag_set(&ctrl->tag_set); - nvme_rdma_dev_put(ctrl->device); - } - nvme_put_ctrl(&ctrl->ctrl); } @@ -1673,7 +1764,8 @@ static void nvme_rdma_del_ctrl_work(struct work_struct *work) struct nvme_rdma_ctrl *ctrl = container_of(work, struct nvme_rdma_ctrl, delete_work); - __nvme_rdma_remove_ctrl(ctrl, true); + nvme_stop_ctrl(&ctrl->ctrl); + nvme_rdma_remove_ctrl(ctrl); } static int __nvme_rdma_del_ctrl(struct nvme_rdma_ctrl *ctrl) @@ -1705,14 +1797,6 @@ static int nvme_rdma_del_ctrl(struct nvme_ctrl *nctrl) return ret; } -static void nvme_rdma_remove_ctrl_work(struct work_struct *work) -{ - struct nvme_rdma_ctrl *ctrl = container_of(work, - struct nvme_rdma_ctrl, delete_work); - - __nvme_rdma_remove_ctrl(ctrl, false); -} - static void nvme_rdma_reset_ctrl_work(struct work_struct *work) { struct nvme_rdma_ctrl *ctrl = @@ -1721,31 +1805,16 @@ static void nvme_rdma_reset_ctrl_work(struct work_struct *work) bool changed; nvme_stop_ctrl(&ctrl->ctrl); - nvme_rdma_shutdown_ctrl(ctrl); + nvme_rdma_shutdown_ctrl(ctrl, false); - ret = nvme_rdma_configure_admin_queue(ctrl); - if (ret) { - /* ctrl is already shutdown, just remove the ctrl */ - INIT_WORK(&ctrl->delete_work, nvme_rdma_remove_ctrl_work); - goto del_dead_ctrl; - } + ret = nvme_rdma_configure_admin_queue(ctrl, false); + if (ret) + goto out_fail; if (ctrl->ctrl.queue_count > 1) { - ret = blk_mq_reinit_tagset(&ctrl->tag_set, - nvme_rdma_reinit_request); - if (ret) - goto del_dead_ctrl; - - ret = nvme_rdma_init_io_queues(ctrl); + ret = nvme_rdma_configure_io_queues(ctrl, false); if (ret) - goto del_dead_ctrl; - - ret = nvme_rdma_connect_io_queues(ctrl); - if (ret) - goto del_dead_ctrl; - - blk_mq_update_nr_hw_queues(&ctrl->tag_set, - ctrl->ctrl.queue_count - 1); + goto out_fail; } changed = nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_LIVE); @@ -1755,10 +1824,9 @@ static void nvme_rdma_reset_ctrl_work(struct work_struct *work) return; -del_dead_ctrl: - /* Deleting this dead controller... */ +out_fail: dev_warn(ctrl->ctrl.device, "Removing after reset failure\n"); - WARN_ON(!queue_work(nvme_wq, &ctrl->delete_work)); + nvme_rdma_remove_ctrl(ctrl); } static const struct nvme_ctrl_ops nvme_rdma_ctrl_ops = { @@ -1774,62 +1842,6 @@ static const struct nvme_ctrl_ops nvme_rdma_ctrl_ops = { .get_address = nvmf_get_address, }; -static int nvme_rdma_create_io_queues(struct nvme_rdma_ctrl *ctrl) -{ - int ret; - - ret = nvme_rdma_init_io_queues(ctrl); - if (ret) - return ret; - - /* - * We need a reference on the device as long as the tag_set is alive, - * as the MRs in the request structures need a valid ib_device. - */ - ret = -EINVAL; - if (!nvme_rdma_dev_get(ctrl->device)) - goto out_free_io_queues; - - memset(&ctrl->tag_set, 0, sizeof(ctrl->tag_set)); - ctrl->tag_set.ops = &nvme_rdma_mq_ops; - ctrl->tag_set.queue_depth = ctrl->ctrl.opts->queue_size; - ctrl->tag_set.reserved_tags = 1; /* fabric connect */ - ctrl->tag_set.numa_node = NUMA_NO_NODE; - ctrl->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; - ctrl->tag_set.cmd_size = sizeof(struct nvme_rdma_request) + - SG_CHUNK_SIZE * sizeof(struct scatterlist); - ctrl->tag_set.driver_data = ctrl; - ctrl->tag_set.nr_hw_queues = ctrl->ctrl.queue_count - 1; - ctrl->tag_set.timeout = NVME_IO_TIMEOUT; - - ret = blk_mq_alloc_tag_set(&ctrl->tag_set); - if (ret) - goto out_put_dev; - ctrl->ctrl.tagset = &ctrl->tag_set; - - ctrl->ctrl.connect_q = blk_mq_init_queue(&ctrl->tag_set); - if (IS_ERR(ctrl->ctrl.connect_q)) { - ret = PTR_ERR(ctrl->ctrl.connect_q); - goto out_free_tag_set; - } - - ret = nvme_rdma_connect_io_queues(ctrl); - if (ret) - goto out_cleanup_connect_q; - - return 0; - -out_cleanup_connect_q: - blk_cleanup_queue(ctrl->ctrl.connect_q); -out_free_tag_set: - blk_mq_free_tag_set(&ctrl->tag_set); -out_put_dev: - nvme_rdma_dev_put(ctrl->device); -out_free_io_queues: - nvme_rdma_free_io_queues(ctrl); - return ret; -} - static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, struct nvmf_ctrl_options *opts) { @@ -1887,7 +1899,7 @@ static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, if (!ctrl->queues) goto out_uninit_ctrl; - ret = nvme_rdma_configure_admin_queue(ctrl); + ret = nvme_rdma_configure_admin_queue(ctrl, true); if (ret) goto out_kfree_queues; @@ -1922,7 +1934,7 @@ static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, } if (opts->nr_io_queues) { - ret = nvme_rdma_create_io_queues(ctrl); + ret = nvme_rdma_configure_io_queues(ctrl, true); if (ret) goto out_remove_admin_queue; } @@ -1944,7 +1956,7 @@ static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, return &ctrl->ctrl; out_remove_admin_queue: - nvme_rdma_destroy_admin_queue(ctrl); + nvme_rdma_destroy_admin_queue(ctrl, true); out_kfree_queues: kfree(ctrl->queues); out_uninit_ctrl: diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c index a53bb6635b83..c4a0bf36e752 100644 --- a/drivers/nvme/target/admin-cmd.c +++ b/drivers/nvme/target/admin-cmd.c @@ -100,7 +100,7 @@ static u16 nvmet_get_smart_log(struct nvmet_req *req, u16 status; WARN_ON(req == NULL || slog == NULL); - if (req->cmd->get_log_page.nsid == cpu_to_le32(0xFFFFFFFF)) + if (req->cmd->get_log_page.nsid == cpu_to_le32(NVME_NSID_ALL)) status = nvmet_get_smart_log_all(req, slog); else status = nvmet_get_smart_log_nsid(req, slog); @@ -168,15 +168,6 @@ out: nvmet_req_complete(req, status); } -static void copy_and_pad(char *dst, int dst_len, const char *src, int src_len) -{ - int len = min(src_len, dst_len); - - memcpy(dst, src, len); - if (dst_len > len) - memset(dst + len, ' ', dst_len - len); -} - static void nvmet_execute_identify_ctrl(struct nvmet_req *req) { struct nvmet_ctrl *ctrl = req->sq->ctrl; @@ -196,8 +187,9 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req) bin2hex(id->sn, &ctrl->subsys->serial, min(sizeof(ctrl->subsys->serial), sizeof(id->sn) / 2)); - copy_and_pad(id->mn, sizeof(id->mn), model, sizeof(model) - 1); - copy_and_pad(id->fr, sizeof(id->fr), UTS_RELEASE, strlen(UTS_RELEASE)); + memcpy_and_pad(id->mn, sizeof(id->mn), model, sizeof(model) - 1, ' '); + memcpy_and_pad(id->fr, sizeof(id->fr), + UTS_RELEASE, strlen(UTS_RELEASE), ' '); id->rab = 6; @@ -451,7 +443,7 @@ static void nvmet_execute_set_features(struct nvmet_req *req) u32 val32; u16 status = 0; - switch (cdw10 & 0xf) { + switch (cdw10 & 0xff) { case NVME_FEAT_NUM_QUEUES: nvmet_set_result(req, (subsys->max_qid - 1) | ((subsys->max_qid - 1) << 16)); @@ -461,6 +453,9 @@ static void nvmet_execute_set_features(struct nvmet_req *req) req->sq->ctrl->kato = DIV_ROUND_UP(val32, 1000); nvmet_set_result(req, req->sq->ctrl->kato); break; + case NVME_FEAT_HOST_ID: + status = NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR; + break; default: status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; break; @@ -475,7 +470,7 @@ static void nvmet_execute_get_features(struct nvmet_req *req) u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10[0]); u16 status = 0; - switch (cdw10 & 0xf) { + switch (cdw10 & 0xff) { /* * These features are mandatory in the spec, but we don't * have a useful way to implement them. We'll eventually @@ -509,6 +504,16 @@ static void nvmet_execute_get_features(struct nvmet_req *req) case NVME_FEAT_KATO: nvmet_set_result(req, req->sq->ctrl->kato * 1000); break; + case NVME_FEAT_HOST_ID: + /* need 128-bit host identifier flag */ + if (!(req->cmd->common.cdw10[1] & cpu_to_le32(1 << 0))) { + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + break; + } + + status = nvmet_copy_to_sgl(req, 0, &req->sq->ctrl->hostid, + sizeof(req->sq->ctrl->hostid)); + break; default: status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; break; diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index 0a0067e771f5..b6aeb1d70951 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -444,7 +444,7 @@ static struct config_group *nvmet_ns_make(struct config_group *group, goto out; ret = -EINVAL; - if (nsid == 0 || nsid == 0xffffffff) + if (nsid == 0 || nsid == NVME_NSID_ALL) goto out; ret = -ENOMEM; diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index f4b02bb4a1a8..7c23eaf8e563 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -538,37 +538,37 @@ EXPORT_SYMBOL_GPL(nvmet_req_uninit); static inline bool nvmet_cc_en(u32 cc) { - return cc & 0x1; + return (cc >> NVME_CC_EN_SHIFT) & 0x1; } static inline u8 nvmet_cc_css(u32 cc) { - return (cc >> 4) & 0x7; + return (cc >> NVME_CC_CSS_SHIFT) & 0x7; } static inline u8 nvmet_cc_mps(u32 cc) { - return (cc >> 7) & 0xf; + return (cc >> NVME_CC_MPS_SHIFT) & 0xf; } static inline u8 nvmet_cc_ams(u32 cc) { - return (cc >> 11) & 0x7; + return (cc >> NVME_CC_AMS_SHIFT) & 0x7; } static inline u8 nvmet_cc_shn(u32 cc) { - return (cc >> 14) & 0x3; + return (cc >> NVME_CC_SHN_SHIFT) & 0x3; } static inline u8 nvmet_cc_iosqes(u32 cc) { - return (cc >> 16) & 0xf; + return (cc >> NVME_CC_IOSQES_SHIFT) & 0xf; } static inline u8 nvmet_cc_iocqes(u32 cc) { - return (cc >> 20) & 0xf; + return (cc >> NVME_CC_IOCQES_SHIFT) & 0xf; } static void nvmet_start_ctrl(struct nvmet_ctrl *ctrl) @@ -749,6 +749,7 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn, hostnqn, subsysnqn); req->rsp->result.u32 = IPO_IATTR_CONNECT_DATA(hostnqn); up_read(&nvmet_config_sem); + status = NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR; goto out_put_subsystem; } up_read(&nvmet_config_sem); diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c index 3cc17269504b..859a66725291 100644 --- a/drivers/nvme/target/fabrics-cmd.c +++ b/drivers/nvme/target/fabrics-cmd.c @@ -154,6 +154,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req) le32_to_cpu(c->kato), &ctrl); if (status) goto out; + uuid_copy(&ctrl->hostid, &d->hostid); status = nvmet_install_queue(ctrl, req); if (status) { diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c index 309c84aa7595..421e43bf1dd7 100644 --- a/drivers/nvme/target/fc.c +++ b/drivers/nvme/target/fc.c @@ -58,7 +58,8 @@ struct nvmet_fc_ls_iod { struct work_struct work; } __aligned(sizeof(unsigned long long)); -#define NVMET_FC_MAX_KB_PER_XFR 256 +#define NVMET_FC_MAX_SEQ_LENGTH (256 * 1024) +#define NVMET_FC_MAX_XFR_SGENTS (NVMET_FC_MAX_SEQ_LENGTH / PAGE_SIZE) enum nvmet_fcp_datadir { NVMET_FCP_NODATA, @@ -74,9 +75,7 @@ struct nvmet_fc_fcp_iod { struct nvme_fc_ersp_iu rspiubuf; dma_addr_t rspdma; struct scatterlist *data_sg; - struct scatterlist *next_sg; int data_sg_cnt; - u32 next_sg_offset; u32 total_length; u32 offset; enum nvmet_fcp_datadir io_dir; @@ -112,6 +111,7 @@ struct nvmet_fc_tgtport { struct ida assoc_cnt; struct nvmet_port *port; struct kref ref; + u32 max_sg_cnt; }; struct nvmet_fc_defer_fcp_req { @@ -994,6 +994,8 @@ nvmet_fc_register_targetport(struct nvmet_fc_port_info *pinfo, INIT_LIST_HEAD(&newrec->assoc_list); kref_init(&newrec->ref); ida_init(&newrec->assoc_cnt); + newrec->max_sg_cnt = min_t(u32, NVMET_FC_MAX_XFR_SGENTS, + template->max_sgl_segments); ret = nvmet_fc_alloc_ls_iodlist(newrec); if (ret) { @@ -1866,51 +1868,23 @@ nvmet_fc_transfer_fcp_data(struct nvmet_fc_tgtport *tgtport, struct nvmet_fc_fcp_iod *fod, u8 op) { struct nvmefc_tgt_fcp_req *fcpreq = fod->fcpreq; - struct scatterlist *sg, *datasg; unsigned long flags; - u32 tlen, sg_off; + u32 tlen; int ret; fcpreq->op = op; fcpreq->offset = fod->offset; fcpreq->timeout = NVME_FC_TGTOP_TIMEOUT_SEC; - tlen = min_t(u32, (NVMET_FC_MAX_KB_PER_XFR * 1024), + + tlen = min_t(u32, tgtport->max_sg_cnt * PAGE_SIZE, (fod->total_length - fod->offset)); - tlen = min_t(u32, tlen, NVME_FC_MAX_SEGMENTS * PAGE_SIZE); - tlen = min_t(u32, tlen, fod->tgtport->ops->max_sgl_segments - * PAGE_SIZE); fcpreq->transfer_length = tlen; fcpreq->transferred_length = 0; fcpreq->fcp_error = 0; fcpreq->rsplen = 0; - fcpreq->sg_cnt = 0; - - datasg = fod->next_sg; - sg_off = fod->next_sg_offset; - - for (sg = fcpreq->sg ; tlen; sg++) { - *sg = *datasg; - if (sg_off) { - sg->offset += sg_off; - sg->length -= sg_off; - sg->dma_address += sg_off; - sg_off = 0; - } - if (tlen < sg->length) { - sg->length = tlen; - fod->next_sg = datasg; - fod->next_sg_offset += tlen; - } else if (tlen == sg->length) { - fod->next_sg_offset = 0; - fod->next_sg = sg_next(datasg); - } else { - fod->next_sg_offset = 0; - datasg = sg_next(datasg); - } - tlen -= sg->length; - fcpreq->sg_cnt++; - } + fcpreq->sg = &fod->data_sg[fod->offset / PAGE_SIZE]; + fcpreq->sg_cnt = DIV_ROUND_UP(tlen, PAGE_SIZE); /* * If the last READDATA request: check if LLDD supports @@ -2225,8 +2199,6 @@ nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport, fod->req.sg = fod->data_sg; fod->req.sg_cnt = fod->data_sg_cnt; fod->offset = 0; - fod->next_sg = fod->data_sg; - fod->next_sg_offset = 0; if (fod->io_dir == NVMET_FCP_WRITE) { /* pull the data over before invoking nvmet layer */ diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c index 1bb9d5b311b1..1cb9847ec261 100644 --- a/drivers/nvme/target/fcloop.c +++ b/drivers/nvme/target/fcloop.c @@ -193,9 +193,6 @@ out_free_options: #define TGTPORT_OPTS (NVMF_OPT_WWNN | NVMF_OPT_WWPN) -#define ALL_OPTS (NVMF_OPT_WWNN | NVMF_OPT_WWPN | NVMF_OPT_ROLES | \ - NVMF_OPT_FCADDR | NVMF_OPT_LPWWNN | NVMF_OPT_LPWWPN) - static DEFINE_SPINLOCK(fcloop_lock); static LIST_HEAD(fcloop_lports); diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c index 717ed7ddb2f6..92628c432926 100644 --- a/drivers/nvme/target/loop.c +++ b/drivers/nvme/target/loop.c @@ -375,6 +375,7 @@ static int nvme_loop_configure_admin_queue(struct nvme_loop_ctrl *ctrl) error = blk_mq_alloc_tag_set(&ctrl->admin_tag_set); if (error) goto out_free_sq; + ctrl->ctrl.admin_tagset = &ctrl->admin_tag_set; ctrl->ctrl.admin_q = blk_mq_init_queue(&ctrl->admin_tag_set); if (IS_ERR(ctrl->ctrl.admin_q)) { diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index e3b244c7e443..7d261ab894f4 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -115,6 +115,7 @@ struct nvmet_ctrl { u32 cc; u32 csts; + uuid_t hostid; u16 cntlid; u32 kato; diff --git a/drivers/of/device.c b/drivers/of/device.c index 17b66e9715d2..64b710265d39 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -9,6 +9,9 @@ #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/slab.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/amba/bus.h> #include <asm/errno.h> #include "of_private.h" @@ -84,31 +87,28 @@ int of_device_add(struct platform_device *ofdev) */ int of_dma_configure(struct device *dev, struct device_node *np) { - u64 dma_addr, paddr, size; + u64 dma_addr, paddr, size = 0; int ret; bool coherent; unsigned long offset; const struct iommu_ops *iommu; u64 mask; - /* - * Set default coherent_dma_mask to 32 bit. Drivers are expected to - * setup the correct supported mask. - */ - if (!dev->coherent_dma_mask) - dev->coherent_dma_mask = DMA_BIT_MASK(32); - - /* - * Set it to coherent_dma_mask by default if the architecture - * code has not set it. - */ - if (!dev->dma_mask) - dev->dma_mask = &dev->coherent_dma_mask; - ret = of_dma_get_range(np, &dma_addr, &paddr, &size); if (ret < 0) { + /* + * For legacy reasons, we have to assume some devices need + * DMA configuration regardless of whether "dma-ranges" is + * correctly specified or not. + */ + if (!dev_is_pci(dev) && +#ifdef CONFIG_ARM_AMBA + dev->bus != &amba_bustype && +#endif + dev->bus != &platform_bus_type) + return ret == -ENODEV ? 0 : ret; + dma_addr = offset = 0; - size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); } else { offset = PFN_DOWN(paddr - dma_addr); @@ -129,6 +129,22 @@ int of_dma_configure(struct device *dev, struct device_node *np) dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); } + /* + * Set default coherent_dma_mask to 32 bit. Drivers are expected to + * setup the correct supported mask. + */ + if (!dev->coherent_dma_mask) + dev->coherent_dma_mask = DMA_BIT_MASK(32); + /* + * Set it to coherent_dma_mask by default if the architecture + * code has not set it. + */ + if (!dev->dma_mask) + dev->dma_mask = &dev->coherent_dma_mask; + + if (!size) + size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); + dev->dma_pfn_offset = offset; /* diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index be635f017756..083276e03c38 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -260,7 +260,7 @@ static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d) return 0; } -static struct dmi_system_id __initdata pcie_portdrv_dmi_table[] = { +static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = { /* * Boxes that should not use MSI for PCIe PME signaling. */ diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index a2afb44fad10..a4d33619a7bb 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1707,7 +1707,7 @@ static int dmi_disable_ioapicreroute(const struct dmi_system_id *d) return 0; } -static struct dmi_system_id boot_interrupt_dmi_table[] = { +static const struct dmi_system_id boot_interrupt_dmi_table[] = { /* * Systems to exclude from boot interrupt reroute quirks */ diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index b8b6ab072cd0..71b944748304 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -550,9 +550,9 @@ static int armada_37xx_irq_set_wake(struct irq_data *d, unsigned int on) spin_lock_irqsave(&info->irq_lock, flags); val = readl(info->base + reg); if (on) - val |= d->mask; + val |= (BIT(d->hwirq % GPIO_PER_REG)); else - val &= ~d->mask; + val &= ~(BIT(d->hwirq % GPIO_PER_REG)); writel(val, info->base + reg); spin_unlock_irqrestore(&info->irq_lock, flags); @@ -571,10 +571,10 @@ static int armada_37xx_irq_set_type(struct irq_data *d, unsigned int type) val = readl(info->base + reg); switch (type) { case IRQ_TYPE_EDGE_RISING: - val &= ~d->mask; + val &= ~(BIT(d->hwirq % GPIO_PER_REG)); break; case IRQ_TYPE_EDGE_FALLING: - val |= d->mask; + val |= (BIT(d->hwirq % GPIO_PER_REG)); break; default: spin_unlock_irqrestore(&info->irq_lock, flags); @@ -624,11 +624,27 @@ static void armada_37xx_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } +static unsigned int armada_37xx_irq_startup(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + int irq = d->hwirq - chip->irq_base; + /* + * The mask field is a "precomputed bitmask for accessing the + * chip registers" which was introduced for the generic + * irqchip framework. As we don't use this framework, we can + * reuse this field for our own usage. + */ + d->mask = BIT(irq % GPIO_PER_REG); + + armada_37xx_irq_unmask(d); + + return 0; +} + static int armada_37xx_irqchip_register(struct platform_device *pdev, struct armada_37xx_pinctrl *info) { struct device_node *np = info->dev->of_node; - int nrirqs = info->data->nr_pins; struct gpio_chip *gc = &info->gpio_chip; struct irq_chip *irqchip = &info->irq_chip; struct resource res; @@ -666,8 +682,8 @@ static int armada_37xx_irqchip_register(struct platform_device *pdev, irqchip->irq_unmask = armada_37xx_irq_unmask; irqchip->irq_set_wake = armada_37xx_irq_set_wake; irqchip->irq_set_type = armada_37xx_irq_set_type; + irqchip->irq_startup = armada_37xx_irq_startup; irqchip->name = info->data->name; - ret = gpiochip_irqchip_add(gc, irqchip, 0, handle_edge_irq, IRQ_TYPE_NONE); if (ret) { @@ -680,19 +696,6 @@ static int armada_37xx_irqchip_register(struct platform_device *pdev, * controller. But we do not take advantage of this and use * the chained irq with all of them. */ - for (i = 0; i < nrirqs; i++) { - struct irq_data *d = irq_get_irq_data(gc->irq_base + i); - - /* - * The mask field is a "precomputed bitmask for - * accessing the chip registers" which was introduced - * for the generic irqchip framework. As we don't use - * this framework, we can reuse this field for our own - * usage. - */ - d->mask = BIT(i % GPIO_PER_REG); - } - for (i = 0; i < nr_irq_parent; i++) { int irq = irq_of_parse_and_map(np, i); diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 38af1ec2df0c..3f6b34febbf1 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -36,6 +36,7 @@ #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> +#include "core.h" #include "pinctrl-utils.h" #include "pinctrl-amd.h" @@ -725,6 +726,69 @@ static const struct pinconf_ops amd_pinconf_ops = { .pin_config_group_set = amd_pinconf_group_set, }; +#ifdef CONFIG_PM_SLEEP +static bool amd_gpio_should_save(struct amd_gpio *gpio_dev, unsigned int pin) +{ + const struct pin_desc *pd = pin_desc_get(gpio_dev->pctrl, pin); + + if (!pd) + return false; + + /* + * Only restore the pin if it is actually in use by the kernel (or + * by userspace). + */ + if (pd->mux_owner || pd->gpio_owner || + gpiochip_line_is_irq(&gpio_dev->gc, pin)) + return true; + + return false; +} + +int amd_gpio_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct amd_gpio *gpio_dev = platform_get_drvdata(pdev); + struct pinctrl_desc *desc = gpio_dev->pctrl->desc; + int i; + + for (i = 0; i < desc->npins; i++) { + int pin = desc->pins[i].number; + + if (!amd_gpio_should_save(gpio_dev, pin)) + continue; + + gpio_dev->saved_regs[i] = readl(gpio_dev->base + pin*4); + } + + return 0; +} + +int amd_gpio_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct amd_gpio *gpio_dev = platform_get_drvdata(pdev); + struct pinctrl_desc *desc = gpio_dev->pctrl->desc; + int i; + + for (i = 0; i < desc->npins; i++) { + int pin = desc->pins[i].number; + + if (!amd_gpio_should_save(gpio_dev, pin)) + continue; + + writel(gpio_dev->saved_regs[i], gpio_dev->base + pin*4); + } + + return 0; +} + +static const struct dev_pm_ops amd_gpio_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(amd_gpio_suspend, + amd_gpio_resume) +}; +#endif + static struct pinctrl_desc amd_pinctrl_desc = { .pins = kerncz_pins, .npins = ARRAY_SIZE(kerncz_pins), @@ -764,6 +828,14 @@ static int amd_gpio_probe(struct platform_device *pdev) return irq_base; } +#ifdef CONFIG_PM_SLEEP + gpio_dev->saved_regs = devm_kcalloc(&pdev->dev, amd_pinctrl_desc.npins, + sizeof(*gpio_dev->saved_regs), + GFP_KERNEL); + if (!gpio_dev->saved_regs) + return -ENOMEM; +#endif + gpio_dev->pdev = pdev; gpio_dev->gc.direction_input = amd_gpio_direction_input; gpio_dev->gc.direction_output = amd_gpio_direction_output; @@ -853,6 +925,9 @@ static struct platform_driver amd_gpio_driver = { .driver = { .name = "amd_gpio", .acpi_match_table = ACPI_PTR(amd_gpio_acpi_match), +#ifdef CONFIG_PM_SLEEP + .pm = &amd_gpio_pm_ops, +#endif }, .probe = amd_gpio_probe, .remove = amd_gpio_remove, diff --git a/drivers/pinctrl/pinctrl-amd.h b/drivers/pinctrl/pinctrl-amd.h index 5b1cb965c767..8fa453a59da5 100644 --- a/drivers/pinctrl/pinctrl-amd.h +++ b/drivers/pinctrl/pinctrl-amd.h @@ -97,6 +97,7 @@ struct amd_gpio { unsigned int hwbank_num; struct resource *res; struct platform_device *pdev; + u32 *saved_regs; }; /* KERNCZ configuration*/ diff --git a/drivers/pinctrl/sprd/Kconfig b/drivers/pinctrl/sprd/Kconfig index 6f4a7f9ac6fd..bc7f3fab22f1 100644 --- a/drivers/pinctrl/sprd/Kconfig +++ b/drivers/pinctrl/sprd/Kconfig @@ -4,6 +4,8 @@ config PINCTRL_SPRD bool "Spreadtrum pinctrl driver" + depends on OF + depends on ARCH_SPRD || COMPILE_TEST select PINMUX select PINCONF select GENERIC_PINCONF @@ -13,5 +15,6 @@ config PINCTRL_SPRD config PINCTRL_SPRD_SC9860 bool "Spreadtrum SC9860 pinctrl driver" + depends on PINCTRL_SPRD help Say Y here to enable Spreadtrum SC9860 pinctrl driver diff --git a/drivers/pinctrl/sprd/pinctrl-sprd.c b/drivers/pinctrl/sprd/pinctrl-sprd.c index 7e7b9ac7e836..63529911445c 100644 --- a/drivers/pinctrl/sprd/pinctrl-sprd.c +++ b/drivers/pinctrl/sprd/pinctrl-sprd.c @@ -353,13 +353,13 @@ static const struct pinctrl_ops sprd_pctrl_ops = { .dt_free_map = pinctrl_utils_free_map, }; -int sprd_pmx_get_function_count(struct pinctrl_dev *pctldev) +static int sprd_pmx_get_function_count(struct pinctrl_dev *pctldev) { return PIN_FUNC_MAX; } -const char *sprd_pmx_get_function_name(struct pinctrl_dev *pctldev, - unsigned int selector) +static const char *sprd_pmx_get_function_name(struct pinctrl_dev *pctldev, + unsigned int selector) { switch (selector) { case PIN_FUNC_1: @@ -375,10 +375,10 @@ const char *sprd_pmx_get_function_name(struct pinctrl_dev *pctldev, } } -int sprd_pmx_get_function_groups(struct pinctrl_dev *pctldev, - unsigned int selector, - const char * const **groups, - unsigned int * const num_groups) +static int sprd_pmx_get_function_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char * const **groups, + unsigned int * const num_groups) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pinctrl_soc_info *info = pctl->info; @@ -400,7 +400,7 @@ static int sprd_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned long reg; unsigned int val = 0; - if (group_selector > info->ngroups) + if (group_selector >= info->ngroups) return -EINVAL; switch (func_selector) { @@ -734,7 +734,7 @@ static int sprd_pinconf_group_get(struct pinctrl_dev *pctldev, struct sprd_pin_group *grp; unsigned int pin_id; - if (selector > info->ngroups) + if (selector >= info->ngroups) return -EINVAL; grp = &info->groups[selector]; @@ -753,7 +753,7 @@ static int sprd_pinconf_group_set(struct pinctrl_dev *pctldev, struct sprd_pin_group *grp; int ret, i; - if (selector > info->ngroups) + if (selector >= info->ngroups) return -EINVAL; grp = &info->groups[selector]; @@ -813,7 +813,7 @@ static void sprd_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, const char *name; int i, ret; - if (selector > info->ngroups) + if (selector >= info->ngroups) return; grp = &info->groups[selector]; @@ -1100,12 +1100,16 @@ int sprd_pinctrl_remove(struct platform_device *pdev) void sprd_pinctrl_shutdown(struct platform_device *pdev) { - struct pinctrl *pinctl = devm_pinctrl_get(&pdev->dev); + struct pinctrl *pinctl; struct pinctrl_state *state; + pinctl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(pinctl)) + return; state = pinctrl_lookup_state(pinctl, "shutdown"); - if (!IS_ERR(state)) - pinctrl_select_state(pinctl, state); + if (IS_ERR(state)) + return; + pinctrl_select_state(pinctl, state); } MODULE_DESCRIPTION("SPREADTRUM Pin Controller Driver"); diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier.h b/drivers/pinctrl/uniphier/pinctrl-uniphier.h index c075ecb8e5db..0a3d2ac27503 100644 --- a/drivers/pinctrl/uniphier/pinctrl-uniphier.h +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier.h @@ -17,7 +17,7 @@ #define __PINCTRL_UNIPHIER_H__ #include <linux/bitops.h> -#include <linux/bug.h> +#include <linux/build_bug.h> #include <linux/kernel.h> #include <linux/types.h> diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index e8a44a9bc916..d8599736a41a 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -518,7 +518,7 @@ static struct chromeos_laptop cr48 = { .callback = chromeos_laptop_dmi_matched, \ .driver_data = (void *)&board_ -static struct dmi_system_id chromeos_laptop_dmi_table[] __initdata = { +static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = { { .ident = "Samsung Series 5 550", .matches = { diff --git a/drivers/platform/chrome/chromeos_pstore.c b/drivers/platform/chrome/chromeos_pstore.c index 308a853ac4f1..b0693fdec8c6 100644 --- a/drivers/platform/chrome/chromeos_pstore.c +++ b/drivers/platform/chrome/chromeos_pstore.c @@ -14,7 +14,7 @@ #include <linux/platform_device.h> #include <linux/pstore_ram.h> -static struct dmi_system_id chromeos_pstore_dmi_table[] __initdata = { +static const struct dmi_system_id chromeos_pstore_dmi_table[] __initconst = { { /* * Today all Chromebooks/boxes ship with Google_* as version and diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 2b6436d1b6a4..1baf720faf69 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -329,7 +329,7 @@ static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids); -static struct dmi_system_id cros_ec_lpc_dmi_table[] __initdata = { +static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = { { /* * Today all Chromebooks/boxes ship with Google_* as version and diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index a8e4a539e704..6bcb750e1865 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -805,7 +805,7 @@ static int dmi_check_cb_extra(const struct dmi_system_id *id) return 1; } -static struct dmi_system_id __initdata compal_dmi_table[] = { +static const struct dmi_system_id compal_dmi_table[] __initconst = { { .ident = "FL90/IFL90", .matches = { diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c index 458e6c948c11..c26baf77938e 100644 --- a/drivers/platform/x86/hdaps.c +++ b/drivers/platform/x86/hdaps.c @@ -514,7 +514,7 @@ static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id) "ThinkPad T42p", so the order of the entries matters. If your ThinkPad is not recognized, please update to latest BIOS. This is especially the case for some R52 ThinkPads. */ -static struct dmi_system_id __initdata hdaps_whitelist[] = { +static const struct dmi_system_id hdaps_whitelist[] __initconst = { HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c index 610ac8391caa..18d55cee5bcd 100644 --- a/drivers/platform/x86/ibm_rtl.c +++ b/drivers/platform/x86/ibm_rtl.c @@ -227,7 +227,7 @@ static void rtl_teardown_sysfs(void) { } -static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = { +static const struct dmi_system_id ibm_rtl_dmi_table[] __initconst = { { \ .matches = { \ DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \ diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c index 6aa33c4a809f..5747f63c8d9f 100644 --- a/drivers/platform/x86/intel_oaktrail.c +++ b/drivers/platform/x86/intel_oaktrail.c @@ -299,7 +299,7 @@ static int dmi_check_cb(const struct dmi_system_id *id) return 0; } -static struct dmi_system_id __initdata oaktrail_dmi_table[] = { +static const struct dmi_system_id oaktrail_dmi_table[] __initconst = { { .ident = "OakTrail platform", .matches = { diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 8f98c211b440..4f3de2a8c4df 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -247,7 +247,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) return 1; }; -static struct dmi_system_id mlxplat_dmi_table[] __initdata = { +static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { { .callback = mlxplat_dmi_default_matched, .matches = { diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 61b9014d2610..d5bfcc602090 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -605,7 +605,7 @@ static int dmi_check_cb(const struct dmi_system_id *dmi) return 1; } -static struct dmi_system_id __initdata msi_dmi_table[] = { +static const struct dmi_system_id msi_dmi_table[] __initconst = { { .ident = "MSI S270", .matches = { diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 0c703feaeb88..d3cb26f6df73 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -1567,7 +1567,7 @@ static int __init samsung_dmi_matched(const struct dmi_system_id *d) return 0; } -static struct dmi_system_id __initdata samsung_dmi_table[] = { +static const struct dmi_system_id samsung_dmi_table[] __initconst = { { .matches = { DMI_MATCH(DMI_SYS_VENDOR, diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c index e6aac725a0af..a2fb7fbc3273 100644 --- a/drivers/platform/x86/samsung-q10.c +++ b/drivers/platform/x86/samsung-q10.c @@ -95,7 +95,7 @@ static int __init dmi_check_callback(const struct dmi_system_id *id) return 1; } -static struct dmi_system_id __initdata samsungq10_dmi_table[] = { +static const struct dmi_system_id samsungq10_dmi_table[] __initconst = { { .ident = "Samsung Q10", .matches = { diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index bfae79534f44..a16cea2be9c3 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -4880,7 +4880,7 @@ static struct acpi_driver sony_pic_driver = { .drv.pm = &sony_pic_pm, }; -static struct dmi_system_id __initdata sonypi_dmi_table[] = { +static const struct dmi_system_id sonypi_dmi_table[] __initconst = { { .ident = "Sony Vaio", .matches = { diff --git a/drivers/platform/x86/toshiba-wmi.c b/drivers/platform/x86/toshiba-wmi.c index 440528676170..03d7620cd6d7 100644 --- a/drivers/platform/x86/toshiba-wmi.c +++ b/drivers/platform/x86/toshiba-wmi.c @@ -64,7 +64,7 @@ static void toshiba_wmi_notify(u32 value, void *context) kfree(response.pointer); } -static struct dmi_system_id toshiba_wmi_dmi_table[] __initdata = { +static const struct dmi_system_id toshiba_wmi_dmi_table[] __initconst = { { .ident = "Toshiba laptop", .matches = { diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c index 0ced908e7aa8..e681140b85d8 100644 --- a/drivers/pnp/pnpbios/core.c +++ b/drivers/pnp/pnpbios/core.c @@ -495,7 +495,7 @@ static int __init exploding_pnp_bios(const struct dmi_system_id *d) return 0; } -static struct dmi_system_id pnpbios_dmi_table[] __initdata = { +static const struct dmi_system_id pnpbios_dmi_table[] __initconst = { { /* PnPBIOS GPF on boot */ .callback = exploding_pnp_bios, .ident = "Higraded P14H", diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 55fce8b75245..31080c254124 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -171,8 +171,8 @@ static u32 at91_shdwc_get_wakeup_input(struct platform_device *pdev, for_each_child_of_node(np, cnp) { if (of_property_read_u32(cnp, "reg", &wk_input)) { - dev_warn(&pdev->dev, "reg property is missing for %s\n", - cnp->full_name); + dev_warn(&pdev->dev, "reg property is missing for %pOF\n", + cnp); continue; } diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 969f5005669c..5ab90c1f3f7c 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -198,6 +198,15 @@ config BATTERY_BQ27XXX_I2C Say Y here to enable support for batteries with BQ27xxx chips connected over an I2C bus. +config BATTERY_BQ27XXX_HDQ + tristate "BQ27xxx HDQ support" + depends on BATTERY_BQ27XXX + depends on W1 + default y + help + Say Y here to enable support for batteries with BQ27xxx chips + connected over an HDQ bus. + config BATTERY_BQ27XXX_DT_UPDATES_NVM bool "BQ27xxx support for update of NVM/flash data memory" depends on BATTERY_BQ27XXX_I2C @@ -313,6 +322,19 @@ config BATTERY_MAX17042 with MAX17042. This driver also supports max17047/50 chips which are improved version of max17042. +config BATTERY_MAX1721X + tristate "MAX17211/MAX17215 standalone gas-gauge" + depends on W1 + select REGMAP_W1 + help + MAX1721x is fuel-gauge systems for lithium-ion (Li+) batteries + in handheld and portable equipment. MAX17211 used with single cell + battery. MAX17215 designed for muticell battery. Both them have + OneWire (W1) host interface. + + Say Y here to enable support for the MAX17211/MAX17215 standalone + battery gas-gauge. + config BATTERY_Z2 tristate "Z2 battery driver" depends on I2C && MACH_ZIPIT2 @@ -365,6 +387,7 @@ config BATTERY_RX51 config CHARGER_CPCAP tristate "CPCAP PMIC Charger Driver" depends on MFD_CPCAP && IIO + depends on OMAP_USB2 || (!OMAP_USB2 && COMPILE_TEST) default MFD_CPCAP help Say Y to enable support for CPCAP PMIC charger driver for Motorola diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index a41f40957847..621a19058fec 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -38,12 +38,14 @@ obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o obj-$(CONFIG_CHARGER_SBS) += sbs-charger.o obj-$(CONFIG_BATTERY_BQ27XXX) += bq27xxx_battery.o obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o +obj-$(CONFIG_BATTERY_BQ27XXX_HDQ) += bq27xxx_battery_hdq.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o obj-$(CONFIG_BATTERY_DA9150) += da9150-fg.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o +obj-$(CONFIG_BATTERY_MAX1721X) += max1721x_battery.o obj-$(CONFIG_BATTERY_Z2) += z2_battery.o obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o obj-$(CONFIG_CHARGER_RT9455) += rt9455_charger.o diff --git a/drivers/power/supply/act8945a_charger.c b/drivers/power/supply/act8945a_charger.c index d1eb2e359532..8e117b31ba79 100644 --- a/drivers/power/supply/act8945a_charger.c +++ b/drivers/power/supply/act8945a_charger.c @@ -596,9 +596,9 @@ static int act8945a_charger_probe(struct platform_device *pdev) return ret; irq = of_irq_get(pdev->dev.of_node, 0); - if (irq == -EPROBE_DEFER) { + if (irq <= 0) { dev_err(&pdev->dev, "failed to find IRQ number\n"); - return -EPROBE_DEFER; + return irq ?: -ENXIO; } ret = devm_request_irq(&pdev->dev, irq, act8945a_status_changed, diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index d5a707e14526..35ff406aca48 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -16,6 +16,9 @@ #include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/power_supply.h> +#include <linux/power/bq24190_charger.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> #include <linux/workqueue.h> #include <linux/gpio.h> #include <linux/i2c.h> @@ -43,6 +46,8 @@ #define BQ24190_REG_POC_CHG_CONFIG_OTG 0x2 #define BQ24190_REG_POC_SYS_MIN_MASK (BIT(3) | BIT(2) | BIT(1)) #define BQ24190_REG_POC_SYS_MIN_SHIFT 1 +#define BQ24190_REG_POC_SYS_MIN_MIN 3000 +#define BQ24190_REG_POC_SYS_MIN_MAX 3700 #define BQ24190_REG_POC_BOOST_LIM_MASK BIT(0) #define BQ24190_REG_POC_BOOST_LIM_SHIFT 0 @@ -57,9 +62,13 @@ #define BQ24190_REG_PCTCC_IPRECHG_MASK (BIT(7) | BIT(6) | BIT(5) | \ BIT(4)) #define BQ24190_REG_PCTCC_IPRECHG_SHIFT 4 +#define BQ24190_REG_PCTCC_IPRECHG_MIN 128 +#define BQ24190_REG_PCTCC_IPRECHG_MAX 2048 #define BQ24190_REG_PCTCC_ITERM_MASK (BIT(3) | BIT(2) | BIT(1) | \ BIT(0)) #define BQ24190_REG_PCTCC_ITERM_SHIFT 0 +#define BQ24190_REG_PCTCC_ITERM_MIN 128 +#define BQ24190_REG_PCTCC_ITERM_MAX 2048 #define BQ24190_REG_CVC 0x04 /* Charge Voltage Control */ #define BQ24190_REG_CVC_VREG_MASK (BIT(7) | BIT(6) | BIT(5) | \ @@ -156,9 +165,13 @@ struct bq24190_dev_info { struct extcon_dev *extcon; struct notifier_block extcon_nb; struct delayed_work extcon_work; + struct delayed_work input_current_limit_work; char model_name[I2C_NAME_SIZE]; bool initialized; bool irq_event; + u16 sys_min; + u16 iprechg; + u16 iterm; struct mutex f_reg_lock; u8 f_reg; u8 ss_reg; @@ -504,15 +517,112 @@ static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {} #endif -/* - * According to the "Host Mode and default Mode" section of the - * manual, a write to any register causes the bq24190 to switch - * from default mode to host mode. It will switch back to default - * mode after a WDT timeout unless the WDT is turned off as well. - * So, by simply turning off the WDT, we accomplish both with the - * same write. - */ -static int bq24190_set_mode_host(struct bq24190_dev_info *bdi) +#ifdef CONFIG_REGULATOR +static int bq24190_set_charge_mode(struct regulator_dev *dev, u8 val) +{ + struct bq24190_dev_info *bdi = rdev_get_drvdata(dev); + int ret; + + ret = pm_runtime_get_sync(bdi->dev); + if (ret < 0) { + dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", ret); + pm_runtime_put_noidle(bdi->dev); + return ret; + } + + ret = bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_CHG_CONFIG_MASK, + BQ24190_REG_POC_CHG_CONFIG_SHIFT, val); + + pm_runtime_mark_last_busy(bdi->dev); + pm_runtime_put_autosuspend(bdi->dev); + + return ret; +} + +static int bq24190_vbus_enable(struct regulator_dev *dev) +{ + return bq24190_set_charge_mode(dev, BQ24190_REG_POC_CHG_CONFIG_OTG); +} + +static int bq24190_vbus_disable(struct regulator_dev *dev) +{ + return bq24190_set_charge_mode(dev, BQ24190_REG_POC_CHG_CONFIG_CHARGE); +} + +static int bq24190_vbus_is_enabled(struct regulator_dev *dev) +{ + struct bq24190_dev_info *bdi = rdev_get_drvdata(dev); + int ret; + u8 val; + + ret = pm_runtime_get_sync(bdi->dev); + if (ret < 0) { + dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", ret); + pm_runtime_put_noidle(bdi->dev); + return ret; + } + + ret = bq24190_read_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_CHG_CONFIG_MASK, + BQ24190_REG_POC_CHG_CONFIG_SHIFT, &val); + + pm_runtime_mark_last_busy(bdi->dev); + pm_runtime_put_autosuspend(bdi->dev); + + return ret ? ret : val == BQ24190_REG_POC_CHG_CONFIG_OTG; +} + +static const struct regulator_ops bq24190_vbus_ops = { + .enable = bq24190_vbus_enable, + .disable = bq24190_vbus_disable, + .is_enabled = bq24190_vbus_is_enabled, +}; + +static const struct regulator_desc bq24190_vbus_desc = { + .name = "usb_otg_vbus", + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &bq24190_vbus_ops, + .fixed_uV = 5000000, + .n_voltages = 1, +}; + +static const struct regulator_init_data bq24190_vbus_init_data = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +static int bq24190_register_vbus_regulator(struct bq24190_dev_info *bdi) +{ + struct bq24190_platform_data *pdata = bdi->dev->platform_data; + struct regulator_config cfg = { }; + struct regulator_dev *reg; + int ret = 0; + + cfg.dev = bdi->dev; + if (pdata && pdata->regulator_init_data) + cfg.init_data = pdata->regulator_init_data; + else + cfg.init_data = &bq24190_vbus_init_data; + cfg.driver_data = bdi; + reg = devm_regulator_register(bdi->dev, &bq24190_vbus_desc, &cfg); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + dev_err(bdi->dev, "Can't register regulator: %d\n", ret); + } + + return ret; +} +#else +static int bq24190_register_vbus_regulator(struct bq24190_dev_info *bdi) +{ + return 0; +} +#endif + +static int bq24190_set_config(struct bq24190_dev_info *bdi) { int ret; u8 v; @@ -523,9 +633,52 @@ static int bq24190_set_mode_host(struct bq24190_dev_info *bdi) bdi->watchdog = ((v & BQ24190_REG_CTTC_WATCHDOG_MASK) >> BQ24190_REG_CTTC_WATCHDOG_SHIFT); + + /* + * According to the "Host Mode and default Mode" section of the + * manual, a write to any register causes the bq24190 to switch + * from default mode to host mode. It will switch back to default + * mode after a WDT timeout unless the WDT is turned off as well. + * So, by simply turning off the WDT, we accomplish both with the + * same write. + */ v &= ~BQ24190_REG_CTTC_WATCHDOG_MASK; - return bq24190_write(bdi, BQ24190_REG_CTTC, v); + ret = bq24190_write(bdi, BQ24190_REG_CTTC, v); + if (ret < 0) + return ret; + + if (bdi->sys_min) { + v = bdi->sys_min / 100 - 30; // manual section 9.5.1.2, table 9 + ret = bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_SYS_MIN_MASK, + BQ24190_REG_POC_SYS_MIN_SHIFT, + v); + if (ret < 0) + return ret; + } + + if (bdi->iprechg) { + v = bdi->iprechg / 128 - 1; // manual section 9.5.1.4, table 11 + ret = bq24190_write_mask(bdi, BQ24190_REG_PCTCC, + BQ24190_REG_PCTCC_IPRECHG_MASK, + BQ24190_REG_PCTCC_IPRECHG_SHIFT, + v); + if (ret < 0) + return ret; + } + + if (bdi->iterm) { + v = bdi->iterm / 128 - 1; // manual section 9.5.1.4, table 11 + ret = bq24190_write_mask(bdi, BQ24190_REG_PCTCC, + BQ24190_REG_PCTCC_ITERM_MASK, + BQ24190_REG_PCTCC_ITERM_SHIFT, + v); + if (ret < 0) + return ret; + } + + return 0; } static int bq24190_register_reset(struct bq24190_dev_info *bdi) @@ -773,6 +926,38 @@ static int bq24190_charger_set_temp_alert_max(struct bq24190_dev_info *bdi, return bq24190_battery_set_temp_alert_max(bdi, val); } +static int bq24190_charger_get_precharge(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 v; + int ret; + + ret = bq24190_read_mask(bdi, BQ24190_REG_PCTCC, + BQ24190_REG_PCTCC_IPRECHG_MASK, + BQ24190_REG_PCTCC_IPRECHG_SHIFT, &v); + if (ret < 0) + return ret; + + val->intval = ++v * 128 * 1000; + return 0; +} + +static int bq24190_charger_get_charge_term(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 v; + int ret; + + ret = bq24190_read_mask(bdi, BQ24190_REG_PCTCC, + BQ24190_REG_PCTCC_ITERM_MASK, + BQ24190_REG_PCTCC_ITERM_SHIFT, &v); + if (ret < 0) + return ret; + + val->intval = ++v * 128 * 1000; + return 0; +} + static int bq24190_charger_get_current(struct bq24190_dev_info *bdi, union power_supply_propval *val) { @@ -865,6 +1050,33 @@ static int bq24190_charger_set_voltage(struct bq24190_dev_info *bdi, ARRAY_SIZE(bq24190_cvc_vreg_values), val->intval); } +static int bq24190_charger_get_iinlimit(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + int iinlimit, ret; + + ret = bq24190_get_field_val(bdi, BQ24190_REG_ISC, + BQ24190_REG_ISC_IINLIM_MASK, + BQ24190_REG_ISC_IINLIM_SHIFT, + bq24190_isc_iinlim_values, + ARRAY_SIZE(bq24190_isc_iinlim_values), &iinlimit); + if (ret < 0) + return ret; + + val->intval = iinlimit; + return 0; +} + +static int bq24190_charger_set_iinlimit(struct bq24190_dev_info *bdi, + const union power_supply_propval *val) +{ + return bq24190_set_field_val(bdi, BQ24190_REG_ISC, + BQ24190_REG_ISC_IINLIM_MASK, + BQ24190_REG_ISC_IINLIM_SHIFT, + bq24190_isc_iinlim_values, + ARRAY_SIZE(bq24190_isc_iinlim_values), val->intval); +} + static int bq24190_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { @@ -893,6 +1105,12 @@ static int bq24190_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ret = bq24190_charger_get_temp_alert_max(bdi, val); break; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + ret = bq24190_charger_get_precharge(bdi, val); + break; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + ret = bq24190_charger_get_charge_term(bdi, val); + break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ret = bq24190_charger_get_current(bdi, val); break; @@ -905,6 +1123,9 @@ static int bq24190_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: ret = bq24190_charger_get_voltage_max(bdi, val); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq24190_charger_get_iinlimit(bdi, val); + break; case POWER_SUPPLY_PROP_SCOPE: val->intval = POWER_SUPPLY_SCOPE_SYSTEM; ret = 0; @@ -956,6 +1177,9 @@ static int bq24190_charger_set_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ret = bq24190_charger_set_voltage(bdi, val); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq24190_charger_set_iinlimit(bdi, val); + break; default: ret = -EINVAL; } @@ -977,6 +1201,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_TYPE: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ret = 1; break; default: @@ -986,16 +1211,45 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, return ret; } +static void bq24190_input_current_limit_work(struct work_struct *work) +{ + struct bq24190_dev_info *bdi = + container_of(work, struct bq24190_dev_info, + input_current_limit_work.work); + + power_supply_set_input_current_limit_from_supplier(bdi->charger); +} + +/* Sync the input-current-limit with our parent supply (if we have one) */ +static void bq24190_charger_external_power_changed(struct power_supply *psy) +{ + struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); + + /* + * The Power-Good detection may take up to 220ms, sometimes + * the external charger detection is quicker, and the bq24190 will + * reset to iinlim based on its own charger detection (which is not + * hooked up when using external charger detection) resulting in a + * too low default 500mA iinlim. Delay setting the input-current-limit + * for 300ms to avoid this. + */ + queue_delayed_work(system_wq, &bdi->input_current_limit_work, + msecs_to_jiffies(300)); +} + static enum power_supply_property bq24190_charger_properties[] = { POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_TEMP_ALERT_MAX, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, @@ -1013,6 +1267,7 @@ static const struct power_supply_desc bq24190_charger_desc = { .get_property = bq24190_charger_get_property, .set_property = bq24190_charger_set_property, .property_is_writeable = bq24190_charger_property_is_writeable, + .external_power_changed = bq24190_charger_external_power_changed, }; /* Battery power supply property routines */ @@ -1460,13 +1715,50 @@ static int bq24190_hw_init(struct bq24190_dev_info *bdi) if (ret < 0) return ret; - ret = bq24190_set_mode_host(bdi); + ret = bq24190_set_config(bdi); if (ret < 0) return ret; return bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg); } +static int bq24190_get_config(struct bq24190_dev_info *bdi) +{ + const char * const s = "ti,system-minimum-microvolt"; + struct power_supply_battery_info info = {}; + int v; + + if (device_property_read_u32(bdi->dev, s, &v) == 0) { + v /= 1000; + if (v >= BQ24190_REG_POC_SYS_MIN_MIN + && v <= BQ24190_REG_POC_SYS_MIN_MAX) + bdi->sys_min = v; + else + dev_warn(bdi->dev, "invalid value for %s: %u\n", s, v); + } + + if (bdi->dev->of_node && + !power_supply_get_battery_info(bdi->charger, &info)) { + v = info.precharge_current_ua / 1000; + if (v >= BQ24190_REG_PCTCC_IPRECHG_MIN + && v <= BQ24190_REG_PCTCC_IPRECHG_MAX) + bdi->iprechg = v; + else + dev_warn(bdi->dev, "invalid value for battery:precharge-current-microamp: %d\n", + v); + + v = info.charge_term_current_ua / 1000; + if (v >= BQ24190_REG_PCTCC_ITERM_MIN + && v <= BQ24190_REG_PCTCC_ITERM_MAX) + bdi->iterm = v; + else + dev_warn(bdi->dev, "invalid value for battery:charge-term-current-microamp: %d\n", + v); + } + + return 0; +} + static int bq24190_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1494,10 +1786,12 @@ static int bq24190_probe(struct i2c_client *client, mutex_init(&bdi->f_reg_lock); bdi->f_reg = 0; bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */ + INIT_DELAYED_WORK(&bdi->input_current_limit_work, + bq24190_input_current_limit_work); i2c_set_clientdata(client, bdi); - if (!client->irq) { + if (client->irq <= 0) { dev_err(dev, "Can't get irq info\n"); return -EINVAL; } @@ -1530,13 +1824,8 @@ static int bq24190_probe(struct i2c_client *client, goto out_pmrt; } - ret = bq24190_hw_init(bdi); - if (ret < 0) { - dev_err(dev, "Hardware init failed\n"); - goto out_pmrt; - } - charger_cfg.drv_data = bdi; + charger_cfg.of_node = dev->of_node; charger_cfg.supplied_to = bq24190_charger_supplied_to; charger_cfg.num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to), bdi->charger = power_supply_register(dev, &bq24190_charger_desc, @@ -1560,8 +1849,20 @@ static int bq24190_probe(struct i2c_client *client, } } + ret = bq24190_get_config(bdi); + if (ret < 0) { + dev_err(dev, "Can't get devicetree config\n"); + goto out_charger; + } + + ret = bq24190_hw_init(bdi); + if (ret < 0) { + dev_err(dev, "Hardware init failed\n"); + goto out_charger; + } + ret = bq24190_sysfs_create_group(bdi); - if (ret) { + if (ret < 0) { dev_err(dev, "Can't create sysfs entries\n"); goto out_charger; } @@ -1577,6 +1878,10 @@ static int bq24190_probe(struct i2c_client *client, goto out_sysfs; } + ret = bq24190_register_vbus_regulator(bdi); + if (ret < 0) + goto out_sysfs; + if (bdi->extcon) { INIT_DELAYED_WORK(&bdi->extcon_work, bq24190_extcon_work); bdi->extcon_nb.notifier_call = bq24190_extcon_event; @@ -1704,7 +2009,7 @@ static __maybe_unused int bq24190_pm_resume(struct device *dev) } bq24190_register_reset(bdi); - bq24190_set_mode_host(bdi); + bq24190_set_config(bdi); bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg); if (error >= 0) { @@ -1736,6 +2041,7 @@ MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); #ifdef CONFIG_OF static const struct of_device_id bq24190_of_match[] = { { .compatible = "ti,bq24190", }, + { .compatible = "ti,bq24192i", }, { }, }; MODULE_DEVICE_TABLE(of, bq24190_of_match); diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index ed44439d0112..51f0961ecf3e 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -58,8 +58,6 @@ #include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" - #define BQ27XXX_MANUFACTURER "Texas Instruments" /* BQ27XXX Flags */ @@ -132,8 +130,8 @@ enum bq27xxx_reg_index { [BQ27XXX_DM_CKSUM] = 0x60 /* Register mappings */ -static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { - [BQ27000] = { +static u8 + bq27000_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, @@ -157,7 +155,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, }, - [BQ27010] = { + bq27010_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, @@ -181,7 +179,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, }, - [BQ2750X] = { + bq2750x_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x28, @@ -201,47 +199,9 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = INVALID_REG_ADDR, BQ27XXX_DM_REG_ROWS, }, - [BQ2751X] = { - [BQ27XXX_REG_CTRL] = 0x00, - [BQ27XXX_REG_TEMP] = 0x06, - [BQ27XXX_REG_INT_TEMP] = 0x28, - [BQ27XXX_REG_VOLT] = 0x08, - [BQ27XXX_REG_AI] = 0x14, - [BQ27XXX_REG_FLAGS] = 0x0a, - [BQ27XXX_REG_TTE] = 0x16, - [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, - [BQ27XXX_REG_TTES] = 0x1a, - [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, - [BQ27XXX_REG_NAC] = 0x0c, - [BQ27XXX_REG_FCC] = 0x12, - [BQ27XXX_REG_CYCT] = 0x1e, - [BQ27XXX_REG_AE] = INVALID_REG_ADDR, - [BQ27XXX_REG_SOC] = 0x20, - [BQ27XXX_REG_DCAP] = 0x2e, - [BQ27XXX_REG_AP] = INVALID_REG_ADDR, - BQ27XXX_DM_REG_ROWS, - }, - [BQ27500] = { - [BQ27XXX_REG_CTRL] = 0x00, - [BQ27XXX_REG_TEMP] = 0x06, - [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, - [BQ27XXX_REG_VOLT] = 0x08, - [BQ27XXX_REG_AI] = 0x14, - [BQ27XXX_REG_FLAGS] = 0x0a, - [BQ27XXX_REG_TTE] = 0x16, - [BQ27XXX_REG_TTF] = 0x18, - [BQ27XXX_REG_TTES] = 0x1c, - [BQ27XXX_REG_TTECP] = 0x26, - [BQ27XXX_REG_NAC] = 0x0c, - [BQ27XXX_REG_FCC] = 0x12, - [BQ27XXX_REG_CYCT] = 0x2a, - [BQ27XXX_REG_AE] = 0x22, - [BQ27XXX_REG_SOC] = 0x2c, - [BQ27XXX_REG_DCAP] = 0x3c, - [BQ27XXX_REG_AP] = 0x24, - BQ27XXX_DM_REG_ROWS, - }, - [BQ27510G1] = { +#define bq2751x_regs bq27510g3_regs +#define bq2752x_regs bq27510g3_regs + bq27500_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, @@ -261,27 +221,9 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27510G2] = { - [BQ27XXX_REG_CTRL] = 0x00, - [BQ27XXX_REG_TEMP] = 0x06, - [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, - [BQ27XXX_REG_VOLT] = 0x08, - [BQ27XXX_REG_AI] = 0x14, - [BQ27XXX_REG_FLAGS] = 0x0a, - [BQ27XXX_REG_TTE] = 0x16, - [BQ27XXX_REG_TTF] = 0x18, - [BQ27XXX_REG_TTES] = 0x1c, - [BQ27XXX_REG_TTECP] = 0x26, - [BQ27XXX_REG_NAC] = 0x0c, - [BQ27XXX_REG_FCC] = 0x12, - [BQ27XXX_REG_CYCT] = 0x2a, - [BQ27XXX_REG_AE] = 0x22, - [BQ27XXX_REG_SOC] = 0x2c, - [BQ27XXX_REG_DCAP] = 0x3c, - [BQ27XXX_REG_AP] = 0x24, - BQ27XXX_DM_REG_ROWS, - }, - [BQ27510G3] = { +#define bq27510g1_regs bq27500_regs +#define bq27510g2_regs bq27500_regs + bq27510g3_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x28, @@ -301,7 +243,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = INVALID_REG_ADDR, BQ27XXX_DM_REG_ROWS, }, - [BQ27520G1] = { + bq27520g1_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, @@ -321,7 +263,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27520G2] = { + bq27520g2_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x36, @@ -341,7 +283,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27520G3] = { + bq27520g3_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x36, @@ -361,7 +303,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27520G4] = { + bq27520g4_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x28, @@ -381,7 +323,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = INVALID_REG_ADDR, BQ27XXX_DM_REG_ROWS, }, - [BQ27530] = { + bq27530_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x32, @@ -401,7 +343,8 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27541] = { +#define bq27531_regs bq27530_regs + bq27541_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x28, @@ -421,7 +364,10 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27545] = { +#define bq27542_regs bq27541_regs +#define bq27546_regs bq27541_regs +#define bq27742_regs bq27541_regs + bq27545_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_INT_TEMP] = 0x28, @@ -441,7 +387,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_AP] = 0x24, BQ27XXX_DM_REG_ROWS, }, - [BQ27421] = { + bq27421_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x02, [BQ27XXX_REG_INT_TEMP] = 0x1e, @@ -460,10 +406,12 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { [BQ27XXX_REG_DCAP] = 0x3c, [BQ27XXX_REG_AP] = 0x18, BQ27XXX_DM_REG_ROWS, - }, -}; + }; +#define bq27425_regs bq27421_regs +#define bq27441_regs bq27421_regs +#define bq27621_regs bq27421_regs -static enum power_supply_property bq27000_battery_props[] = { +static enum power_supply_property bq27000_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -485,7 +433,7 @@ static enum power_supply_property bq27000_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27010_battery_props[] = { +static enum power_supply_property bq27010_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -505,25 +453,11 @@ static enum power_supply_property bq27010_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq2750x_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_MANUFACTURER, -}; +#define bq2750x_props bq27510g3_props +#define bq2751x_props bq27510g3_props +#define bq2752x_props bq27510g3_props -static enum power_supply_property bq2751x_battery_props[] = { +static enum power_supply_property bq27500_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -532,16 +466,21 @@ static enum power_supply_property bq2751x_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; +#define bq27510g1_props bq27500_props +#define bq27510g2_props bq27500_props -static enum power_supply_property bq27500_battery_props[] = { +static enum power_supply_property bq27510g3_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -550,19 +489,16 @@ static enum power_supply_property bq27500_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_ENERGY_NOW, - POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27510g1_battery_props[] = { +static enum power_supply_property bq27520g1_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -576,14 +512,15 @@ static enum power_supply_property bq27510g1_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27510g2_battery_props[] = { +#define bq27520g2_props bq27500_props + +static enum power_supply_property bq27520g3_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -592,7 +529,6 @@ static enum power_supply_property bq27510g2_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, @@ -604,7 +540,7 @@ static enum power_supply_property bq27510g2_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27510g3_battery_props[] = { +static enum power_supply_property bq27520g4_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -616,13 +552,12 @@ static enum power_supply_property bq27510g3_battery_props[] = { POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27520g1_battery_props[] = { +static enum power_supply_property bq27530_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -631,18 +566,17 @@ static enum power_supply_property bq27520g1_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_MANUFACTURER, }; +#define bq27531_props bq27530_props -static enum power_supply_property bq27520g2_battery_props[] = { +static enum power_supply_property bq27541_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -651,19 +585,20 @@ static enum power_supply_property bq27520g2_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; +#define bq27542_props bq27541_props +#define bq27546_props bq27541_props +#define bq27742_props bq27541_props -static enum power_supply_property bq27520g3_battery_props[] = { +static enum power_supply_property bq27545_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -675,15 +610,13 @@ static enum power_supply_property bq27520g3_battery_props[] = { POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_POWER_AVG, - POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_MANUFACTURER, }; -static enum power_supply_property bq27520g4_battery_props[] = { +static enum power_supply_property bq27421_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -691,111 +624,144 @@ static enum power_supply_property bq27520g4_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_MANUFACTURER, }; +#define bq27425_props bq27421_props +#define bq27441_props bq27421_props +#define bq27621_props bq27421_props -static enum power_supply_property bq27530_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_POWER_AVG, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_MANUFACTURER, +struct bq27xxx_dm_reg { + u8 subclass_id; + u8 offset; + u8 bytes; + u16 min, max; }; -static enum power_supply_property bq27541_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_POWER_AVG, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_MANUFACTURER, +enum bq27xxx_dm_reg_id { + BQ27XXX_DM_DESIGN_CAPACITY = 0, + BQ27XXX_DM_DESIGN_ENERGY, + BQ27XXX_DM_TERMINATE_VOLTAGE, }; -static enum power_supply_property bq27545_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_POWER_AVG, - POWER_SUPPLY_PROP_MANUFACTURER, +#define bq27000_dm_regs 0 +#define bq27010_dm_regs 0 +#define bq2750x_dm_regs 0 +#define bq2751x_dm_regs 0 +#define bq2752x_dm_regs 0 + +#if 0 /* not yet tested */ +static struct bq27xxx_dm_reg bq27500_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 10, 2, 0, 65535 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { }, /* missing on chip */ + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 48, 2, 1000, 32767 }, }; +#else +#define bq27500_dm_regs 0 +#endif -static enum power_supply_property bq27421_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_LEVEL, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_MANUFACTURER, +/* todo create data memory definitions from datasheets and test on chips */ +#define bq27510g1_dm_regs 0 +#define bq27510g2_dm_regs 0 +#define bq27510g3_dm_regs 0 +#define bq27520g1_dm_regs 0 +#define bq27520g2_dm_regs 0 +#define bq27520g3_dm_regs 0 +#define bq27520g4_dm_regs 0 +#define bq27530_dm_regs 0 +#define bq27531_dm_regs 0 +#define bq27541_dm_regs 0 +#define bq27542_dm_regs 0 +#define bq27546_dm_regs 0 +#define bq27742_dm_regs 0 + +#if 0 /* not yet tested */ +static struct bq27xxx_dm_reg bq27545_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 23, 2, 0, 32767 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { 48, 25, 2, 0, 32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 67, 2, 2800, 3700 }, }; +#else +#define bq27545_dm_regs 0 +#endif -#define BQ27XXX_PROP(_id, _prop) \ - [_id] = { \ - .props = _prop, \ - .size = ARRAY_SIZE(_prop), \ - } +static struct bq27xxx_dm_reg bq27421_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 10, 2, 0, 8000 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 12, 2, 0, 32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 16, 2, 2500, 3700 }, +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 12, 2, 0, 32767 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 14, 2, 0, 32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 18, 2, 2800, 3700 }, +}; + +#if 0 /* not yet tested */ +#define bq27441_dm_regs bq27421_dm_regs +#else +#define bq27441_dm_regs 0 +#endif + +#if 0 /* not yet tested */ +static struct bq27xxx_dm_reg bq27621_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 3, 2, 0, 8000 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 5, 2, 0, 32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 9, 2, 2500, 3700 }, +}; +#else +#define bq27621_dm_regs 0 +#endif + +#define BQ27XXX_O_ZERO 0x00000001 +#define BQ27XXX_O_OTDC 0x00000002 +#define BQ27XXX_O_UTOT 0x00000004 +#define BQ27XXX_O_CFGUP 0x00000008 +#define BQ27XXX_O_RAM 0x00000010 + +#define BQ27XXX_DATA(ref, key, opt) { \ + .opts = (opt), \ + .unseal_key = key, \ + .regs = ref##_regs, \ + .dm_regs = ref##_dm_regs, \ + .props = ref##_props, \ + .props_size = ARRAY_SIZE(ref##_props) } static struct { + u32 opts; + u32 unseal_key; + u8 *regs; + struct bq27xxx_dm_reg *dm_regs; enum power_supply_property *props; - size_t size; -} bq27xxx_battery_props[] = { - BQ27XXX_PROP(BQ27000, bq27000_battery_props), - BQ27XXX_PROP(BQ27010, bq27010_battery_props), - BQ27XXX_PROP(BQ2750X, bq2750x_battery_props), - BQ27XXX_PROP(BQ2751X, bq2751x_battery_props), - BQ27XXX_PROP(BQ27500, bq27500_battery_props), - BQ27XXX_PROP(BQ27510G1, bq27510g1_battery_props), - BQ27XXX_PROP(BQ27510G2, bq27510g2_battery_props), - BQ27XXX_PROP(BQ27510G3, bq27510g3_battery_props), - BQ27XXX_PROP(BQ27520G1, bq27520g1_battery_props), - BQ27XXX_PROP(BQ27520G2, bq27520g2_battery_props), - BQ27XXX_PROP(BQ27520G3, bq27520g3_battery_props), - BQ27XXX_PROP(BQ27520G4, bq27520g4_battery_props), - BQ27XXX_PROP(BQ27530, bq27530_battery_props), - BQ27XXX_PROP(BQ27541, bq27541_battery_props), - BQ27XXX_PROP(BQ27545, bq27545_battery_props), - BQ27XXX_PROP(BQ27421, bq27421_battery_props), + size_t props_size; +} bq27xxx_chip_data[] = { + [BQ27000] = BQ27XXX_DATA(bq27000, 0 , BQ27XXX_O_ZERO), + [BQ27010] = BQ27XXX_DATA(bq27010, 0 , BQ27XXX_O_ZERO), + [BQ2750X] = BQ27XXX_DATA(bq2750x, 0 , BQ27XXX_O_OTDC), + [BQ2751X] = BQ27XXX_DATA(bq2751x, 0 , BQ27XXX_O_OTDC), + [BQ2752X] = BQ27XXX_DATA(bq2752x, 0 , BQ27XXX_O_OTDC), + [BQ27500] = BQ27XXX_DATA(bq27500, 0x04143672, BQ27XXX_O_OTDC), + [BQ27510G1] = BQ27XXX_DATA(bq27510g1, 0 , BQ27XXX_O_OTDC), + [BQ27510G2] = BQ27XXX_DATA(bq27510g2, 0 , BQ27XXX_O_OTDC), + [BQ27510G3] = BQ27XXX_DATA(bq27510g3, 0 , BQ27XXX_O_OTDC), + [BQ27520G1] = BQ27XXX_DATA(bq27520g1, 0 , BQ27XXX_O_OTDC), + [BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0 , BQ27XXX_O_OTDC), + [BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0 , BQ27XXX_O_OTDC), + [BQ27520G4] = BQ27XXX_DATA(bq27520g4, 0 , BQ27XXX_O_OTDC), + [BQ27530] = BQ27XXX_DATA(bq27530, 0 , BQ27XXX_O_UTOT), + [BQ27531] = BQ27XXX_DATA(bq27531, 0 , BQ27XXX_O_UTOT), + [BQ27541] = BQ27XXX_DATA(bq27541, 0 , BQ27XXX_O_OTDC), + [BQ27542] = BQ27XXX_DATA(bq27542, 0 , BQ27XXX_O_OTDC), + [BQ27546] = BQ27XXX_DATA(bq27546, 0 , BQ27XXX_O_OTDC), + [BQ27742] = BQ27XXX_DATA(bq27742, 0 , BQ27XXX_O_OTDC), + [BQ27545] = BQ27XXX_DATA(bq27545, 0x04143672, BQ27XXX_O_OTDC), + [BQ27421] = BQ27XXX_DATA(bq27421, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), + [BQ27425] = BQ27XXX_DATA(bq27425, 0x04143672, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP), + [BQ27441] = BQ27XXX_DATA(bq27441, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), + [BQ27621] = BQ27XXX_DATA(bq27621, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), }; static DEFINE_MUTEX(bq27xxx_list_lock); @@ -805,13 +771,6 @@ static LIST_HEAD(bq27xxx_battery_devices); #define BQ27XXX_DM_SZ 32 -struct bq27xxx_dm_reg { - u8 subclass_id; - u8 offset; - u8 bytes; - u16 min, max; -}; - /** * struct bq27xxx_dm_buf - chip data memory buffer * @class: data memory subclass_id @@ -844,12 +803,6 @@ static inline u16 *bq27xxx_dm_reg_ptr(struct bq27xxx_dm_buf *buf, return NULL; } -enum bq27xxx_dm_reg_id { - BQ27XXX_DM_DESIGN_CAPACITY = 0, - BQ27XXX_DM_DESIGN_ENERGY, - BQ27XXX_DM_TERMINATE_VOLTAGE, -}; - static const char * const bq27xxx_dm_reg_name[] = { [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity", [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy", @@ -1092,9 +1045,9 @@ static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di, } #ifdef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM - if (!di->ram_chip && !bq27xxx_dt_to_nvm) { + if (!(di->opts & BQ27XXX_O_RAM) && !bq27xxx_dt_to_nvm) { #else - if (!di->ram_chip) { + if (!(di->opts & BQ27XXX_O_RAM)) { #endif /* devicetree and NVM differ; defer to NVM */ dev_warn(di->dev, "%s has %u; update to %u disallowed " @@ -1130,7 +1083,7 @@ static int bq27xxx_battery_cfgupdate_priv(struct bq27xxx_device_info *di, bool a return ret; } while (!!(ret & BQ27XXX_FLAG_CFGUP) != active && --try); - if (!try) { + if (!try && di->chip != BQ27425) { // 425 has a bug dev_err(di->dev, "timed out waiting for cfgupdate flag %d\n", active); return -EINVAL; } @@ -1162,7 +1115,7 @@ static inline int bq27xxx_battery_soft_reset(struct bq27xxx_device_info *di) static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di, struct bq27xxx_dm_buf *buf) { - bool cfgup = di->chip == BQ27421; /* assume related chips need cfgupdate */ + bool cfgup = di->opts & BQ27XXX_O_CFGUP; int ret; if (!buf->dirty) @@ -1261,7 +1214,7 @@ static void bq27xxx_battery_set_config(struct bq27xxx_device_info *di, bq27xxx_battery_seal(di); - if (updated && di->chip != BQ27421) { /* not a cfgupdate chip, so reset */ + if (updated && !(di->opts & BQ27XXX_O_CFGUP)) { bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_RESET, false); BQ27XXX_MSLEEP(300); /* reset time is not documented */ } @@ -1328,7 +1281,7 @@ static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di) { int soc; - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) soc = bq27xxx_read(di, BQ27XXX_REG_SOC, true); else soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false); @@ -1354,7 +1307,7 @@ static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg) return charge; } - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) charge *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; else charge *= 1000; @@ -1370,7 +1323,7 @@ static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di) { int flags; - if (di->chip == BQ27000 || di->chip == BQ27010) { + if (di->opts & BQ27XXX_O_ZERO) { flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); if (flags >= 0 && (flags & BQ27000_FLAG_CI)) return -ENODATA; @@ -1396,7 +1349,7 @@ static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di) { int dcap; - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, true); else dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false); @@ -1406,7 +1359,7 @@ static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di) return dcap; } - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) dcap = (dcap << 8) * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; else dcap *= 1000; @@ -1428,7 +1381,7 @@ static int bq27xxx_battery_read_energy(struct bq27xxx_device_info *di) return ae; } - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) ae *= BQ27XXX_POWER_CONSTANT / BQ27XXX_RS; else ae *= 1000; @@ -1450,7 +1403,7 @@ static int bq27xxx_battery_read_temperature(struct bq27xxx_device_info *di) return temp; } - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) temp = 5 * temp / 2; return temp; @@ -1507,7 +1460,7 @@ static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di) return tval; } - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS; else return tval; @@ -1518,26 +1471,12 @@ static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di) */ static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags) { - switch (di->chip) { - case BQ2750X: - case BQ2751X: - case BQ27500: - case BQ27510G1: - case BQ27510G2: - case BQ27510G3: - case BQ27520G1: - case BQ27520G2: - case BQ27520G3: - case BQ27520G4: - case BQ27541: - case BQ27545: + if (di->opts & BQ27XXX_O_OTDC) return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD); - case BQ27530: - case BQ27421: + if (di->opts & BQ27XXX_O_UTOT) return flags & BQ27XXX_FLAG_OT; - default: - return false; - } + + return false; } /* @@ -1545,7 +1484,7 @@ static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags) */ static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags) { - if (di->chip == BQ27530 || di->chip == BQ27421) + if (di->opts & BQ27XXX_O_UTOT) return flags & BQ27XXX_FLAG_UT; return false; @@ -1556,7 +1495,7 @@ static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags) */ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags) { - if (di->chip == BQ27000 || di->chip == BQ27010) + if (di->opts & BQ27XXX_O_ZERO) return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF); else return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF); @@ -1569,7 +1508,7 @@ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags) static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di) { int flags; - bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010; + bool has_singe_flag = di->opts & BQ27XXX_O_ZERO; flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag); if (flags < 0) { @@ -1591,8 +1530,8 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di) void bq27xxx_battery_update(struct bq27xxx_device_info *di) { struct bq27xxx_reg_cache cache = {0, }; - bool has_ci_flag = di->chip == BQ27000 || di->chip == BQ27010; - bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010; + bool has_ci_flag = di->opts & BQ27XXX_O_ZERO; + bool has_singe_flag = di->opts & BQ27XXX_O_ZERO; cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag); if ((cache.flags & 0xff) == 0xff) @@ -1670,7 +1609,7 @@ static int bq27xxx_battery_current(struct bq27xxx_device_info *di, return curr; } - if (di->chip == BQ27000 || di->chip == BQ27010) { + if (di->opts & BQ27XXX_O_ZERO) { flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); if (flags & BQ27000_FLAG_CHGS) { dev_dbg(di->dev, "negative current!\n"); @@ -1691,7 +1630,7 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di, { int status; - if (di->chip == BQ27000 || di->chip == BQ27010) { + if (di->opts & BQ27XXX_O_ZERO) { if (di->cache.flags & BQ27000_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; else if (di->cache.flags & BQ27000_FLAG_CHGS) @@ -1719,7 +1658,7 @@ static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di, { int level; - if (di->chip == BQ27000 || di->chip == BQ27010) { + if (di->opts & BQ27XXX_O_ZERO) { if (di->cache.flags & BQ27000_FLAG_FC) level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; else if (di->cache.flags & BQ27000_FLAG_EDV1) @@ -1884,7 +1823,11 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll); mutex_init(&di->lock); - di->regs = bq27xxx_regs[di->chip]; + + di->regs = bq27xxx_chip_data[di->chip].regs; + di->unseal_key = bq27xxx_chip_data[di->chip].unseal_key; + di->dm_regs = bq27xxx_chip_data[di->chip].dm_regs; + di->opts = bq27xxx_chip_data[di->chip].opts; psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL); if (!psy_desc) @@ -1892,8 +1835,8 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) psy_desc->name = di->name; psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; - psy_desc->properties = bq27xxx_battery_props[di->chip].props; - psy_desc->num_properties = bq27xxx_battery_props[di->chip].size; + psy_desc->properties = bq27xxx_chip_data[di->chip].props; + psy_desc->num_properties = bq27xxx_chip_data[di->chip].props_size; psy_desc->get_property = bq27xxx_battery_get_property; psy_desc->external_power_changed = bq27xxx_external_power_changed; @@ -1903,8 +1846,6 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) return PTR_ERR(di->bat); } - dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION); - bq27xxx_battery_settings(di); bq27xxx_battery_update(di); @@ -1938,110 +1879,6 @@ void bq27xxx_battery_teardown(struct bq27xxx_device_info *di) } EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown); -static int bq27xxx_battery_platform_read(struct bq27xxx_device_info *di, u8 reg, - bool single) -{ - struct device *dev = di->dev; - struct bq27xxx_platform_data *pdata = dev->platform_data; - unsigned int timeout = 3; - int upper, lower; - int temp; - - if (!single) { - /* Make sure the value has not changed in between reading the - * lower and the upper part */ - upper = pdata->read(dev, reg + 1); - do { - temp = upper; - if (upper < 0) - return upper; - - lower = pdata->read(dev, reg); - if (lower < 0) - return lower; - - upper = pdata->read(dev, reg + 1); - } while (temp != upper && --timeout); - - if (timeout == 0) - return -EIO; - - return (upper << 8) | lower; - } - - return pdata->read(dev, reg); -} - -static int bq27xxx_battery_platform_probe(struct platform_device *pdev) -{ - struct bq27xxx_device_info *di; - struct bq27xxx_platform_data *pdata = pdev->dev.platform_data; - - if (!pdata) { - dev_err(&pdev->dev, "no platform_data supplied\n"); - return -EINVAL; - } - - if (!pdata->read) { - dev_err(&pdev->dev, "no hdq read callback supplied\n"); - return -EINVAL; - } - - if (!pdata->chip) { - dev_err(&pdev->dev, "no device supplied\n"); - return -EINVAL; - } - - di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); - if (!di) - return -ENOMEM; - - platform_set_drvdata(pdev, di); - - di->dev = &pdev->dev; - di->chip = pdata->chip; - di->name = pdata->name ?: dev_name(&pdev->dev); - di->bus.read = bq27xxx_battery_platform_read; - - return bq27xxx_battery_setup(di); -} - -static int bq27xxx_battery_platform_remove(struct platform_device *pdev) -{ - struct bq27xxx_device_info *di = platform_get_drvdata(pdev); - - bq27xxx_battery_teardown(di); - - return 0; -} - -static const struct platform_device_id bq27xxx_battery_platform_id_table[] = { - { "bq27000-battery", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(platform, bq27xxx_battery_platform_id_table); - -#ifdef CONFIG_OF -static const struct of_device_id bq27xxx_battery_platform_of_match_table[] = { - { .compatible = "ti,bq27000" }, - {}, -}; -MODULE_DEVICE_TABLE(of, bq27xxx_battery_platform_of_match_table); -#endif - -static struct platform_driver bq27xxx_battery_platform_driver = { - .probe = bq27xxx_battery_platform_probe, - .remove = bq27xxx_battery_platform_remove, - .driver = { - .name = "bq27000-battery", - .of_match_table = of_match_ptr(bq27xxx_battery_platform_of_match_table), - }, - .id_table = bq27xxx_battery_platform_id_table, -}; -module_platform_driver(bq27xxx_battery_platform_driver); - -MODULE_ALIAS("platform:bq27000-battery"); - MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); MODULE_DESCRIPTION("BQ27xxx battery monitor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/bq27xxx_battery_hdq.c b/drivers/power/supply/bq27xxx_battery_hdq.c new file mode 100644 index 000000000000..9aff896c9802 --- /dev/null +++ b/drivers/power/supply/bq27xxx_battery_hdq.c @@ -0,0 +1,135 @@ +/* + * BQ27xxx battery monitor HDQ/1-wire driver + * + * Copyright (C) 2007-2017 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/power/bq27xxx_battery.h> + +#include <linux/w1.h> + +#define W1_FAMILY_BQ27000 0x01 + +#define HDQ_CMD_READ (0 << 7) +#define HDQ_CMD_WRITE (1 << 7) + +static int F_ID; +module_param(F_ID, int, S_IRUSR); +MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ27xxx device"); + +static int w1_bq27000_read(struct w1_slave *sl, unsigned int reg) +{ + u8 val; + + mutex_lock(&sl->master->bus_mutex); + w1_write_8(sl->master, HDQ_CMD_READ | reg); + val = w1_read_8(sl->master); + mutex_unlock(&sl->master->bus_mutex); + + return val; +} + +static int bq27xxx_battery_hdq_read(struct bq27xxx_device_info *di, u8 reg, + bool single) +{ + struct w1_slave *sl = dev_to_w1_slave(di->dev); + unsigned int timeout = 3; + int upper, lower; + int temp; + + if (!single) { + /* + * Make sure the value has not changed in between reading the + * lower and the upper part + */ + upper = w1_bq27000_read(sl, reg + 1); + do { + temp = upper; + if (upper < 0) + return upper; + + lower = w1_bq27000_read(sl, reg); + if (lower < 0) + return lower; + + upper = w1_bq27000_read(sl, reg + 1); + } while (temp != upper && --timeout); + + if (timeout == 0) + return -EIO; + + return (upper << 8) | lower; + } + + return w1_bq27000_read(sl, reg); +} + +static int bq27xxx_battery_hdq_add_slave(struct w1_slave *sl) +{ + struct bq27xxx_device_info *di; + + di = devm_kzalloc(&sl->dev, sizeof(*di), GFP_KERNEL); + if (!di) + return -ENOMEM; + + dev_set_drvdata(&sl->dev, di); + + di->dev = &sl->dev; + di->chip = BQ27000; + di->name = "bq27000-battery"; + di->bus.read = bq27xxx_battery_hdq_read; + + return bq27xxx_battery_setup(di); +} + +static void bq27xxx_battery_hdq_remove_slave(struct w1_slave *sl) +{ + struct bq27xxx_device_info *di = dev_get_drvdata(&sl->dev); + + bq27xxx_battery_teardown(di); +} + +static struct w1_family_ops bq27xxx_battery_hdq_fops = { + .add_slave = bq27xxx_battery_hdq_add_slave, + .remove_slave = bq27xxx_battery_hdq_remove_slave, +}; + +static struct w1_family bq27xxx_battery_hdq_family = { + .fid = W1_FAMILY_BQ27000, + .fops = &bq27xxx_battery_hdq_fops, +}; + +static int __init bq27xxx_battery_hdq_init(void) +{ + if (F_ID) + bq27xxx_battery_hdq_family.fid = F_ID; + + return w1_register_family(&bq27xxx_battery_hdq_family); +} +module_init(bq27xxx_battery_hdq_init); + +static void __exit bq27xxx_battery_hdq_exit(void) +{ + w1_unregister_family(&bq27xxx_battery_hdq_family); +} +module_exit(bq27xxx_battery_hdq_exit); + +MODULE_AUTHOR("Texas Instruments Ltd"); +MODULE_DESCRIPTION("BQ27xxx battery monitor HDQ/1-wire driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_BQ27000)); diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index a5972214f074..0b11ed472f33 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -230,7 +230,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { { "bq27210", BQ27010 }, { "bq27500", BQ2750X }, { "bq27510", BQ2751X }, - { "bq27520", BQ2751X }, + { "bq27520", BQ2752X }, { "bq27500-1", BQ27500 }, { "bq27510g1", BQ27510G1 }, { "bq27510g2", BQ27510G2 }, @@ -240,16 +240,16 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { { "bq27520g3", BQ27520G3 }, { "bq27520g4", BQ27520G4 }, { "bq27530", BQ27530 }, - { "bq27531", BQ27530 }, + { "bq27531", BQ27531 }, { "bq27541", BQ27541 }, - { "bq27542", BQ27541 }, - { "bq27546", BQ27541 }, - { "bq27742", BQ27541 }, + { "bq27542", BQ27542 }, + { "bq27546", BQ27546 }, + { "bq27742", BQ27742 }, { "bq27545", BQ27545 }, { "bq27421", BQ27421 }, - { "bq27425", BQ27421 }, - { "bq27441", BQ27421 }, - { "bq27621", BQ27421 }, + { "bq27425", BQ27425 }, + { "bq27441", BQ27441 }, + { "bq27621", BQ27621 }, {}, }; MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table); diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index adc3761831e1..6502fa7c2106 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1632,8 +1632,7 @@ static int charger_manager_probe(struct platform_device *pdev) return -ENODEV; } - cm = devm_kzalloc(&pdev->dev, - sizeof(struct charger_manager), GFP_KERNEL); + cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL); if (!cm) return -ENOMEM; @@ -1645,12 +1644,14 @@ static int charger_manager_probe(struct platform_device *pdev) /* Initialize alarm timer */ if (alarmtimer_get_rtcdev()) { cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL); + if (!cm_timer) + return -ENOMEM; alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func); } /* - * The following two do not need to be errors. - * Users may intentionally ignore those two features. + * Some of the following do not need to be errors. + * Users may intentionally ignore those features. */ if (desc->fullbatt_uV == 0) { dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n"); diff --git a/drivers/power/supply/ds2780_battery.c b/drivers/power/supply/ds2780_battery.c index 8edd4aa5f475..e5d81b493c45 100644 --- a/drivers/power/supply/ds2780_battery.c +++ b/drivers/power/supply/ds2780_battery.c @@ -663,7 +663,7 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2780_param_eeprom_bin_attr = { +static const struct bin_attribute ds2780_param_eeprom_bin_attr = { .attr = { .name = "param_eeprom", .mode = S_IRUGO | S_IWUSR, @@ -708,7 +708,7 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2780_user_eeprom_bin_attr = { +static const struct bin_attribute ds2780_user_eeprom_bin_attr = { .attr = { .name = "user_eeprom", .mode = S_IRUGO | S_IWUSR, diff --git a/drivers/power/supply/ds2781_battery.c b/drivers/power/supply/ds2781_battery.c index 4400402f9ec5..efe83ef8670c 100644 --- a/drivers/power/supply/ds2781_battery.c +++ b/drivers/power/supply/ds2781_battery.c @@ -665,7 +665,7 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2781_param_eeprom_bin_attr = { +static const struct bin_attribute ds2781_param_eeprom_bin_attr = { .attr = { .name = "param_eeprom", .mode = S_IRUGO | S_IWUSR, @@ -711,7 +711,7 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp, return count; } -static struct bin_attribute ds2781_user_eeprom_bin_attr = { +static const struct bin_attribute ds2781_user_eeprom_bin_attr = { .attr = { .name = "user_eeprom", .mode = S_IRUGO | S_IWUSR, diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index 677f7c40b25a..0f3432795f3c 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -626,7 +626,7 @@ static ssize_t lp8788_show_charger_status(struct device *dev, { struct lp8788_charger *pchg = dev_get_drvdata(dev); enum lp8788_charging_state state; - char *desc[LP8788_MAX_CHG_STATE] = { + static const char * const desc[LP8788_MAX_CHG_STATE] = { [LP8788_OFF] = "CHARGER OFF", [LP8788_WARM_UP] = "WARM UP", [LP8788_LOW_INPUT] = "LOW INPUT STATE", @@ -650,8 +650,10 @@ static ssize_t lp8788_show_eoc_time(struct device *dev, struct device_attribute *attr, char *buf) { struct lp8788_charger *pchg = dev_get_drvdata(dev); - char *stime[] = { "400ms", "5min", "10min", "15min", - "20min", "25min", "30min", "No timeout" }; + static const char * const stime[] = { + "400ms", "5min", "10min", "15min", + "20min", "25min", "30min", "No timeout" + }; u8 val; lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val); @@ -665,9 +667,13 @@ static ssize_t lp8788_show_eoc_level(struct device *dev, struct device_attribute *attr, char *buf) { struct lp8788_charger *pchg = dev_get_drvdata(dev); - char *abs_level[] = { "25mA", "49mA", "75mA", "98mA" }; - char *relative_level[] = { "5%", "10%", "15%", "20%" }; - char *level; + static const char * const abs_level[] = { + "25mA", "49mA", "75mA", "98mA" + }; + static const char * const relative_level[] = { + "5%", "10%", "15%", "20%" + }; + const char *level; u8 val; u8 mode; diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index 7efb908f4451..08e4fd9ee607 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -1,6 +1,6 @@ /* - * I2C client/driver for the Linear Technology LTC2941 and LTC2943 - * Battery Gas Gauge IC + * I2C client/driver for the Linear Technology LTC2941, LTC2942, LTC2943 + * and LTC2944 Battery Gas Gauge IC * * Copyright (C) 2014 Topic Embedded Systems * @@ -34,35 +34,39 @@ enum ltc294x_reg { LTC294X_REG_CONTROL = 0x01, LTC294X_REG_ACC_CHARGE_MSB = 0x02, LTC294X_REG_ACC_CHARGE_LSB = 0x03, - LTC294X_REG_THRESH_HIGH_MSB = 0x04, - LTC294X_REG_THRESH_HIGH_LSB = 0x05, - LTC294X_REG_THRESH_LOW_MSB = 0x06, - LTC294X_REG_THRESH_LOW_LSB = 0x07, - LTC294X_REG_VOLTAGE_MSB = 0x08, - LTC294X_REG_VOLTAGE_LSB = 0x09, - LTC294X_REG_CURRENT_MSB = 0x0E, - LTC294X_REG_CURRENT_LSB = 0x0F, - LTC294X_REG_TEMPERATURE_MSB = 0x14, - LTC294X_REG_TEMPERATURE_LSB = 0x15, + LTC294X_REG_VOLTAGE_MSB = 0x08, + LTC294X_REG_VOLTAGE_LSB = 0x09, + LTC2942_REG_TEMPERATURE_MSB = 0x0C, + LTC2942_REG_TEMPERATURE_LSB = 0x0D, + LTC2943_REG_CURRENT_MSB = 0x0E, + LTC2943_REG_CURRENT_LSB = 0x0F, + LTC2943_REG_TEMPERATURE_MSB = 0x14, + LTC2943_REG_TEMPERATURE_LSB = 0x15, }; -#define LTC2943_REG_CONTROL_MODE_MASK (BIT(7) | BIT(6)) -#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7) +enum ltc294x_id { + LTC2941_ID, + LTC2942_ID, + LTC2943_ID, + LTC2944_ID, +}; + +#define LTC2941_REG_STATUS_CHIP_ID BIT(7) + +#define LTC2942_REG_CONTROL_MODE_SCAN (BIT(7) | BIT(6)) +#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7) #define LTC294X_REG_CONTROL_PRESCALER_MASK (BIT(5) | BIT(4) | BIT(3)) #define LTC294X_REG_CONTROL_SHUTDOWN_MASK (BIT(0)) #define LTC294X_REG_CONTROL_PRESCALER_SET(x) \ ((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK) #define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0 -#define LTC2941_NUM_REGS 0x08 -#define LTC2943_NUM_REGS 0x18 - struct ltc294x_info { struct i2c_client *client; /* I2C Client pointer */ struct power_supply *supply; /* Supply pointer */ struct power_supply_desc supply_desc; /* Supply description */ struct delayed_work work; /* Work scheduler */ - unsigned long num_regs; /* Number of registers (chip type) */ + enum ltc294x_id id; /* Chip type */ int charge; /* Last charge register content */ int r_sense; /* mOhm */ int Qlsb; /* nAh */ @@ -145,9 +149,18 @@ static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp) control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) | LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED; - /* Put the 2943 into "monitor" mode, so it measures every 10 sec */ - if (info->num_regs == LTC2943_NUM_REGS) + /* Put device into "monitor" mode */ + switch (info->id) { + case LTC2942_ID: /* 2942 measures every 2 sec */ + control |= LTC2942_REG_CONTROL_MODE_SCAN; + break; + case LTC2943_ID: + case LTC2944_ID: /* 2943 and 2944 measure every 10 sec */ control |= LTC2943_REG_CONTROL_MODE_SCAN; + break; + default: + break; + } if (value != control) { ret = ltc294x_write_regs(info->client, @@ -252,7 +265,24 @@ static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val) ret = ltc294x_read_regs(info->client, LTC294X_REG_VOLTAGE_MSB, &datar[0], 2); value = (datar[0] << 8) | datar[1]; - *val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */ + switch (info->id) { + case LTC2943_ID: + value *= 23600 * 2; + value /= 0xFFFF; + value *= 1000 / 2; + break; + case LTC2944_ID: + value *= 70800 / 5*4; + value /= 0xFFFF; + value *= 1000 * 5/4; + break; + default: + value *= 6000 * 10; + value /= 0xFFFF; + value *= 1000 / 10; + break; + } + *val = value; return ret; } @@ -263,27 +293,38 @@ static int ltc294x_get_current(const struct ltc294x_info *info, int *val) s32 value; ret = ltc294x_read_regs(info->client, - LTC294X_REG_CURRENT_MSB, &datar[0], 2); + LTC2943_REG_CURRENT_MSB, &datar[0], 2); value = (datar[0] << 8) | datar[1]; value -= 0x7FFF; + if (info->id == LTC2944_ID) + value *= 64000; + else + value *= 60000; /* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm, * the formula below keeps everything in s32 range while preserving * enough digits */ - *val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */ + *val = 1000 * (value / (info->r_sense * 0x7FFF)); /* in uA */ return ret; } static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val) { + enum ltc294x_reg reg; int ret; u8 datar[2]; u32 value; - ret = ltc294x_read_regs(info->client, - LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2); - value = (datar[0] << 8) | datar[1]; - /* Full-scale is 510 Kelvin, convert to centidegrees */ - *val = (((51000 * value) / 0xFFFF) - 27215); + if (info->id == LTC2942_ID) { + reg = LTC2942_REG_TEMPERATURE_MSB; + value = 60000; /* Full-scale is 600 Kelvin */ + } else { + reg = LTC2943_REG_TEMPERATURE_MSB; + value = 51000; /* Full-scale is 510 Kelvin */ + } + ret = ltc294x_read_regs(info->client, reg, &datar[0], 2); + value *= (datar[0] << 8) | datar[1]; + /* Convert to centidegrees */ + *val = value / 0xFFFF - 27215; return ret; } @@ -357,8 +398,8 @@ static enum power_supply_property ltc294x_properties[] = { POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CURRENT_NOW, }; static int ltc294x_i2c_remove(struct i2c_client *client) @@ -375,10 +416,11 @@ static int ltc294x_i2c_probe(struct i2c_client *client, { struct power_supply_config psy_cfg = {}; struct ltc294x_info *info; + struct device_node *np; int ret; u32 prescaler_exp; s32 r_sense; - struct device_node *np; + u8 status; info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); if (info == NULL) @@ -388,7 +430,7 @@ static int ltc294x_i2c_probe(struct i2c_client *client, np = of_node_get(client->dev.of_node); - info->num_regs = (unsigned long)of_device_get_match_data(&client->dev); + info->id = (enum ltc294x_id)of_device_get_match_data(&client->dev); info->supply_desc.name = np->name; /* r_sense can be negative, when sense+ is connected to the battery @@ -409,7 +451,7 @@ static int ltc294x_i2c_probe(struct i2c_client *client, prescaler_exp = LTC2941_MAX_PRESCALER_EXP; } - if (info->num_regs == LTC2943_NUM_REGS) { + if (info->id == LTC2943_ID) { if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP) prescaler_exp = LTC2943_MAX_PRESCALER_EXP; info->Qlsb = ((340 * 50000) / r_sense) / @@ -421,21 +463,39 @@ static int ltc294x_i2c_probe(struct i2c_client *client, (128 / (1 << prescaler_exp)); } + /* Read status register to check for LTC2942 */ + if (info->id == LTC2941_ID || info->id == LTC2942_ID) { + ret = ltc294x_read_regs(client, LTC294X_REG_STATUS, &status, 1); + if (ret < 0) { + dev_err(&client->dev, + "Could not read status register\n"); + return ret; + } + if (status & LTC2941_REG_STATUS_CHIP_ID) + info->id = LTC2941_ID; + else + info->id = LTC2942_ID; + } + info->client = client; info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY; info->supply_desc.properties = ltc294x_properties; - if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB) + switch (info->id) { + case LTC2944_ID: + case LTC2943_ID: info->supply_desc.num_properties = ARRAY_SIZE(ltc294x_properties); - else if (info->num_regs >= LTC294X_REG_CURRENT_LSB) + break; + case LTC2942_ID: info->supply_desc.num_properties = ARRAY_SIZE(ltc294x_properties) - 1; - else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB) - info->supply_desc.num_properties = - ARRAY_SIZE(ltc294x_properties) - 2; - else + break; + case LTC2941_ID: + default: info->supply_desc.num_properties = ARRAY_SIZE(ltc294x_properties) - 3; + break; + } info->supply_desc.get_property = ltc294x_get_property; info->supply_desc.set_property = ltc294x_set_property; info->supply_desc.property_is_writeable = ltc294x_property_is_writeable; @@ -492,8 +552,10 @@ static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume); static const struct i2c_device_id ltc294x_i2c_id[] = { - {"ltc2941", LTC2941_NUM_REGS}, - {"ltc2943", LTC2943_NUM_REGS}, + { "ltc2941", LTC2941_ID, }, + { "ltc2942", LTC2942_ID, }, + { "ltc2943", LTC2943_ID, }, + { "ltc2944", LTC2944_ID, }, { }, }; MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id); @@ -501,11 +563,19 @@ MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id); static const struct of_device_id ltc294x_i2c_of_match[] = { { .compatible = "lltc,ltc2941", - .data = (void *)LTC2941_NUM_REGS + .data = (void *)LTC2941_ID, + }, + { + .compatible = "lltc,ltc2942", + .data = (void *)LTC2942_ID, }, { .compatible = "lltc,ltc2943", - .data = (void *)LTC2943_NUM_REGS + .data = (void *)LTC2943_ID, + }, + { + .compatible = "lltc,ltc2944", + .data = (void *)LTC2944_ID, }, { }, }; @@ -525,5 +595,5 @@ module_i2c_driver(ltc294x_driver); MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems"); MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products"); -MODULE_DESCRIPTION("LTC2941/LTC2943 Battery Gas Gauge IC driver"); +MODULE_DESCRIPTION("LTC2941/LTC2942/LTC2943/LTC2944 Battery Gas Gauge IC driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index aecaaa2b0586..5b556a13f517 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -22,6 +22,7 @@ * This driver is based on max17040_battery.c */ +#include <linux/acpi.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> @@ -982,6 +983,8 @@ static int max17042_probe(struct i2c_client *client, struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); const struct power_supply_desc *max17042_desc = &max17042_psy_desc; struct power_supply_config psy_cfg = {}; + const struct acpi_device_id *acpi_id = NULL; + struct device *dev = &client->dev; struct max17042_chip *chip; int ret; int i; @@ -995,7 +998,15 @@ static int max17042_probe(struct i2c_client *client, return -ENOMEM; chip->client = client; - chip->chip_type = id->driver_data; + if (id) { + chip->chip_type = id->driver_data; + } else { + acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!acpi_id) + return -ENODEV; + + chip->chip_type = acpi_id->driver_data; + } chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config); if (IS_ERR(chip->regmap)) { dev_err(&client->dev, "Failed to initialize regmap\n"); @@ -1039,11 +1050,18 @@ static int max17042_probe(struct i2c_client *client, } if (client->irq) { + unsigned int flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + + /* + * On ACPI systems the IRQ may be handled by ACPI-event code, + * so we need to share (if the ACPI code is willing to share). + */ + if (acpi_id) + flags |= IRQF_SHARED | IRQF_PROBE_SHARED; + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, - max17042_thread_handler, - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, + max17042_thread_handler, flags, chip->battery->desc->name, chip); if (!ret) { @@ -1053,10 +1071,13 @@ static int max17042_probe(struct i2c_client *client, max17042_set_soc_threshold(chip, 1); } else { client->irq = 0; - dev_err(&client->dev, "%s(): cannot get IRQ\n", - __func__); + if (ret != -EBUSY) + dev_err(&client->dev, "Failed to get IRQ\n"); } } + /* Not able to update the charge threshold when exceeded? -> disable */ + if (!client->irq) + regmap_write(chip->regmap, MAX17042_SALRT_Th, 0xff00); regmap_read(chip->regmap, MAX17042_STATUS, &val); if (val & STATUS_POR_BIT) { @@ -1104,6 +1125,14 @@ static int max17042_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend, max17042_resume); +#ifdef CONFIG_ACPI +static const struct acpi_device_id max17042_acpi_match[] = { + { "MAX17047", MAXIM_DEVICE_TYPE_MAX17047 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, max17042_acpi_match); +#endif + #ifdef CONFIG_OF static const struct of_device_id max17042_dt_match[] = { { .compatible = "maxim,max17042" }, @@ -1125,6 +1154,7 @@ MODULE_DEVICE_TABLE(i2c, max17042_id); static struct i2c_driver max17042_i2c_driver = { .driver = { .name = "max17042", + .acpi_match_table = ACPI_PTR(max17042_acpi_match), .of_match_table = of_match_ptr(max17042_dt_match), .pm = &max17042_pm_ops, }, diff --git a/drivers/power/supply/max1721x_battery.c b/drivers/power/supply/max1721x_battery.c new file mode 100644 index 000000000000..9ee601a03d9b --- /dev/null +++ b/drivers/power/supply/max1721x_battery.c @@ -0,0 +1,448 @@ +/* + * 1-Wire implementation for Maxim Semiconductor + * MAX7211/MAX17215 stanalone fuel gauge chip + * + * Copyright (C) 2017 Radioavionica Corporation + * Author: Alex A. Mihaylov <minimumlaw@rambler.ru> + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/w1.h> +#include <linux/regmap.h> +#include <linux/power_supply.h> + +#define W1_MAX1721X_FAMILY_ID 0x26 +#define DEF_DEV_NAME_MAX17211 "MAX17211" +#define DEF_DEV_NAME_MAX17215 "MAX17215" +#define DEF_DEV_NAME_UNKNOWN "UNKNOWN" +#define DEF_MFG_NAME "MAXIM" + +#define PSY_MAX_NAME_LEN 32 + +/* Number of valid register addresses in W1 mode */ +#define MAX1721X_MAX_REG_NR 0x1EF + +/* Factory settings (nonvilatile registers) (W1 specific) */ +#define MAX1721X_REG_NRSENSE 0x1CF /* RSense in 10^-5 Ohm */ +/* Strings */ +#define MAX1721X_REG_MFG_STR 0x1CC +#define MAX1721X_REG_MFG_NUMB 3 +#define MAX1721X_REG_DEV_STR 0x1DB +#define MAX1721X_REG_DEV_NUMB 5 +/* HEX Strings */ +#define MAX1721X_REG_SER_HEX 0x1D8 + +/* MAX172XX Output Registers for W1 chips */ +#define MAX172XX_REG_STATUS 0x000 /* status reg */ +#define MAX172XX_BAT_PRESENT (1<<4) /* battery connected bit */ +#define MAX172XX_REG_DEVNAME 0x021 /* chip config */ +#define MAX172XX_DEV_MASK 0x000F /* chip type mask */ +#define MAX172X1_DEV 0x0001 +#define MAX172X5_DEV 0x0005 +#define MAX172XX_REG_TEMP 0x008 /* Temperature */ +#define MAX172XX_REG_BATT 0x0DA /* Battery voltage */ +#define MAX172XX_REG_CURRENT 0x00A /* Actual current */ +#define MAX172XX_REG_AVGCURRENT 0x00B /* Average current */ +#define MAX172XX_REG_REPSOC 0x006 /* Percentage of charge */ +#define MAX172XX_REG_DESIGNCAP 0x018 /* Design capacity */ +#define MAX172XX_REG_REPCAP 0x005 /* Average capacity */ +#define MAX172XX_REG_TTE 0x011 /* Time to empty */ +#define MAX172XX_REG_TTF 0x020 /* Time to full */ + +struct max17211_device_info { + char name[PSY_MAX_NAME_LEN]; + struct power_supply *bat; + struct power_supply_desc bat_desc; + struct device *w1_dev; + struct regmap *regmap; + /* battery design format */ + unsigned int rsense; /* in tenths uOhm */ + char DeviceName[2 * MAX1721X_REG_DEV_NUMB + 1]; + char ManufacturerName[2 * MAX1721X_REG_MFG_NUMB + 1]; + char SerialNumber[13]; /* see get_sn_str() later for comment */ +}; + +/* Convert regs value to power_supply units */ + +static inline int max172xx_time_to_ps(unsigned int reg) +{ + return reg * 5625 / 1000; /* in sec. */ +} + +static inline int max172xx_percent_to_ps(unsigned int reg) +{ + return reg / 256; /* in percent from 0 to 100 */ +} + +static inline int max172xx_voltage_to_ps(unsigned int reg) +{ + return reg * 1250; /* in uV */ +} + +static inline int max172xx_capacity_to_ps(unsigned int reg) +{ + return reg * 500; /* in uAh */ +} + +/* + * Current and temperature is signed values, so unsigned regs + * value must be converted to signed type + */ + +static inline int max172xx_temperature_to_ps(unsigned int reg) +{ + int val = (int16_t)(reg); + + return val * 10 / 256; /* in tenths of deg. C */ +} + +/* + * Calculating current registers resolution: + * + * RSense stored in 10^-5 Ohm, so mesaurment voltage must be + * in 10^-11 Volts for get current in uA. + * 16 bit current reg fullscale +/-51.2mV is 102400 uV. + * So: 102400 / 65535 * 10^5 = 156252 + */ +static inline int max172xx_current_to_voltage(unsigned int reg) +{ + int val = (int16_t)(reg); + + return val * 156252; +} + + +static inline struct max17211_device_info * +to_device_info(struct power_supply *psy) +{ + return power_supply_get_drvdata(psy); +} + +static int max1721x_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max17211_device_info *info = to_device_info(psy); + unsigned int reg = 0; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + /* + * POWER_SUPPLY_PROP_PRESENT will always readable via + * sysfs interface. Value return 0 if battery not + * present or unaccesable via W1. + */ + val->intval = + regmap_read(info->regmap, MAX172XX_REG_STATUS, + ®) ? 0 : !(reg & MAX172XX_BAT_PRESENT); + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = regmap_read(info->regmap, MAX172XX_REG_REPSOC, ®); + val->intval = max172xx_percent_to_ps(reg); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = regmap_read(info->regmap, MAX172XX_REG_BATT, ®); + val->intval = max172xx_voltage_to_ps(reg); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = regmap_read(info->regmap, MAX172XX_REG_DESIGNCAP, ®); + val->intval = max172xx_capacity_to_ps(reg); + break; + case POWER_SUPPLY_PROP_CHARGE_AVG: + ret = regmap_read(info->regmap, MAX172XX_REG_REPCAP, ®); + val->intval = max172xx_capacity_to_ps(reg); + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + ret = regmap_read(info->regmap, MAX172XX_REG_TTE, ®); + val->intval = max172xx_time_to_ps(reg); + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + ret = regmap_read(info->regmap, MAX172XX_REG_TTF, ®); + val->intval = max172xx_time_to_ps(reg); + break; + case POWER_SUPPLY_PROP_TEMP: + ret = regmap_read(info->regmap, MAX172XX_REG_TEMP, ®); + val->intval = max172xx_temperature_to_ps(reg); + break; + /* We need signed current, so must cast info->rsense to signed type */ + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = regmap_read(info->regmap, MAX172XX_REG_CURRENT, ®); + val->intval = + max172xx_current_to_voltage(reg) / (int)info->rsense; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + ret = regmap_read(info->regmap, MAX172XX_REG_AVGCURRENT, ®); + val->intval = + max172xx_current_to_voltage(reg) / (int)info->rsense; + break; + /* + * Strings already received and inited by probe. + * We do dummy read for check battery still available. + */ + case POWER_SUPPLY_PROP_MODEL_NAME: + ret = regmap_read(info->regmap, MAX1721X_REG_DEV_STR, ®); + val->strval = info->DeviceName; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + ret = regmap_read(info->regmap, MAX1721X_REG_MFG_STR, ®); + val->strval = info->ManufacturerName; + break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + ret = regmap_read(info->regmap, MAX1721X_REG_SER_HEX, ®); + val->strval = info->SerialNumber; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static enum power_supply_property max1721x_battery_props[] = { + /* int */ + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_AVG, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + /* strings */ + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_SERIAL_NUMBER, +}; + +static int get_string(struct max17211_device_info *info, + uint16_t reg, uint8_t nr, char *str) +{ + unsigned int val; + + if (!str || !(reg == MAX1721X_REG_MFG_STR || + reg == MAX1721X_REG_DEV_STR)) + return -EFAULT; + + while (nr--) { + if (regmap_read(info->regmap, reg++, &val)) + return -EFAULT; + *str++ = val>>8 & 0x00FF; + *str++ = val & 0x00FF; + } + return 0; +} + +/* Maxim say: Serial number is a hex string up to 12 hex characters */ +static int get_sn_string(struct max17211_device_info *info, char *str) +{ + unsigned int val[3]; + + if (!str) + return -EFAULT; + + if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX, &val[0])) + return -EFAULT; + if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 1, &val[1])) + return -EFAULT; + if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 2, &val[2])) + return -EFAULT; + + snprintf(str, 13, "%04X%04X%04X", val[0], val[1], val[2]); + return 0; +} + +/* + * MAX1721x registers description for w1-regmap + */ +static const struct regmap_range max1721x_allow_range[] = { + regmap_reg_range(0, 0xDF), /* volatile data */ + regmap_reg_range(0x180, 0x1DF), /* non-volatile memory */ + regmap_reg_range(0x1E0, 0x1EF), /* non-volatile history (unused) */ +}; + +static const struct regmap_range max1721x_deny_range[] = { + /* volatile data unused registers */ + regmap_reg_range(0x24, 0x26), + regmap_reg_range(0x30, 0x31), + regmap_reg_range(0x33, 0x34), + regmap_reg_range(0x37, 0x37), + regmap_reg_range(0x3B, 0x3C), + regmap_reg_range(0x40, 0x41), + regmap_reg_range(0x43, 0x44), + regmap_reg_range(0x47, 0x49), + regmap_reg_range(0x4B, 0x4C), + regmap_reg_range(0x4E, 0xAF), + regmap_reg_range(0xB1, 0xB3), + regmap_reg_range(0xB5, 0xB7), + regmap_reg_range(0xBF, 0xD0), + regmap_reg_range(0xDB, 0xDB), + /* hole between volatile and non-volatile registers */ + regmap_reg_range(0xE0, 0x17F), +}; + +static const struct regmap_access_table max1721x_regs = { + .yes_ranges = max1721x_allow_range, + .n_yes_ranges = ARRAY_SIZE(max1721x_allow_range), + .no_ranges = max1721x_deny_range, + .n_no_ranges = ARRAY_SIZE(max1721x_deny_range), +}; + +/* + * Model Gauge M5 Algorithm output register + * Volatile data (must not be cached) + */ +static const struct regmap_range max1721x_volatile_allow[] = { + regmap_reg_range(0, 0xDF), +}; + +static const struct regmap_access_table max1721x_volatile_regs = { + .yes_ranges = max1721x_volatile_allow, + .n_yes_ranges = ARRAY_SIZE(max1721x_volatile_allow), +}; + +/* + * W1-regmap config + */ +static const struct regmap_config max1721x_regmap_w1_config = { + .reg_bits = 16, + .val_bits = 16, + .rd_table = &max1721x_regs, + .volatile_table = &max1721x_volatile_regs, + .max_register = MAX1721X_MAX_REG_NR, +}; + +static int devm_w1_max1721x_add_device(struct w1_slave *sl) +{ + struct power_supply_config psy_cfg = {}; + struct max17211_device_info *info; + + info = devm_kzalloc(&sl->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + sl->family_data = (void *)info; + info->w1_dev = &sl->dev; + + /* + * power_supply class battery name translated from W1 slave device + * unical ID (look like 26-0123456789AB) to "max1721x-0123456789AB\0" + * so, 26 (device family) correcpondent to max1721x devices. + * Device name still unical for any numbers connected devices. + */ + snprintf(info->name, sizeof(info->name), + "max1721x-%012X", (unsigned int)sl->reg_num.id); + info->bat_desc.name = info->name; + + /* + * FixMe: battery device name exceed max len for thermal_zone device + * name and translation to thermal_zone must be disabled. + */ + info->bat_desc.no_thermal = true; + info->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; + info->bat_desc.properties = max1721x_battery_props; + info->bat_desc.num_properties = ARRAY_SIZE(max1721x_battery_props); + info->bat_desc.get_property = max1721x_battery_get_property; + psy_cfg.drv_data = info; + + /* regmap init */ + info->regmap = devm_regmap_init_w1(info->w1_dev, + &max1721x_regmap_w1_config); + if (IS_ERR(info->regmap)) { + int err = PTR_ERR(info->regmap); + + dev_err(info->w1_dev, "Failed to allocate register map: %d\n", + err); + return err; + } + + /* rsense init */ + info->rsense = 0; + if (regmap_read(info->regmap, MAX1721X_REG_NRSENSE, &info->rsense)) { + dev_err(info->w1_dev, "Can't read RSense. Hardware error.\n"); + return -ENODEV; + } + + if (!info->rsense) { + dev_warn(info->w1_dev, "RSenese not calibrated, set 10 mOhms!\n"); + info->rsense = 1000; /* in regs in 10^-5 */ + } + dev_info(info->w1_dev, "RSense: %d mOhms.\n", info->rsense / 100); + + if (get_string(info, MAX1721X_REG_MFG_STR, + MAX1721X_REG_MFG_NUMB, info->ManufacturerName)) { + dev_err(info->w1_dev, "Can't read manufacturer. Hardware error.\n"); + return -ENODEV; + } + + if (!info->ManufacturerName[0]) + strncpy(info->ManufacturerName, DEF_MFG_NAME, + 2 * MAX1721X_REG_MFG_NUMB); + + if (get_string(info, MAX1721X_REG_DEV_STR, + MAX1721X_REG_DEV_NUMB, info->DeviceName)) { + dev_err(info->w1_dev, "Can't read device. Hardware error.\n"); + return -ENODEV; + } + if (!info->DeviceName[0]) { + unsigned int dev_name; + + if (regmap_read(info->regmap, + MAX172XX_REG_DEVNAME, &dev_name)) { + dev_err(info->w1_dev, "Can't read device name reg.\n"); + return -ENODEV; + } + + switch (dev_name & MAX172XX_DEV_MASK) { + case MAX172X1_DEV: + strncpy(info->DeviceName, DEF_DEV_NAME_MAX17211, + 2 * MAX1721X_REG_DEV_NUMB); + break; + case MAX172X5_DEV: + strncpy(info->DeviceName, DEF_DEV_NAME_MAX17215, + 2 * MAX1721X_REG_DEV_NUMB); + break; + default: + strncpy(info->DeviceName, DEF_DEV_NAME_UNKNOWN, + 2 * MAX1721X_REG_DEV_NUMB); + } + } + + if (get_sn_string(info, info->SerialNumber)) { + dev_err(info->w1_dev, "Can't read serial. Hardware error.\n"); + return -ENODEV; + } + + info->bat = devm_power_supply_register(&sl->dev, &info->bat_desc, + &psy_cfg); + if (IS_ERR(info->bat)) { + dev_err(info->w1_dev, "failed to register battery\n"); + return PTR_ERR(info->bat); + } + + return 0; +} + +static struct w1_family_ops w1_max1721x_fops = { + .add_slave = devm_w1_max1721x_add_device, +}; + +static struct w1_family w1_max1721x_family = { + .fid = W1_MAX1721X_FAMILY_ID, + .fops = &w1_max1721x_fops, +}; + +module_w1_family(w1_max1721x_family); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alex A. Mihaylov <minimumlaw@rambler.ru>"); +MODULE_DESCRIPTION("Maxim MAX17211/MAX17215 Fuel Gauage IC driver"); +MODULE_ALIAS("w1-family-" __stringify(W1_MAX1721X_FAMILY_ID)); diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c index 9e29b1321648..3bc2eea7b3b7 100644 --- a/drivers/power/supply/olpc_battery.c +++ b/drivers/power/supply/olpc_battery.c @@ -535,7 +535,7 @@ static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj, return count; } -static struct bin_attribute olpc_bat_eeprom = { +static const struct bin_attribute olpc_bat_eeprom = { .attr = { .name = "eeprom", .mode = S_IRUGO, @@ -559,7 +559,7 @@ static ssize_t olpc_bat_error_read(struct device *dev, return sprintf(buf, "%d\n", ec_byte); } -static struct device_attribute olpc_bat_error = { +static const struct device_attribute olpc_bat_error = { .attr = { .name = "error", .mode = S_IRUGO, diff --git a/drivers/power/supply/pcf50633-charger.c b/drivers/power/supply/pcf50633-charger.c index b3c1873ad84d..1ad7ccce6075 100644 --- a/drivers/power/supply/pcf50633-charger.c +++ b/drivers/power/supply/pcf50633-charger.c @@ -254,7 +254,7 @@ static struct attribute *pcf50633_mbc_sysfs_entries[] = { NULL, }; -static struct attribute_group mbc_attr_group = { +static const struct attribute_group mbc_attr_group = { .name = NULL, /* put in device directory */ .attrs = pcf50633_mbc_sysfs_entries, }; diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 540d3e0aa011..02c6340ae36f 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -259,18 +259,14 @@ static int power_supply_check_supplies(struct power_supply *psy) /* All supplies found, allocate char ** array for filling */ psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from), GFP_KERNEL); - if (!psy->supplied_from) { - dev_err(&psy->dev, "Couldn't allocate memory for supply list\n"); + if (!psy->supplied_from) return -ENOMEM; - } *psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(char *) * (cnt - 1), GFP_KERNEL); - if (!*psy->supplied_from) { - dev_err(&psy->dev, "Couldn't allocate memory for supply list\n"); + if (!*psy->supplied_from) return -ENOMEM; - } return power_supply_populate_supplied_from(psy); } @@ -314,11 +310,12 @@ static int __power_supply_am_i_supplied(struct device *dev, void *_data) struct power_supply *epsy = dev_get_drvdata(dev); struct psy_am_i_supplied_data *data = _data; - data->count++; - if (__power_supply_is_supplied_by(epsy, data->psy)) + if (__power_supply_is_supplied_by(epsy, data->psy)) { + data->count++; if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) return ret.intval; + } return 0; } @@ -374,6 +371,47 @@ int power_supply_is_system_supplied(void) } EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); +static int __power_supply_get_supplier_max_current(struct device *dev, + void *data) +{ + union power_supply_propval ret = {0,}; + struct power_supply *epsy = dev_get_drvdata(dev); + struct power_supply *psy = data; + + if (__power_supply_is_supplied_by(epsy, psy)) + if (!epsy->desc->get_property(epsy, + POWER_SUPPLY_PROP_CURRENT_MAX, + &ret)) + return ret.intval; + + return 0; +} + +int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy) +{ + union power_supply_propval val = {0,}; + int curr; + + if (!psy->desc->set_property) + return -EINVAL; + + /* + * This function is not intended for use with a supply with multiple + * suppliers, we simply pick the first supply to report a non 0 + * max-current. + */ + curr = class_for_each_device(power_supply_class, NULL, psy, + __power_supply_get_supplier_max_current); + if (curr <= 0) + return (curr == 0) ? -ENODEV : curr; + + val.intval = curr; + + return psy->desc->set_property(psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); +} +EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier); + int power_supply_set_battery_charged(struct power_supply *psy) { if (atomic_read(&psy->use_cnt) >= 0 && diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index f7059459f0fb..b19a73176910 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -12,25 +12,21 @@ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/kernel.h> +#include <linux/delay.h> #include <linux/err.h> -#include <linux/power_supply.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> -#include <linux/slab.h> +#include <linux/init.h> #include <linux/interrupt.h> -#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/of.h> -#include <linux/stat.h> - #include <linux/power/sbs-battery.h> +#include <linux/power_supply.h> +#include <linux/slab.h> +#include <linux/stat.h> enum { REG_MANUFACTURER_DATA, @@ -60,8 +56,8 @@ enum { #define BATTERY_MODE_OFFSET 0x03 #define BATTERY_MODE_MASK 0x8000 enum sbs_battery_mode { - BATTERY_MODE_AMPS, - BATTERY_MODE_WATTS + BATTERY_MODE_AMPS = 0, + BATTERY_MODE_WATTS = 0x8000 }; /* manufacturer access defines */ @@ -532,6 +528,8 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client, if (ret < 0) return ret; + usleep_range(1000, 2000); + return original_val & BATTERY_MODE_MASK; } diff --git a/drivers/pps/Kconfig b/drivers/pps/Kconfig index 4b29a7182d7b..c6008f296605 100644 --- a/drivers/pps/Kconfig +++ b/drivers/pps/Kconfig @@ -19,9 +19,10 @@ menuconfig PPS To compile this driver as a module, choose M here: the module will be called pps_core.ko. +if PPS + config PPS_DEBUG bool "PPS debugging messages" - depends on PPS help Say Y here if you want the PPS support to produce a bunch of debug messages to the system log. Select this if you are having a @@ -29,7 +30,7 @@ config PPS_DEBUG config NTP_PPS bool "PPS kernel consumer support" - depends on PPS && !NO_HZ_COMMON + depends on !NO_HZ_COMMON help This option adds support for direct in-kernel time synchronization using an external PPS signal. @@ -39,3 +40,5 @@ config NTP_PPS source drivers/pps/clients/Kconfig source drivers/pps/generators/Kconfig + +endif # PPS diff --git a/drivers/pps/clients/Kconfig b/drivers/pps/clients/Kconfig index efec021ce662..7f02a9b1a1fd 100644 --- a/drivers/pps/clients/Kconfig +++ b/drivers/pps/clients/Kconfig @@ -3,11 +3,9 @@ # comment "PPS clients support" - depends on PPS config PPS_CLIENT_KTIMER tristate "Kernel timer client (Testing client, use for debug)" - depends on PPS help If you say yes here you get support for a PPS debugging client which uses a kernel timer to generate the PPS signal. @@ -17,21 +15,20 @@ config PPS_CLIENT_KTIMER config PPS_CLIENT_LDISC tristate "PPS line discipline" - depends on PPS && TTY + depends on TTY help If you say yes here you get support for a PPS source connected with the CD (Carrier Detect) pin of your serial port. config PPS_CLIENT_PARPORT tristate "Parallel port PPS client" - depends on PPS && PARPORT + depends on PARPORT help If you say yes here you get support for a PPS source connected with the interrupt pin of your parallel port. config PPS_CLIENT_GPIO tristate "PPS client using GPIO" - depends on PPS help If you say yes here you get support for a PPS source using GPIO. To be useful you must also register a platform device diff --git a/drivers/pps/generators/Kconfig b/drivers/pps/generators/Kconfig index 86b59378e71f..e4c4f3dc0728 100644 --- a/drivers/pps/generators/Kconfig +++ b/drivers/pps/generators/Kconfig @@ -3,11 +3,10 @@ # comment "PPS generators support" - depends on PPS config PPS_GENERATOR_PARPORT tristate "Parallel port PPS signal generator" - depends on PPS && PARPORT && BROKEN + depends on PARPORT && BROKEN help If you say yes here you get support for a PPS signal generator which utilizes STROBE pin of a parallel port to send PPS signals. It uses diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 7cb982b54c8c..763ee50ea57d 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -300,7 +300,7 @@ config PWM_MEDIATEK Generic PWM framework driver for Mediatek ARM SoC. To compile this driver as a module, choose M here: the module - will be called pwm-mxs. + will be called pwm-mediatek. config PWM_MXS tristate "Freescale MXS PWM support" @@ -456,7 +456,7 @@ config PWM_TEGRA config PWM_TIECAP tristate "ECAP PWM support" - depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX + depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE help PWM driver support for the ECAP APWM controller found on AM33XX TI SOC @@ -510,4 +510,13 @@ config PWM_VT8500 To compile this driver as a module, choose M here: the module will be called pwm-vt8500. +config PWM_ZX + tristate "ZTE ZX PWM support" + depends on ARCH_ZX + help + Generic PWM framework driver for ZTE ZX family SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-zx. + endif diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index a3a4beef6daa..ebefba5f528b 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -50,3 +50,4 @@ obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL) += pwm-twl.o obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o +obj-$(CONFIG_PWM_ZX) += pwm-zx.o diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c index c5dbf16d810b..db001cba937f 100644 --- a/drivers/pwm/pwm-bcm2835.c +++ b/drivers/pwm/pwm-bcm2835.c @@ -167,6 +167,8 @@ static int bcm2835_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &bcm2835_pwm_ops; pc->chip.npwm = 2; + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.of_pwm_n_cells = 3; platform_set_drvdata(pdev, pc); diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index 8dadc58d6cdf..27c107e78d59 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -208,7 +208,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev) if (ret < 0) return ret; - pwm_chip->rstc = devm_reset_control_get(&pdev->dev, NULL); + pwm_chip->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(pwm_chip->rstc)) { clk_disable_unprepare(pwm_chip->clk); return PTR_ERR(pwm_chip->rstc); diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 5c11bc708a3c..b52f3afb2ba1 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -2,6 +2,7 @@ * Mediatek Pulse Width Modulator driver * * Copyright (C) 2015 John Crispin <blogic@openwrt.org> + * Copyright (C) 2017 Zhi Mao <zhi.mao@mediatek.com> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any @@ -29,6 +30,8 @@ #define PWMDWIDTH 0x2c #define PWMTHRES 0x30 +#define PWM_CLK_DIV_MAX 7 + enum { MTK_CLK_MAIN = 0, MTK_CLK_TOP, @@ -61,6 +64,42 @@ static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip) return container_of(chip, struct mtk_pwm_chip, chip); } +static int mtk_pwm_clk_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); + int ret; + + ret = clk_prepare_enable(pc->clks[MTK_CLK_TOP]); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(pc->clks[MTK_CLK_MAIN]); + if (ret < 0) + goto disable_clk_top; + + ret = clk_prepare_enable(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); + if (ret < 0) + goto disable_clk_main; + + return 0; + +disable_clk_main: + clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]); +disable_clk_top: + clk_disable_unprepare(pc->clks[MTK_CLK_TOP]); + + return ret; +} + +static void mtk_pwm_clk_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); + + clk_disable_unprepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); + clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]); + clk_disable_unprepare(pc->clks[MTK_CLK_TOP]); +} + static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num, unsigned int offset) { @@ -80,6 +119,11 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]; u32 resolution, clkdiv = 0; + int ret; + + ret = mtk_pwm_clk_enable(chip, pwm); + if (ret < 0) + return ret; resolution = NSEC_PER_SEC / clk_get_rate(clk); @@ -88,13 +132,18 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, clkdiv++; } - if (clkdiv > 7) + if (clkdiv > PWM_CLK_DIV_MAX) { + mtk_pwm_clk_disable(chip, pwm); + dev_err(chip->dev, "period %d not supported\n", period_ns); return -EINVAL; + } - mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | BIT(3) | clkdiv); + mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution); mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution); + mtk_pwm_clk_disable(chip, pwm); + return 0; } @@ -104,7 +153,7 @@ static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) u32 value; int ret; - ret = clk_prepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); + ret = mtk_pwm_clk_enable(chip, pwm); if (ret < 0) return ret; @@ -124,7 +173,7 @@ static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) value &= ~BIT(pwm->hwpwm); writel(value, pc->regs); - clk_unprepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); + mtk_pwm_clk_disable(chip, pwm); } static const struct pwm_ops mtk_pwm_ops = { @@ -156,14 +205,6 @@ static int mtk_pwm_probe(struct platform_device *pdev) return PTR_ERR(pc->clks[i]); } - ret = clk_prepare(pc->clks[MTK_CLK_TOP]); - if (ret < 0) - return ret; - - ret = clk_prepare(pc->clks[MTK_CLK_MAIN]); - if (ret < 0) - goto disable_clk_top; - platform_set_drvdata(pdev, pc); pc->chip.dev = &pdev->dev; @@ -174,26 +215,15 @@ static int mtk_pwm_probe(struct platform_device *pdev) ret = pwmchip_add(&pc->chip); if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - goto disable_clk_main; + return ret; } return 0; - -disable_clk_main: - clk_unprepare(pc->clks[MTK_CLK_MAIN]); -disable_clk_top: - clk_unprepare(pc->clks[MTK_CLK_TOP]); - - return ret; } static int mtk_pwm_remove(struct platform_device *pdev) { struct mtk_pwm_chip *pc = platform_get_drvdata(pdev); - unsigned int i; - - for (i = 0; i < pc->chip.npwm; i++) - pwm_disable(&pc->chip.pwms[i]); return pwmchip_remove(&pc->chip); } diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index cb845edfe2b4..d589331d1884 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -441,7 +441,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson, for (i = 0; i < meson->chip.npwm; i++) { struct meson_pwm_channel *channel = &channels[i]; - snprintf(name, sizeof(name), "%s#mux%u", np->full_name, i); + snprintf(name, sizeof(name), "%pOF#mux%u", np, i); init.name = name; init.ops = &clk_mux_ops; diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 5f55cfab9b1c..a7eaf962a95b 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -241,11 +241,11 @@ static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca) } #endif -static void pca9685_set_sleep_mode(struct pca9685 *pca, int sleep) +static void pca9685_set_sleep_mode(struct pca9685 *pca, bool enable) { regmap_update_bits(pca->regmap, PCA9685_MODE1, - MODE1_SLEEP, sleep ? MODE1_SLEEP : 0); - if (!sleep) { + MODE1_SLEEP, enable ? MODE1_SLEEP : 0); + if (!enable) { /* Wait 500us for the oscillator to be back up */ udelay(500); } @@ -272,13 +272,13 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * state is guaranteed active here. */ /* Put chip into sleep mode */ - pca9685_set_sleep_mode(pca, 1); + pca9685_set_sleep_mode(pca, true); /* Change the chip-wide output frequency */ regmap_write(pca->regmap, PCA9685_PRESCALE, prescale); /* Wake the chip up */ - pca9685_set_sleep_mode(pca, 0); + pca9685_set_sleep_mode(pca, false); pca->period_ns = period_ns; } else { @@ -534,7 +534,7 @@ static int pca9685_pwm_runtime_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct pca9685 *pca = i2c_get_clientdata(client); - pca9685_set_sleep_mode(pca, 1); + pca9685_set_sleep_mode(pca, true); return 0; } @@ -543,7 +543,7 @@ static int pca9685_pwm_runtime_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct pca9685 *pca = i2c_get_clientdata(client); - pca9685_set_sleep_mode(pca, 0); + pca9685_set_sleep_mode(pca, false); return 0; } #endif diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index 075c1a764ba2..29267d12fb4c 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -455,7 +455,6 @@ static const struct of_device_id tpu_of_table[] = { { .compatible = "renesas,tpu-r8a73a4", }, { .compatible = "renesas,tpu-r8a7740", }, { .compatible = "renesas,tpu-r8a7790", }, - { .compatible = "renesas,tpu-sh7372", }, { .compatible = "renesas,tpu", }, { }, }; diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 744d56197286..4d99d468df09 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -27,12 +27,15 @@ #define PWM_DUTY_NEGATIVE (0 << 3) #define PWM_INACTIVE_NEGATIVE (0 << 4) #define PWM_INACTIVE_POSITIVE (1 << 4) +#define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE) #define PWM_OUTPUT_LEFT (0 << 5) +#define PWM_LOCK_EN (1 << 6) #define PWM_LP_DISABLE (0 << 8) struct rockchip_pwm_chip { struct pwm_chip chip; struct clk *clk; + struct clk *pclk; const struct rockchip_pwm_data *data; void __iomem *base; }; @@ -48,13 +51,8 @@ struct rockchip_pwm_data { struct rockchip_pwm_regs regs; unsigned int prescaler; bool supports_polarity; - const struct pwm_ops *ops; - - void (*set_enable)(struct pwm_chip *chip, - struct pwm_device *pwm, bool enable, - enum pwm_polarity polarity); - void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state); + bool supports_lock; + u32 enable_conf; }; static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) @@ -62,90 +60,18 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) return container_of(c, struct rockchip_pwm_chip, chip); } -static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, - struct pwm_device *pwm, bool enable, - enum pwm_polarity polarity) -{ - struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; - u32 val; - - val = readl_relaxed(pc->base + pc->data->regs.ctrl); - - if (enable) - val |= enable_conf; - else - val &= ~enable_conf; - - writel_relaxed(val, pc->base + pc->data->regs.ctrl); -} - -static void rockchip_pwm_get_state_v1(struct pwm_chip *chip, - struct pwm_device *pwm, - struct pwm_state *state) -{ - struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; - u32 val; - - val = readl_relaxed(pc->base + pc->data->regs.ctrl); - if ((val & enable_conf) == enable_conf) - state->enabled = true; -} - -static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, - struct pwm_device *pwm, bool enable, - enum pwm_polarity polarity) -{ - struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | - PWM_CONTINUOUS; - u32 val; - - if (polarity == PWM_POLARITY_INVERSED) - enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; - else - enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; - - val = readl_relaxed(pc->base + pc->data->regs.ctrl); - - if (enable) - val |= enable_conf; - else - val &= ~enable_conf; - - writel_relaxed(val, pc->base + pc->data->regs.ctrl); -} - -static void rockchip_pwm_get_state_v2(struct pwm_chip *chip, - struct pwm_device *pwm, - struct pwm_state *state) -{ - struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | - PWM_CONTINUOUS; - u32 val; - - val = readl_relaxed(pc->base + pc->data->regs.ctrl); - if ((val & enable_conf) != enable_conf) - return; - - state->enabled = true; - - if (!(val & PWM_DUTY_POSITIVE)) - state->polarity = PWM_POLARITY_INVERSED; -} - static void rockchip_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u32 enable_conf = pc->data->enable_conf; unsigned long clk_rate; u64 tmp; + u32 val; int ret; - ret = clk_enable(pc->clk); + ret = clk_enable(pc->pclk); if (ret) return; @@ -157,19 +83,31 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, tmp = readl_relaxed(pc->base + pc->data->regs.duty); tmp *= pc->data->prescaler * NSEC_PER_SEC; - state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + + val = readl_relaxed(pc->base + pc->data->regs.ctrl); + if (pc->data->supports_polarity) + state->enabled = ((val & enable_conf) != enable_conf) ? + false : true; + else + state->enabled = ((val & enable_conf) == enable_conf) ? + true : false; - pc->data->get_state(chip, pwm, state); + if (pc->data->supports_polarity) { + if (!(val & PWM_DUTY_POSITIVE)) + state->polarity = PWM_POLARITY_INVERSED; + } - clk_disable(pc->clk); + clk_disable(pc->pclk); } -static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); unsigned long period, duty; u64 clk_rate, div; + u32 ctrl; clk_rate = clk_get_rate(pc->clk); @@ -178,26 +116,53 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * bits, every possible input period can be obtained using the * default prescaler value for all practical clock rate values. */ - div = clk_rate * period_ns; + div = clk_rate * state->period; period = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); - div = clk_rate * duty_ns; + div = clk_rate * state->duty_cycle; duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); + /* + * Lock the period and duty of previous configuration, then + * change the duty and period, that would not be effective. + */ + ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); + if (pc->data->supports_lock) { + ctrl |= PWM_LOCK_EN; + writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl); + } + writel(period, pc->base + pc->data->regs.period); writel(duty, pc->base + pc->data->regs.duty); - return 0; + if (pc->data->supports_polarity) { + ctrl &= ~PWM_POLARITY_MASK; + if (state->polarity == PWM_POLARITY_INVERSED) + ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; + else + ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; + } + + /* + * Unlock and set polarity at the same time, + * the configuration of duty, period and polarity + * would be effective together at next period. + */ + if (pc->data->supports_lock) + ctrl &= ~PWM_LOCK_EN; + + writel(ctrl, pc->base + pc->data->regs.ctrl); } static int rockchip_pwm_enable(struct pwm_chip *chip, - struct pwm_device *pwm, - bool enable, - enum pwm_polarity polarity) + struct pwm_device *pwm, + bool enable) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u32 enable_conf = pc->data->enable_conf; int ret; + u32 val; if (enable) { ret = clk_enable(pc->clk); @@ -205,7 +170,14 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, return ret; } - pc->data->set_enable(chip, pwm, enable, polarity); + val = readl_relaxed(pc->base + pc->data->regs.ctrl); + + if (enable) + val |= enable_conf; + else + val &= ~enable_conf; + + writel_relaxed(val, pc->base + pc->data->regs.ctrl); if (!enable) clk_disable(pc->clk); @@ -219,33 +191,26 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); struct pwm_state curstate; bool enabled; - int ret; + int ret = 0; - pwm_get_state(pwm, &curstate); - enabled = curstate.enabled; - - ret = clk_enable(pc->clk); + ret = clk_enable(pc->pclk); if (ret) return ret; - if (state->polarity != curstate.polarity && enabled) { - ret = rockchip_pwm_enable(chip, pwm, false, state->polarity); + pwm_get_state(pwm, &curstate); + enabled = curstate.enabled; + + if (state->polarity != curstate.polarity && enabled && + !pc->data->supports_lock) { + ret = rockchip_pwm_enable(chip, pwm, false); if (ret) goto out; enabled = false; } - ret = rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); - if (ret) { - if (enabled != curstate.enabled) - rockchip_pwm_enable(chip, pwm, !enabled, - state->polarity); - goto out; - } - + rockchip_pwm_config(chip, pwm, state); if (state->enabled != enabled) { - ret = rockchip_pwm_enable(chip, pwm, state->enabled, - state->polarity); + ret = rockchip_pwm_enable(chip, pwm, state->enabled); if (ret) goto out; } @@ -257,18 +222,12 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, rockchip_pwm_get_state(chip, pwm, state); out: - clk_disable(pc->clk); + clk_disable(pc->pclk); return ret; } -static const struct pwm_ops rockchip_pwm_ops_v1 = { - .get_state = rockchip_pwm_get_state, - .apply = rockchip_pwm_apply, - .owner = THIS_MODULE, -}; - -static const struct pwm_ops rockchip_pwm_ops_v2 = { +static const struct pwm_ops rockchip_pwm_ops = { .get_state = rockchip_pwm_get_state, .apply = rockchip_pwm_apply, .owner = THIS_MODULE, @@ -282,9 +241,9 @@ static const struct rockchip_pwm_data pwm_data_v1 = { .ctrl = 0x0c, }, .prescaler = 2, - .ops = &rockchip_pwm_ops_v1, - .set_enable = rockchip_pwm_set_enable_v1, - .get_state = rockchip_pwm_get_state_v1, + .supports_polarity = false, + .supports_lock = false, + .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN, }; static const struct rockchip_pwm_data pwm_data_v2 = { @@ -296,9 +255,9 @@ static const struct rockchip_pwm_data pwm_data_v2 = { }, .prescaler = 1, .supports_polarity = true, - .ops = &rockchip_pwm_ops_v2, - .set_enable = rockchip_pwm_set_enable_v2, - .get_state = rockchip_pwm_get_state_v2, + .supports_lock = false, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, }; static const struct rockchip_pwm_data pwm_data_vop = { @@ -310,15 +269,30 @@ static const struct rockchip_pwm_data pwm_data_vop = { }, .prescaler = 1, .supports_polarity = true, - .ops = &rockchip_pwm_ops_v2, - .set_enable = rockchip_pwm_set_enable_v2, - .get_state = rockchip_pwm_get_state_v2, + .supports_lock = false, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, +}; + +static const struct rockchip_pwm_data pwm_data_v3 = { + .regs = { + .duty = 0x08, + .period = 0x04, + .cntr = 0x00, + .ctrl = 0x0c, + }, + .prescaler = 1, + .supports_polarity = true, + .supports_lock = true, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, }; static const struct of_device_id rockchip_pwm_dt_ids[] = { { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1}, { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2}, { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop}, + { .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); @@ -328,7 +302,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) const struct of_device_id *id; struct rockchip_pwm_chip *pc; struct resource *r; - int ret; + int ret, count; id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); if (!id) @@ -343,19 +317,49 @@ static int rockchip_pwm_probe(struct platform_device *pdev) if (IS_ERR(pc->base)) return PTR_ERR(pc->base); - pc->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pc->clk)) - return PTR_ERR(pc->clk); + pc->clk = devm_clk_get(&pdev->dev, "pwm"); + if (IS_ERR(pc->clk)) { + pc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pc->clk)) { + ret = PTR_ERR(pc->clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Can't get bus clk: %d\n", + ret); + return ret; + } + } + + count = of_count_phandle_with_args(pdev->dev.of_node, + "clocks", "#clock-cells"); + if (count == 2) + pc->pclk = devm_clk_get(&pdev->dev, "pclk"); + else + pc->pclk = pc->clk; + + if (IS_ERR(pc->pclk)) { + ret = PTR_ERR(pc->pclk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Can't get APB clk: %d\n", ret); + return ret; + } ret = clk_prepare_enable(pc->clk); - if (ret) + if (ret) { + dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret); return ret; + } + + ret = clk_prepare(pc->pclk); + if (ret) { + dev_err(&pdev->dev, "Can't prepare APB clk: %d\n", ret); + goto err_clk; + } platform_set_drvdata(pdev, pc); pc->data = id->data; pc->chip.dev = &pdev->dev; - pc->chip.ops = pc->data->ops; + pc->chip.ops = &rockchip_pwm_ops; pc->chip.base = -1; pc->chip.npwm = 1; @@ -368,12 +372,20 @@ static int rockchip_pwm_probe(struct platform_device *pdev) if (ret < 0) { clk_unprepare(pc->clk); dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + goto err_pclk; } /* Keep the PWM clk enabled if the PWM appears to be up and running. */ if (!pwm_is_enabled(pc->chip.pwms)) clk_disable(pc->clk); + return 0; + +err_pclk: + clk_unprepare(pc->pclk); +err_clk: + clk_disable_unprepare(pc->clk); + return ret; } @@ -395,6 +407,7 @@ static int rockchip_pwm_remove(struct platform_device *pdev) if (pwm_is_enabled(pc->chip.pwms)) clk_disable(pc->clk); + clk_unprepare(pc->pclk); clk_unprepare(pc->clk); return pwmchip_remove(&pc->chip); diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index f113cda47032..062f2cfc45ec 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -3,6 +3,7 @@ * Copyright (c) 2008 Simtec Electronics * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org> * Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com> + * Copyright (c) 2017 Samsung Electronics Co., Ltd. * * PWM driver for Samsung SoCs * @@ -74,6 +75,7 @@ struct samsung_pwm_channel { * @chip: generic PWM chip * @variant: local copy of hardware variant data * @inverter_mask: inverter status for all channels - one bit per channel + * @disabled_mask: disabled status for all channels - one bit per channel * @base: base address of mapped PWM registers * @base_clk: base clock used to drive the timers * @tclk0: external clock 0 (can be ERR_PTR if not present) @@ -83,6 +85,7 @@ struct samsung_pwm_chip { struct pwm_chip chip; struct samsung_pwm_variant variant; u8 inverter_mask; + u8 disabled_mask; void __iomem *base; struct clk *base_clk; @@ -257,6 +260,8 @@ static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) tcon |= TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan); writel(tcon, our_chip->base + REG_TCON); + our_chip->disabled_mask &= ~BIT(pwm->hwpwm); + spin_unlock_irqrestore(&samsung_pwm_lock, flags); return 0; @@ -275,6 +280,8 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) tcon &= ~TCON_AUTORELOAD(tcon_chan); writel(tcon, our_chip->base + REG_TCON); + our_chip->disabled_mask |= BIT(pwm->hwpwm); + spin_unlock_irqrestore(&samsung_pwm_lock, flags); } @@ -297,8 +304,8 @@ static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, spin_unlock_irqrestore(&samsung_pwm_lock, flags); } -static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns, bool force_period) { struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); @@ -312,9 +319,6 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, if (period_ns > NSEC_PER_SEC) return -ERANGE; - if (period_ns == chan->period_ns && duty_ns == chan->duty_ns) - return 0; - tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm)); oldtcmp = readl(our_chip->base + REG_TCMPB(pwm->hwpwm)); @@ -322,7 +326,7 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, ++tcnt; /* Check to see if we are changing the clock rate of the PWM. */ - if (chan->period_ns != period_ns) { + if (chan->period_ns != period_ns || force_period) { unsigned long tin_rate; u32 period; @@ -381,6 +385,12 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + return __pwm_samsung_config(chip, pwm, duty_ns, period_ns, false); +} + static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip, unsigned int channel, bool invert) { @@ -592,51 +602,41 @@ static int pwm_samsung_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -static int pwm_samsung_suspend(struct device *dev) +static int pwm_samsung_resume(struct device *dev) { - struct samsung_pwm_chip *chip = dev_get_drvdata(dev); + struct samsung_pwm_chip *our_chip = dev_get_drvdata(dev); + struct pwm_chip *chip = &our_chip->chip; unsigned int i; - /* - * No one preserves these values during suspend so reset them. - * Otherwise driver leaves PWM unconfigured if same values are - * passed to pwm_config() next time. - */ - for (i = 0; i < SAMSUNG_PWM_NUM; ++i) { - struct pwm_device *pwm = &chip->chip.pwms[i]; + for (i = 0; i < SAMSUNG_PWM_NUM; i++) { + struct pwm_device *pwm = &chip->pwms[i]; struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); if (!chan) continue; - chan->period_ns = 0; - chan->duty_ns = 0; - } - - return 0; -} + if (our_chip->variant.output_mask & BIT(i)) + pwm_samsung_set_invert(our_chip, i, + our_chip->inverter_mask & BIT(i)); -static int pwm_samsung_resume(struct device *dev) -{ - struct samsung_pwm_chip *chip = dev_get_drvdata(dev); - unsigned int chan; + if (chan->period_ns) { + __pwm_samsung_config(chip, pwm, chan->duty_ns, + chan->period_ns, true); + /* needed to make PWM disable work on Odroid-XU3 */ + pwm_samsung_manual_update(our_chip, pwm); + } - /* - * Inverter setting must be preserved across suspend/resume - * as nobody really seems to configure it more than once. - */ - for (chan = 0; chan < SAMSUNG_PWM_NUM; ++chan) { - if (chip->variant.output_mask & BIT(chan)) - pwm_samsung_set_invert(chip, chan, - chip->inverter_mask & BIT(chan)); + if (our_chip->disabled_mask & BIT(i)) + pwm_samsung_disable(chip, pwm); + else + pwm_samsung_enable(chip, pwm); } return 0; } #endif -static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, pwm_samsung_suspend, - pwm_samsung_resume); +static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, NULL, pwm_samsung_resume); static struct platform_driver pwm_samsung_driver = { .driver = { diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index e9b33f09ff09..f8ebbece57b7 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -218,7 +218,7 @@ static int tegra_pwm_probe(struct platform_device *pdev) */ pwm->clk_rate = clk_get_rate(pwm->clk); - pwm->rst = devm_reset_control_get(&pdev->dev, "pwm"); + pwm->rst = devm_reset_control_get_exclusive(&pdev->dev, "pwm"); if (IS_ERR(pwm->rst)) { ret = PTR_ERR(pwm->rst); dev_err(&pdev->dev, "Reset control is not found: %d\n", ret); diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 6ec342dd3eea..34b228626bd5 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -39,15 +39,15 @@ #define ECCTL2_TSCTR_FREERUN BIT(4) struct ecap_context { - u32 cap3; - u32 cap4; - u16 ecctl2; + u32 cap3; + u32 cap4; + u16 ecctl2; }; struct ecap_pwm_chip { - struct pwm_chip chip; - unsigned int clk_rate; - void __iomem *mmio_base; + struct pwm_chip chip; + unsigned int clk_rate; + void __iomem *mmio_base; struct ecap_context ctx; }; @@ -64,9 +64,9 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); + u32 period_cycles, duty_cycles; unsigned long long c; - unsigned long period_cycles, duty_cycles; - unsigned int reg_val; + u16 value; if (period_ns > NSEC_PER_SEC) return -ERANGE; @@ -74,7 +74,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, c = pc->clk_rate; c = c * period_ns; do_div(c, NSEC_PER_SEC); - period_cycles = (unsigned long)c; + period_cycles = (u32)c; if (period_cycles < 1) { period_cycles = 1; @@ -83,17 +83,17 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, c = pc->clk_rate; c = c * duty_ns; do_div(c, NSEC_PER_SEC); - duty_cycles = (unsigned long)c; + duty_cycles = (u32)c; } pm_runtime_get_sync(pc->chip.dev); - reg_val = readw(pc->mmio_base + ECCTL2); + value = readw(pc->mmio_base + ECCTL2); /* Configure APWM mode & disable sync option */ - reg_val |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA; + value |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA; - writew(reg_val, pc->mmio_base + ECCTL2); + writew(value, pc->mmio_base + ECCTL2); if (!pwm_is_enabled(pwm)) { /* Update active registers if not running */ @@ -110,40 +110,45 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } if (!pwm_is_enabled(pwm)) { - reg_val = readw(pc->mmio_base + ECCTL2); + value = readw(pc->mmio_base + ECCTL2); /* Disable APWM mode to put APWM output Low */ - reg_val &= ~ECCTL2_APWM_MODE; - writew(reg_val, pc->mmio_base + ECCTL2); + value &= ~ECCTL2_APWM_MODE; + writew(value, pc->mmio_base + ECCTL2); } pm_runtime_put_sync(pc->chip.dev); + return 0; } static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, - enum pwm_polarity polarity) + enum pwm_polarity polarity) { struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); - unsigned short reg_val; + u16 value; pm_runtime_get_sync(pc->chip.dev); - reg_val = readw(pc->mmio_base + ECCTL2); + + value = readw(pc->mmio_base + ECCTL2); + if (polarity == PWM_POLARITY_INVERSED) /* Duty cycle defines LOW period of PWM */ - reg_val |= ECCTL2_APWM_POL_LOW; + value |= ECCTL2_APWM_POL_LOW; else /* Duty cycle defines HIGH period of PWM */ - reg_val &= ~ECCTL2_APWM_POL_LOW; + value &= ~ECCTL2_APWM_POL_LOW; + + writew(value, pc->mmio_base + ECCTL2); - writew(reg_val, pc->mmio_base + ECCTL2); pm_runtime_put_sync(pc->chip.dev); + return 0; } static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); - unsigned int reg_val; + u16 value; /* Leave clock enabled on enabling PWM */ pm_runtime_get_sync(pc->chip.dev); @@ -152,24 +157,25 @@ static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) * Enable 'Free run Time stamp counter mode' to start counter * and 'APWM mode' to enable APWM output */ - reg_val = readw(pc->mmio_base + ECCTL2); - reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE; - writew(reg_val, pc->mmio_base + ECCTL2); + value = readw(pc->mmio_base + ECCTL2); + value |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE; + writew(value, pc->mmio_base + ECCTL2); + return 0; } static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); - unsigned int reg_val; + u16 value; /* * Disable 'Free run Time stamp counter mode' to stop counter * and 'APWM mode' to put APWM output to low */ - reg_val = readw(pc->mmio_base + ECCTL2); - reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE); - writew(reg_val, pc->mmio_base + ECCTL2); + value = readw(pc->mmio_base + ECCTL2); + value &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE); + writew(value, pc->mmio_base + ECCTL2); /* Disable clock on PWM disable */ pm_runtime_put_sync(pc->chip.dev); @@ -184,12 +190,12 @@ static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) } static const struct pwm_ops ecap_pwm_ops = { - .free = ecap_pwm_free, - .config = ecap_pwm_config, - .set_polarity = ecap_pwm_set_polarity, - .enable = ecap_pwm_enable, - .disable = ecap_pwm_disable, - .owner = THIS_MODULE, + .free = ecap_pwm_free, + .config = ecap_pwm_config, + .set_polarity = ecap_pwm_set_polarity, + .enable = ecap_pwm_enable, + .disable = ecap_pwm_disable, + .owner = THIS_MODULE, }; static const struct of_device_id ecap_of_match[] = { @@ -202,10 +208,10 @@ MODULE_DEVICE_TABLE(of, ecap_of_match); static int ecap_pwm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - int ret; + struct ecap_pwm_chip *pc; struct resource *r; struct clk *clk; - struct ecap_pwm_chip *pc; + int ret; pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) @@ -248,9 +254,9 @@ static int ecap_pwm_probe(struct platform_device *pdev) return ret; } + platform_set_drvdata(pdev, pc); pm_runtime_enable(&pdev->dev); - platform_set_drvdata(pdev, pc); return 0; } @@ -259,6 +265,7 @@ static int ecap_pwm_remove(struct platform_device *pdev) struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); + return pwmchip_remove(&pc->chip); } @@ -311,14 +318,13 @@ static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume); static struct platform_driver ecap_pwm_driver = { .driver = { - .name = "ecap", + .name = "ecap", .of_match_table = ecap_of_match, - .pm = &ecap_pwm_pm_ops, + .pm = &ecap_pwm_pm_ops, }, .probe = ecap_pwm_probe, .remove = ecap_pwm_remove, }; - module_platform_driver(ecap_pwm_driver); MODULE_DESCRIPTION("ECAP PWM driver"); diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index b5c6b0636893..4c22cb395040 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -122,12 +122,12 @@ struct ehrpwm_context { }; struct ehrpwm_pwm_chip { - struct pwm_chip chip; - unsigned int clk_rate; - void __iomem *mmio_base; + struct pwm_chip chip; + unsigned long clk_rate; + void __iomem *mmio_base; unsigned long period_cycles[NUM_PWM_CHANNEL]; enum pwm_polarity polarity[NUM_PWM_CHANNEL]; - struct clk *tbclk; + struct clk *tbclk; struct ehrpwm_context ctx; }; @@ -136,25 +136,26 @@ static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) return container_of(chip, struct ehrpwm_pwm_chip, chip); } -static inline u16 ehrpwm_read(void __iomem *base, int offset) +static inline u16 ehrpwm_read(void __iomem *base, unsigned int offset) { return readw(base + offset); } -static inline void ehrpwm_write(void __iomem *base, int offset, unsigned int val) +static inline void ehrpwm_write(void __iomem *base, unsigned int offset, + u16 value) { - writew(val & 0xFFFF, base + offset); + writew(value, base + offset); } -static void ehrpwm_modify(void __iomem *base, int offset, - unsigned short mask, unsigned short val) +static void ehrpwm_modify(void __iomem *base, unsigned int offset, u16 mask, + u16 value) { - unsigned short regval; + unsigned short val; - regval = readw(base + offset); - regval &= ~mask; - regval |= val & mask; - writew(regval, base + offset); + val = readw(base + offset); + val &= ~mask; + val |= value & mask; + writew(val, base + offset); } /** @@ -163,14 +164,13 @@ static void ehrpwm_modify(void __iomem *base, int offset, * @prescale_div: prescaler value set * @tb_clk_div: Time Base Control prescaler bits */ -static int set_prescale_div(unsigned long rqst_prescaler, - unsigned short *prescale_div, unsigned short *tb_clk_div) +static int set_prescale_div(unsigned long rqst_prescaler, u16 *prescale_div, + u16 *tb_clk_div) { unsigned int clkdiv, hspclkdiv; for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) { for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) { - /* * calculations for prescaler value : * prescale_div = HSPCLKDIVIDER * CLKDIVIDER. @@ -191,13 +191,14 @@ static int set_prescale_div(unsigned long rqst_prescaler, } } } + return 1; } static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) { - int aqctl_reg; - unsigned short aqctl_val, aqctl_mask; + u16 aqctl_val, aqctl_mask; + unsigned int aqctl_reg; /* * Configure PWM output to HIGH/LOW level on counter @@ -232,13 +233,13 @@ static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE */ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + int duty_ns, int period_ns) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); + u32 period_cycles, duty_cycles; + u16 ps_divval, tb_divval; + unsigned int i, cmp_reg; unsigned long long c; - unsigned long period_cycles, duty_cycles; - unsigned short ps_divval, tb_divval; - int i, cmp_reg; if (period_ns > NSEC_PER_SEC) return -ERANGE; @@ -272,8 +273,9 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (i == pwm->hwpwm) continue; - dev_err(chip->dev, "Period value conflicts with channel %d\n", - i); + dev_err(chip->dev, + "period value conflicts with channel %u\n", + i); return -EINVAL; } } @@ -282,7 +284,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, /* Configure clock prescaler to support Low frequency PWM wave */ if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval, - &tb_divval)) { + &tb_divval)) { dev_err(chip->dev, "Unsupported values\n"); return -EINVAL; } @@ -303,7 +305,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, /* Configure ehrpwm counter for up-count mode */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, - TBCTL_CTRMODE_UP); + TBCTL_CTRMODE_UP); if (pwm->hwpwm == 1) /* Channel 1 configured with compare B register */ @@ -315,23 +317,26 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); pm_runtime_put_sync(chip->dev); + return 0; } static int ehrpwm_pwm_set_polarity(struct pwm_chip *chip, - struct pwm_device *pwm, enum pwm_polarity polarity) + struct pwm_device *pwm, + enum pwm_polarity polarity) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); /* Configuration of polarity in hardware delayed, do at enable */ pc->polarity[pwm->hwpwm] = polarity; + return 0; } static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); - unsigned short aqcsfrc_val, aqcsfrc_mask; + u16 aqcsfrc_val, aqcsfrc_mask; int ret; /* Leave clock enabled on enabling PWM */ @@ -348,7 +353,7 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) /* Changes to shadow mode */ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, - AQSFRC_RLDCSF_ZRO); + AQSFRC_RLDCSF_ZRO); ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); @@ -358,20 +363,21 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) /* Enable TBCLK before enabling PWM device */ ret = clk_enable(pc->tbclk); if (ret) { - dev_err(chip->dev, "Failed to enable TBCLK for %s\n", - dev_name(pc->chip.dev)); + dev_err(chip->dev, "Failed to enable TBCLK for %s: %d\n", + dev_name(pc->chip.dev), ret); return ret; } /* Enable time counter for free_run */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); + return 0; } static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); - unsigned short aqcsfrc_val, aqcsfrc_mask; + u16 aqcsfrc_val, aqcsfrc_mask; /* Action Qualifier puts PWM output low forcefully */ if (pwm->hwpwm) { @@ -387,7 +393,7 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) * Action Qualifier control on PWM output from next TBCLK */ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, - AQSFRC_RLDCSF_IMDT); + AQSFRC_RLDCSF_IMDT); ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); @@ -415,17 +421,17 @@ static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) } static const struct pwm_ops ehrpwm_pwm_ops = { - .free = ehrpwm_pwm_free, - .config = ehrpwm_pwm_config, - .set_polarity = ehrpwm_pwm_set_polarity, - .enable = ehrpwm_pwm_enable, - .disable = ehrpwm_pwm_disable, - .owner = THIS_MODULE, + .free = ehrpwm_pwm_free, + .config = ehrpwm_pwm_config, + .set_polarity = ehrpwm_pwm_set_polarity, + .enable = ehrpwm_pwm_enable, + .disable = ehrpwm_pwm_disable, + .owner = THIS_MODULE, }; static const struct of_device_id ehrpwm_of_match[] = { - { .compatible = "ti,am3352-ehrpwm" }, - { .compatible = "ti,am33xx-ehrpwm" }, + { .compatible = "ti,am3352-ehrpwm" }, + { .compatible = "ti,am33xx-ehrpwm" }, {}, }; MODULE_DEVICE_TABLE(of, ehrpwm_of_match); @@ -433,10 +439,10 @@ MODULE_DEVICE_TABLE(of, ehrpwm_of_match); static int ehrpwm_pwm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - int ret; + struct ehrpwm_pwm_chip *pc; struct resource *r; struct clk *clk; - struct ehrpwm_pwm_chip *pc; + int ret; pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) @@ -489,13 +495,18 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) ret = pwmchip_add(&pc->chip); if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - return ret; + goto err_clk_unprepare; } + platform_set_drvdata(pdev, pc); pm_runtime_enable(&pdev->dev); - platform_set_drvdata(pdev, pc); return 0; + +err_clk_unprepare: + clk_unprepare(pc->tbclk); + + return ret; } static int ehrpwm_pwm_remove(struct platform_device *pdev) @@ -504,8 +515,8 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) clk_unprepare(pc->tbclk); - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + return pwmchip_remove(&pc->chip); } @@ -513,6 +524,7 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) { pm_runtime_get_sync(pc->chip.dev); + pc->ctx.tbctl = ehrpwm_read(pc->mmio_base, TBCTL); pc->ctx.tbprd = ehrpwm_read(pc->mmio_base, TBPRD); pc->ctx.cmpa = ehrpwm_read(pc->mmio_base, CMPA); @@ -521,6 +533,7 @@ static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) pc->ctx.aqctlb = ehrpwm_read(pc->mmio_base, AQCTLB); pc->ctx.aqsfrc = ehrpwm_read(pc->mmio_base, AQSFRC); pc->ctx.aqcsfrc = ehrpwm_read(pc->mmio_base, AQCSFRC); + pm_runtime_put_sync(pc->chip.dev); } @@ -539,9 +552,10 @@ static void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc) static int ehrpwm_pwm_suspend(struct device *dev) { struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); - int i; + unsigned int i; ehrpwm_pwm_save_context(pc); + for (i = 0; i < pc->chip.npwm; i++) { struct pwm_device *pwm = &pc->chip.pwms[i]; @@ -551,13 +565,14 @@ static int ehrpwm_pwm_suspend(struct device *dev) /* Disable explicitly if PWM is running */ pm_runtime_put_sync(dev); } + return 0; } static int ehrpwm_pwm_resume(struct device *dev) { struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); - int i; + unsigned int i; for (i = 0; i < pc->chip.npwm; i++) { struct pwm_device *pwm = &pc->chip.pwms[i]; @@ -568,24 +583,25 @@ static int ehrpwm_pwm_resume(struct device *dev) /* Enable explicitly if PWM was running */ pm_runtime_get_sync(dev); } + ehrpwm_pwm_restore_context(pc); + return 0; } #endif static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend, - ehrpwm_pwm_resume); + ehrpwm_pwm_resume); static struct platform_driver ehrpwm_pwm_driver = { .driver = { - .name = "ehrpwm", + .name = "ehrpwm", .of_match_table = ehrpwm_of_match, - .pm = &ehrpwm_pwm_pm_ops, + .pm = &ehrpwm_pwm_pm_ops, }, .probe = ehrpwm_pwm_probe, .remove = ehrpwm_pwm_remove, }; - module_platform_driver(ehrpwm_pwm_driver); MODULE_DESCRIPTION("EHRPWM PWM driver"); diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index 8141a4984126..3a78dd09ac81 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -241,6 +241,7 @@ static int vt8500_pwm_probe(struct platform_device *pdev) ret = pwmchip_add(&chip->chip); if (ret < 0) { dev_err(&pdev->dev, "failed to add PWM chip\n"); + clk_unprepare(chip->clk); return ret; } diff --git a/drivers/pwm/pwm-zx.c b/drivers/pwm/pwm-zx.c new file mode 100644 index 000000000000..5d27c16edfb1 --- /dev/null +++ b/drivers/pwm/pwm-zx.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2017 Sanechips Technology Co., Ltd. + * Copyright 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> + +#define ZX_PWM_MODE 0x0 +#define ZX_PWM_CLKDIV_SHIFT 2 +#define ZX_PWM_CLKDIV_MASK GENMASK(11, 2) +#define ZX_PWM_CLKDIV(x) (((x) << ZX_PWM_CLKDIV_SHIFT) & \ + ZX_PWM_CLKDIV_MASK) +#define ZX_PWM_POLAR BIT(1) +#define ZX_PWM_EN BIT(0) +#define ZX_PWM_PERIOD 0x4 +#define ZX_PWM_DUTY 0x8 + +#define ZX_PWM_CLKDIV_MAX 1023 +#define ZX_PWM_PERIOD_MAX 65535 + +struct zx_pwm_chip { + struct pwm_chip chip; + struct clk *pclk; + struct clk *wclk; + void __iomem *base; +}; + +static inline struct zx_pwm_chip *to_zx_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct zx_pwm_chip, chip); +} + +static inline u32 zx_pwm_readl(struct zx_pwm_chip *zpc, unsigned int hwpwm, + unsigned int offset) +{ + return readl(zpc->base + (hwpwm + 1) * 0x10 + offset); +} + +static inline void zx_pwm_writel(struct zx_pwm_chip *zpc, unsigned int hwpwm, + unsigned int offset, u32 value) +{ + writel(value, zpc->base + (hwpwm + 1) * 0x10 + offset); +} + +static void zx_pwm_set_mask(struct zx_pwm_chip *zpc, unsigned int hwpwm, + unsigned int offset, u32 mask, u32 value) +{ + u32 data; + + data = zx_pwm_readl(zpc, hwpwm, offset); + data &= ~mask; + data |= value & mask; + zx_pwm_writel(zpc, hwpwm, offset, data); +} + +static void zx_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip); + unsigned long rate; + unsigned int div; + u32 value; + u64 tmp; + + value = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_MODE); + + if (value & ZX_PWM_POLAR) + state->polarity = PWM_POLARITY_NORMAL; + else + state->polarity = PWM_POLARITY_INVERSED; + + if (value & ZX_PWM_EN) + state->enabled = true; + else + state->enabled = false; + + div = (value & ZX_PWM_CLKDIV_MASK) >> ZX_PWM_CLKDIV_SHIFT; + rate = clk_get_rate(zpc->wclk); + + tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_PERIOD); + tmp *= div * NSEC_PER_SEC; + state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate); + + tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_DUTY); + tmp *= div * NSEC_PER_SEC; + state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate); +} + +static int zx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + unsigned int duty_ns, unsigned int period_ns) +{ + struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip); + unsigned int period_cycles, duty_cycles; + unsigned long long c; + unsigned int div = 1; + unsigned long rate; + + /* Find out the best divider */ + rate = clk_get_rate(zpc->wclk); + + while (1) { + c = rate / div; + c = c * period_ns; + do_div(c, NSEC_PER_SEC); + + if (c < ZX_PWM_PERIOD_MAX) + break; + + div++; + + if (div > ZX_PWM_CLKDIV_MAX) + return -ERANGE; + } + + /* Calculate duty cycles */ + period_cycles = c; + c *= duty_ns; + do_div(c, period_ns); + duty_cycles = c; + + /* + * If the PWM is being enabled, we have to temporarily disable it + * before configuring the registers. + */ + if (pwm_is_enabled(pwm)) + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_EN, 0); + + /* Set up registers */ + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_CLKDIV_MASK, + ZX_PWM_CLKDIV(div)); + zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_PERIOD, period_cycles); + zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_DUTY, duty_cycles); + + /* Re-enable the PWM if needed */ + if (pwm_is_enabled(pwm)) + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, + ZX_PWM_EN, ZX_PWM_EN); + + return 0; +} + +static int zx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip); + struct pwm_state cstate; + int ret; + + pwm_get_state(pwm, &cstate); + + if (state->polarity != cstate.polarity) + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_POLAR, + (state->polarity == PWM_POLARITY_INVERSED) ? + 0 : ZX_PWM_POLAR); + + if (state->period != cstate.period || + state->duty_cycle != cstate.duty_cycle) { + ret = zx_pwm_config(chip, pwm, state->duty_cycle, + state->period); + if (ret) + return ret; + } + + if (state->enabled != cstate.enabled) { + if (state->enabled) { + ret = clk_prepare_enable(zpc->wclk); + if (ret) + return ret; + + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, + ZX_PWM_EN, ZX_PWM_EN); + } else { + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, + ZX_PWM_EN, 0); + clk_disable_unprepare(zpc->wclk); + } + } + + return 0; +} + +static const struct pwm_ops zx_pwm_ops = { + .apply = zx_pwm_apply, + .get_state = zx_pwm_get_state, + .owner = THIS_MODULE, +}; + +static int zx_pwm_probe(struct platform_device *pdev) +{ + struct zx_pwm_chip *zpc; + struct resource *res; + unsigned int i; + int ret; + + zpc = devm_kzalloc(&pdev->dev, sizeof(*zpc), GFP_KERNEL); + if (!zpc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + zpc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(zpc->base)) + return PTR_ERR(zpc->base); + + zpc->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(zpc->pclk)) + return PTR_ERR(zpc->pclk); + + zpc->wclk = devm_clk_get(&pdev->dev, "wclk"); + if (IS_ERR(zpc->wclk)) + return PTR_ERR(zpc->wclk); + + ret = clk_prepare_enable(zpc->pclk); + if (ret) + return ret; + + zpc->chip.dev = &pdev->dev; + zpc->chip.ops = &zx_pwm_ops; + zpc->chip.base = -1; + zpc->chip.npwm = 4; + zpc->chip.of_xlate = of_pwm_xlate_with_flags; + zpc->chip.of_pwm_n_cells = 3; + + /* + * PWM devices may be enabled by firmware, and let's disable all of + * them initially to save power. + */ + for (i = 0; i < zpc->chip.npwm; i++) + zx_pwm_set_mask(zpc, i, ZX_PWM_MODE, ZX_PWM_EN, 0); + + ret = pwmchip_add(&zpc->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, zpc); + + return 0; +} + +static int zx_pwm_remove(struct platform_device *pdev) +{ + struct zx_pwm_chip *zpc = platform_get_drvdata(pdev); + int ret; + + ret = pwmchip_remove(&zpc->chip); + clk_disable_unprepare(zpc->pclk); + + return ret; +} + +static const struct of_device_id zx_pwm_dt_ids[] = { + { .compatible = "zte,zx296718-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, zx_pwm_dt_ids); + +static struct platform_driver zx_pwm_driver = { + .driver = { + .name = "zx-pwm", + .of_match_table = zx_pwm_dt_ids, + }, + .probe = zx_pwm_probe, + .remove = zx_pwm_remove, +}; +module_platform_driver(zx_pwm_driver); + +MODULE_ALIAS("platform:zx-pwm"); +MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); +MODULE_DESCRIPTION("ZTE ZX PWM Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 8891a8e50f12..df63e44526ac 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -12,6 +12,15 @@ config REMOTEPROC if REMOTEPROC +config IMX_REMOTEPROC + tristate "IMX6/7 remoteproc support" + depends on SOC_IMX6SX || SOC_IMX7D + help + Say y here to support iMX's remote processors (Cortex M4 + on iMX7D) via the remote processor framework. + + It's safe to say N here. + config OMAP_REMOTEPROC tristate "OMAP remoteproc support" depends on HAS_DMA @@ -83,6 +92,7 @@ config QCOM_ADSP_PIL depends on OF && ARCH_QCOM depends on QCOM_SMEM depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) + depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n select MFD_SYSCON select QCOM_MDT_LOADER select QCOM_RPROC_COMMON diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index f1ce5fc8a2f3..1a0b3dd44b8c 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -8,6 +8,7 @@ remoteproc-y += remoteproc_debugfs.o remoteproc-y += remoteproc_sysfs.o remoteproc-y += remoteproc_virtio.o remoteproc-y += remoteproc_elf_loader.o +obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index 99539cec1329..bf3b9034c319 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c @@ -16,6 +16,7 @@ #include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/remoteproc.h> @@ -38,9 +39,27 @@ MODULE_PARM_DESC(da8xx_fw_name, #define SYSCFG_CHIPSIG3 BIT(3) #define SYSCFG_CHIPSIG4 BIT(4) +#define DA8XX_RPROC_LOCAL_ADDRESS_MASK (SZ_16M - 1) + +/** + * struct da8xx_rproc_mem - internal memory structure + * @cpu_addr: MPU virtual address of the memory region + * @bus_addr: Bus address used to access the memory region + * @dev_addr: Device address of the memory region from DSP view + * @size: Size of the memory region + */ +struct da8xx_rproc_mem { + void __iomem *cpu_addr; + phys_addr_t bus_addr; + u32 dev_addr; + size_t size; +}; + /** * struct da8xx_rproc - da8xx remote processor instance state * @rproc: rproc handle + * @mem: internal memory regions data + * @num_mems: number of internal memory regions * @dsp_clk: placeholder for platform's DSP clk * @ack_fxn: chip-specific ack function for ack'ing irq * @irq_data: ack_fxn function parameter @@ -50,6 +69,8 @@ MODULE_PARM_DESC(da8xx_fw_name, */ struct da8xx_rproc { struct rproc *rproc; + struct da8xx_rproc_mem *mem; + int num_mems; struct clk *dsp_clk; void (*ack_fxn)(struct irq_data *data); struct irq_data *irq_data; @@ -158,6 +179,44 @@ static const struct rproc_ops da8xx_rproc_ops = { .kick = da8xx_rproc_kick, }; +static int da8xx_rproc_get_internal_memories(struct platform_device *pdev, + struct da8xx_rproc *drproc) +{ + static const char * const mem_names[] = {"l2sram", "l1pram", "l1dram"}; + int num_mems = ARRAY_SIZE(mem_names); + struct device *dev = &pdev->dev; + struct resource *res; + int i; + + drproc->mem = devm_kcalloc(dev, num_mems, sizeof(*drproc->mem), + GFP_KERNEL); + if (!drproc->mem) + return -ENOMEM; + + for (i = 0; i < num_mems; i++) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + mem_names[i]); + drproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res); + if (IS_ERR(drproc->mem[i].cpu_addr)) { + dev_err(dev, "failed to parse and map %s memory\n", + mem_names[i]); + return PTR_ERR(drproc->mem[i].cpu_addr); + } + drproc->mem[i].bus_addr = res->start; + drproc->mem[i].dev_addr = + res->start & DA8XX_RPROC_LOCAL_ADDRESS_MASK; + drproc->mem[i].size = resource_size(res); + + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%x va %p da 0x%x\n", + mem_names[i], &drproc->mem[i].bus_addr, + drproc->mem[i].size, drproc->mem[i].cpu_addr, + drproc->mem[i].dev_addr); + } + drproc->num_mems = num_mems; + + return 0; +} + static int da8xx_rproc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -184,12 +243,14 @@ static int da8xx_rproc_probe(struct platform_device *pdev) return -EINVAL; } - bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + bootreg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "host1cfg"); bootreg = devm_ioremap_resource(dev, bootreg_res); if (IS_ERR(bootreg)) return PTR_ERR(bootreg); - chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + chipsig_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "chipsig"); chipsig = devm_ioremap_resource(dev, chipsig_res); if (IS_ERR(chipsig)) return PTR_ERR(chipsig); @@ -201,16 +262,31 @@ static int da8xx_rproc_probe(struct platform_device *pdev) return PTR_ERR(dsp_clk); } + if (dev->of_node) { + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "device does not have specific CMA pool: %d\n", + ret); + return ret; + } + } + rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name, sizeof(*drproc)); - if (!rproc) - return -ENOMEM; + if (!rproc) { + ret = -ENOMEM; + goto free_mem; + } drproc = rproc->priv; drproc->rproc = rproc; drproc->dsp_clk = dsp_clk; rproc->has_iommu = false; + ret = da8xx_rproc_get_internal_memories(pdev, drproc); + if (ret) + goto free_rproc; + platform_set_drvdata(pdev, rproc); /* everything the ISR needs is now setup, so hook it up */ @@ -247,7 +323,9 @@ static int da8xx_rproc_probe(struct platform_device *pdev) free_rproc: rproc_free(rproc); - +free_mem: + if (dev->of_node) + of_reserved_mem_device_release(dev); return ret; } @@ -255,6 +333,7 @@ static int da8xx_rproc_remove(struct platform_device *pdev) { struct rproc *rproc = platform_get_drvdata(pdev); struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; + struct device *dev = &pdev->dev; /* * The devm subsystem might end up releasing things before @@ -265,15 +344,24 @@ static int da8xx_rproc_remove(struct platform_device *pdev) rproc_del(rproc); rproc_free(rproc); + if (dev->of_node) + of_reserved_mem_device_release(dev); return 0; } +static const struct of_device_id davinci_rproc_of_match[] __maybe_unused = { + { .compatible = "ti,da850-dsp", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, davinci_rproc_of_match); + static struct platform_driver da8xx_rproc_driver = { .probe = da8xx_rproc_probe, .remove = da8xx_rproc_remove, .driver = { .name = "davinci-rproc", + .of_match_table = of_match_ptr(davinci_rproc_of_match), }, }; diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c new file mode 100644 index 000000000000..612d91403341 --- /dev/null +++ b/drivers/remoteproc/imx_rproc.c @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/remoteproc.h> + +#define IMX7D_SRC_SCR 0x0C +#define IMX7D_ENABLE_M4 BIT(3) +#define IMX7D_SW_M4P_RST BIT(2) +#define IMX7D_SW_M4C_RST BIT(1) +#define IMX7D_SW_M4C_NON_SCLR_RST BIT(0) + +#define IMX7D_M4_RST_MASK (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \ + | IMX7D_SW_M4C_RST \ + | IMX7D_SW_M4C_NON_SCLR_RST) + +#define IMX7D_M4_START (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \ + | IMX7D_SW_M4C_RST) +#define IMX7D_M4_STOP IMX7D_SW_M4C_NON_SCLR_RST + +/* Address: 0x020D8000 */ +#define IMX6SX_SRC_SCR 0x00 +#define IMX6SX_ENABLE_M4 BIT(22) +#define IMX6SX_SW_M4P_RST BIT(12) +#define IMX6SX_SW_M4C_NON_SCLR_RST BIT(4) +#define IMX6SX_SW_M4C_RST BIT(3) + +#define IMX6SX_M4_START (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \ + | IMX6SX_SW_M4C_RST) +#define IMX6SX_M4_STOP IMX6SX_SW_M4C_NON_SCLR_RST +#define IMX6SX_M4_RST_MASK (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \ + | IMX6SX_SW_M4C_NON_SCLR_RST \ + | IMX6SX_SW_M4C_RST) + +#define IMX7D_RPROC_MEM_MAX 8 + +/** + * struct imx_rproc_mem - slim internal memory structure + * @cpu_addr: MPU virtual address of the memory region + * @sys_addr: Bus address used to access the memory region + * @size: Size of the memory region + */ +struct imx_rproc_mem { + void __iomem *cpu_addr; + phys_addr_t sys_addr; + size_t size; +}; + +/* att flags */ +/* M4 own area. Can be mapped at probe */ +#define ATT_OWN BIT(1) + +/* address translation table */ +struct imx_rproc_att { + u32 da; /* device address (From Cortex M4 view)*/ + u32 sa; /* system bus address */ + u32 size; /* size of reg range */ + int flags; +}; + +struct imx_rproc_dcfg { + u32 src_reg; + u32 src_mask; + u32 src_start; + u32 src_stop; + const struct imx_rproc_att *att; + size_t att_size; +}; + +struct imx_rproc { + struct device *dev; + struct regmap *regmap; + struct rproc *rproc; + const struct imx_rproc_dcfg *dcfg; + struct imx_rproc_mem mem[IMX7D_RPROC_MEM_MAX]; + struct clk *clk; +}; + +static const struct imx_rproc_att imx_rproc_att_imx7d[] = { + /* dev addr , sys addr , size , flags */ + /* OCRAM_S (M4 Boot code) - alias */ + { 0x00000000, 0x00180000, 0x00008000, 0 }, + /* OCRAM_S (Code) */ + { 0x00180000, 0x00180000, 0x00008000, ATT_OWN }, + /* OCRAM (Code) - alias */ + { 0x00900000, 0x00900000, 0x00020000, 0 }, + /* OCRAM_EPDC (Code) - alias */ + { 0x00920000, 0x00920000, 0x00020000, 0 }, + /* OCRAM_PXP (Code) - alias */ + { 0x00940000, 0x00940000, 0x00008000, 0 }, + /* TCML (Code) */ + { 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN }, + /* DDR (Code) - alias, first part of DDR (Data) */ + { 0x10000000, 0x80000000, 0x0FFF0000, 0 }, + + /* TCMU (Data) */ + { 0x20000000, 0x00800000, 0x00008000, ATT_OWN }, + /* OCRAM (Data) */ + { 0x20200000, 0x00900000, 0x00020000, 0 }, + /* OCRAM_EPDC (Data) */ + { 0x20220000, 0x00920000, 0x00020000, 0 }, + /* OCRAM_PXP (Data) */ + { 0x20240000, 0x00940000, 0x00008000, 0 }, + /* DDR (Data) */ + { 0x80000000, 0x80000000, 0x60000000, 0 }, +}; + +static const struct imx_rproc_att imx_rproc_att_imx6sx[] = { + /* dev addr , sys addr , size , flags */ + /* TCML (M4 Boot Code) - alias */ + { 0x00000000, 0x007F8000, 0x00008000, 0 }, + /* OCRAM_S (Code) */ + { 0x00180000, 0x008F8000, 0x00004000, 0 }, + /* OCRAM_S (Code) - alias */ + { 0x00180000, 0x008FC000, 0x00004000, 0 }, + /* TCML (Code) */ + { 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN }, + /* DDR (Code) - alias, first part of DDR (Data) */ + { 0x10000000, 0x80000000, 0x0FFF8000, 0 }, + + /* TCMU (Data) */ + { 0x20000000, 0x00800000, 0x00008000, ATT_OWN }, + /* OCRAM_S (Data) - alias? */ + { 0x208F8000, 0x008F8000, 0x00004000, 0 }, + /* DDR (Data) */ + { 0x80000000, 0x80000000, 0x60000000, 0 }, +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = { + .src_reg = IMX7D_SRC_SCR, + .src_mask = IMX7D_M4_RST_MASK, + .src_start = IMX7D_M4_START, + .src_stop = IMX7D_M4_STOP, + .att = imx_rproc_att_imx7d, + .att_size = ARRAY_SIZE(imx_rproc_att_imx7d), +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = { + .src_reg = IMX6SX_SRC_SCR, + .src_mask = IMX6SX_M4_RST_MASK, + .src_start = IMX6SX_M4_START, + .src_stop = IMX6SX_M4_STOP, + .att = imx_rproc_att_imx6sx, + .att_size = ARRAY_SIZE(imx_rproc_att_imx6sx), +}; + +static int imx_rproc_start(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; + const struct imx_rproc_dcfg *dcfg = priv->dcfg; + struct device *dev = priv->dev; + int ret; + + ret = regmap_update_bits(priv->regmap, dcfg->src_reg, + dcfg->src_mask, dcfg->src_start); + if (ret) + dev_err(dev, "Filed to enable M4!\n"); + + return ret; +} + +static int imx_rproc_stop(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; + const struct imx_rproc_dcfg *dcfg = priv->dcfg; + struct device *dev = priv->dev; + int ret; + + ret = regmap_update_bits(priv->regmap, dcfg->src_reg, + dcfg->src_mask, dcfg->src_stop); + if (ret) + dev_err(dev, "Filed to stop M4!\n"); + + return ret; +} + +static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, + int len, u64 *sys) +{ + const struct imx_rproc_dcfg *dcfg = priv->dcfg; + int i; + + /* parse address translation table */ + for (i = 0; i < dcfg->att_size; i++) { + const struct imx_rproc_att *att = &dcfg->att[i]; + + if (da >= att->da && da + len < att->da + att->size) { + unsigned int offset = da - att->da; + + *sys = att->sa + offset; + return 0; + } + } + + dev_warn(priv->dev, "Translation filed: da = 0x%llx len = 0x%x\n", + da, len); + return -ENOENT; +} + +static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, int len) +{ + struct imx_rproc *priv = rproc->priv; + void *va = NULL; + u64 sys; + int i; + + if (len <= 0) + return NULL; + + /* + * On device side we have many aliases, so we need to convert device + * address (M4) to system bus address first. + */ + if (imx_rproc_da_to_sys(priv, da, len, &sys)) + return NULL; + + for (i = 0; i < IMX7D_RPROC_MEM_MAX; i++) { + if (sys >= priv->mem[i].sys_addr && sys + len < + priv->mem[i].sys_addr + priv->mem[i].size) { + unsigned int offset = sys - priv->mem[i].sys_addr; + /* __force to make sparse happy with type conversion */ + va = (__force void *)(priv->mem[i].cpu_addr + offset); + break; + } + } + + dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va); + + return va; +} + +static const struct rproc_ops imx_rproc_ops = { + .start = imx_rproc_start, + .stop = imx_rproc_stop, + .da_to_va = imx_rproc_da_to_va, +}; + +static int imx_rproc_addr_init(struct imx_rproc *priv, + struct platform_device *pdev) +{ + const struct imx_rproc_dcfg *dcfg = priv->dcfg; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int a, b = 0, err, nph; + + /* remap required addresses */ + for (a = 0; a < dcfg->att_size; a++) { + const struct imx_rproc_att *att = &dcfg->att[a]; + + if (!(att->flags & ATT_OWN)) + continue; + + if (b > IMX7D_RPROC_MEM_MAX) + break; + + priv->mem[b].cpu_addr = devm_ioremap(&pdev->dev, + att->sa, att->size); + if (IS_ERR(priv->mem[b].cpu_addr)) { + dev_err(dev, "devm_ioremap_resource failed\n"); + err = PTR_ERR(priv->mem[b].cpu_addr); + return err; + } + priv->mem[b].sys_addr = att->sa; + priv->mem[b].size = att->size; + b++; + } + + /* memory-region is optional property */ + nph = of_count_phandle_with_args(np, "memory-region", NULL); + if (nph <= 0) + return 0; + + /* remap optional addresses */ + for (a = 0; a < nph; a++) { + struct device_node *node; + struct resource res; + + node = of_parse_phandle(np, "memory-region", a); + err = of_address_to_resource(node, 0, &res); + if (err) { + dev_err(dev, "unable to resolve memory region\n"); + return err; + } + + if (b > IMX7D_RPROC_MEM_MAX) + break; + + priv->mem[b].cpu_addr = devm_ioremap_resource(&pdev->dev, &res); + if (IS_ERR(priv->mem[b].cpu_addr)) { + dev_err(dev, "devm_ioremap_resource failed\n"); + err = PTR_ERR(priv->mem[b].cpu_addr); + return err; + } + priv->mem[b].sys_addr = res.start; + priv->mem[b].size = resource_size(&res); + b++; + } + + return 0; +} + +static int imx_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct imx_rproc *priv; + struct rproc *rproc; + struct regmap_config config = { .name = "imx-rproc" }; + const struct imx_rproc_dcfg *dcfg; + struct regmap *regmap; + int ret; + + regmap = syscon_regmap_lookup_by_phandle(np, "syscon"); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to find syscon\n"); + return PTR_ERR(regmap); + } + regmap_attach_dev(dev, regmap, &config); + + /* set some other name then imx */ + rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops, + NULL, sizeof(*priv)); + if (!rproc) { + ret = -ENOMEM; + goto err; + } + + dcfg = of_device_get_match_data(dev); + if (!dcfg) + return -EINVAL; + + priv = rproc->priv; + priv->rproc = rproc; + priv->regmap = regmap; + priv->dcfg = dcfg; + priv->dev = dev; + + dev_set_drvdata(dev, rproc); + + ret = imx_rproc_addr_init(priv, pdev); + if (ret) { + dev_err(dev, "filed on imx_rproc_addr_init\n"); + goto err_put_rproc; + } + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "Failed to get clock\n"); + rproc_free(rproc); + return PTR_ERR(priv->clk); + } + + /* + * clk for M4 block including memory. Should be + * enabled before .start for FW transfer. + */ + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(&rproc->dev, "Failed to enable clock\n"); + rproc_free(rproc); + return ret; + } + + ret = rproc_add(rproc); + if (ret) { + dev_err(dev, "rproc_add failed\n"); + goto err_put_clk; + } + + return ret; + +err_put_clk: + clk_disable_unprepare(priv->clk); +err_put_rproc: + rproc_free(rproc); +err: + return ret; +} + +static int imx_rproc_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + struct imx_rproc *priv = rproc->priv; + + clk_disable_unprepare(priv->clk); + rproc_del(rproc); + rproc_free(rproc); + + return 0; +} + +static const struct of_device_id imx_rproc_of_match[] = { + { .compatible = "fsl,imx7d-cm4", .data = &imx_rproc_cfg_imx7d }, + { .compatible = "fsl,imx6sx-cm4", .data = &imx_rproc_cfg_imx6sx }, + {}, +}; +MODULE_DEVICE_TABLE(of, imx_rproc_of_match); + +static struct platform_driver imx_rproc_driver = { + .probe = imx_rproc_probe, + .remove = imx_rproc_remove, + .driver = { + .name = "imx-rproc", + .of_match_table = imx_rproc_of_match, + }, +}; + +module_platform_driver(imx_rproc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("IMX6SX/7D remote processor control driver"); +MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); diff --git a/drivers/remoteproc/keystone_remoteproc.c b/drivers/remoteproc/keystone_remoteproc.c index 5f776bfd674a..aaac31134e39 100644 --- a/drivers/remoteproc/keystone_remoteproc.c +++ b/drivers/remoteproc/keystone_remoteproc.c @@ -410,7 +410,7 @@ static int keystone_rproc_probe(struct platform_device *pdev) if (ret) goto free_rproc; - ksproc->reset = devm_reset_control_get(dev, NULL); + ksproc->reset = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(ksproc->reset)) { ret = PTR_ERR(ksproc->reset); goto free_rproc; @@ -505,6 +505,7 @@ static const struct of_device_id keystone_rproc_of_match[] = { { .compatible = "ti,k2hk-dsp", }, { .compatible = "ti,k2l-dsp", }, { .compatible = "ti,k2e-dsp", }, + { .compatible = "ti,k2g-dsp", }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, keystone_rproc_of_match); diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c index 49fe2f807e1d..3f6af54dbc96 100644 --- a/drivers/remoteproc/qcom_adsp_pil.c +++ b/drivers/remoteproc/qcom_adsp_pil.c @@ -38,6 +38,7 @@ struct adsp_data { const char *firmware_name; int pas_id; bool has_aggre2_clk; + const char *ssr_name; }; struct qcom_adsp { @@ -71,7 +72,9 @@ struct qcom_adsp { void *mem_region; size_t mem_size; + struct qcom_rproc_glink glink_subdev; struct qcom_rproc_subdev smd_subdev; + struct qcom_rproc_ssr ssr_subdev; }; static int adsp_load(struct rproc *rproc, const struct firmware *fw) @@ -266,10 +269,7 @@ static int adsp_init_regulator(struct qcom_adsp *adsp) regulator_set_load(adsp->cx_supply, 100000); adsp->px_supply = devm_regulator_get(adsp->dev, "px"); - if (IS_ERR(adsp->px_supply)) - return PTR_ERR(adsp->px_supply); - - return 0; + return PTR_ERR_OR_ZERO(adsp->px_supply); } static int adsp_request_irq(struct qcom_adsp *adsp, @@ -401,7 +401,9 @@ static int adsp_probe(struct platform_device *pdev) goto free_rproc; } + qcom_add_glink_subdev(rproc, &adsp->glink_subdev); qcom_add_smd_subdev(rproc, &adsp->smd_subdev); + qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); ret = rproc_add(rproc); if (ret) @@ -422,7 +424,9 @@ static int adsp_remove(struct platform_device *pdev) qcom_smem_state_put(adsp->state); rproc_del(adsp->rproc); + qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); + qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); rproc_free(adsp->rproc); return 0; @@ -433,6 +437,7 @@ static const struct adsp_data adsp_resource_init = { .firmware_name = "adsp.mdt", .pas_id = 1, .has_aggre2_clk = false, + .ssr_name = "lpass", }; static const struct adsp_data slpi_resource_init = { @@ -440,6 +445,7 @@ static const struct adsp_data slpi_resource_init = { .firmware_name = "slpi.mdt", .pas_id = 12, .has_aggre2_clk = true, + .ssr_name = "dsps", }; static const struct of_device_id adsp_of_match[] = { diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index bb90481215c6..d487040b528b 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -18,13 +18,19 @@ #include <linux/firmware.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/notifier.h> #include <linux/remoteproc.h> +#include <linux/rpmsg/qcom_glink.h> #include <linux/rpmsg/qcom_smd.h> #include "remoteproc_internal.h" #include "qcom_common.h" +#define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev) #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev) +#define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev) + +static BLOCKING_NOTIFIER_HEAD(ssr_notifiers); /** * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc @@ -45,13 +51,60 @@ struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, } EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table); +static int glink_subdev_probe(struct rproc_subdev *subdev) +{ + struct qcom_rproc_glink *glink = to_glink_subdev(subdev); + + glink->edge = qcom_glink_smem_register(glink->dev, glink->node); + + return IS_ERR(glink->edge) ? PTR_ERR(glink->edge) : 0; +} + +static void glink_subdev_remove(struct rproc_subdev *subdev) +{ + struct qcom_rproc_glink *glink = to_glink_subdev(subdev); + + qcom_glink_smem_unregister(glink->edge); + glink->edge = NULL; +} + +/** + * qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc + * @rproc: rproc handle to parent the subdevice + * @glink: reference to a GLINK subdev context + */ +void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) +{ + struct device *dev = &rproc->dev; + + glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge"); + if (!glink->node) + return; + + glink->dev = dev; + rproc_add_subdev(rproc, &glink->subdev, glink_subdev_probe, glink_subdev_remove); +} +EXPORT_SYMBOL_GPL(qcom_add_glink_subdev); + +/** + * qcom_remove_glink_subdev() - remove a GLINK subdevice from rproc + * @rproc: rproc handle + * @glink: reference to a GLINK subdev context + */ +void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) +{ + rproc_remove_subdev(rproc, &glink->subdev); + of_node_put(glink->node); +} +EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev); + static int smd_subdev_probe(struct rproc_subdev *subdev) { struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); smd->edge = qcom_smd_register_edge(smd->dev, smd->node); - return IS_ERR(smd->edge) ? PTR_ERR(smd->edge) : 0; + return PTR_ERR_OR_ZERO(smd->edge); } static void smd_subdev_remove(struct rproc_subdev *subdev) @@ -92,5 +145,72 @@ void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) } EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev); +/** + * qcom_register_ssr_notifier() - register SSR notification handler + * @nb: notifier_block to notify for restart notifications + * + * Returns 0 on success, negative errno on failure. + * + * This register the @notify function as handler for restart notifications. As + * remote processors are stopped this function will be called, with the SSR + * name passed as a parameter. + */ +int qcom_register_ssr_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&ssr_notifiers, nb); +} +EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier); + +/** + * qcom_unregister_ssr_notifier() - unregister SSR notification handler + * @nb: notifier_block to unregister + */ +void qcom_unregister_ssr_notifier(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&ssr_notifiers, nb); +} +EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier); + +static int ssr_notify_start(struct rproc_subdev *subdev) +{ + return 0; +} + +static void ssr_notify_stop(struct rproc_subdev *subdev) +{ + struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); + + blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr->name); +} + +/** + * qcom_add_ssr_subdev() - register subdevice as restart notification source + * @rproc: rproc handle + * @ssr: SSR subdevice handle + * @ssr_name: identifier to use for notifications originating from @rproc + * + * As the @ssr is registered with the @rproc SSR events will be sent to all + * registered listeners in the system as the remoteproc is shut down. + */ +void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, + const char *ssr_name) +{ + ssr->name = ssr_name; + + rproc_add_subdev(rproc, &ssr->subdev, ssr_notify_start, ssr_notify_stop); +} +EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev); + +/** + * qcom_remove_ssr_subdev() - remove subdevice as restart notification source + * @rproc: rproc handle + * @ssr: SSR subdevice handle + */ +void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr) +{ + rproc_remove_subdev(rproc, &ssr->subdev); +} +EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev); + MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h index db5c826d5cd4..4f8bc168473c 100644 --- a/drivers/remoteproc/qcom_common.h +++ b/drivers/remoteproc/qcom_common.h @@ -4,6 +4,14 @@ #include <linux/remoteproc.h> #include "remoteproc_internal.h" +struct qcom_rproc_glink { + struct rproc_subdev subdev; + + struct device *dev; + struct device_node *node; + struct qcom_glink *edge; +}; + struct qcom_rproc_subdev { struct rproc_subdev subdev; @@ -12,11 +20,24 @@ struct qcom_rproc_subdev { struct qcom_smd_edge *edge; }; +struct qcom_rproc_ssr { + struct rproc_subdev subdev; + + const char *name; +}; + struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, const struct firmware *fw, int *tablesz); +void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink); +void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink); + void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); +void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, + const char *ssr_name); +void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr); + #endif diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 8fd697a3cf8f..2d3d5ac92c06 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -153,6 +153,7 @@ struct q6v5 { size_t mpss_size; struct qcom_rproc_subdev smd_subdev; + struct qcom_rproc_ssr ssr_subdev; }; static int q6v5_regulator_init(struct device *dev, struct reg_info *regs, @@ -867,7 +868,8 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks, static int q6v5_init_reset(struct q6v5 *qproc) { - qproc->mss_restart = devm_reset_control_get(qproc->dev, NULL); + qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev, + NULL); if (IS_ERR(qproc->mss_restart)) { dev_err(qproc->dev, "failed to acquire mss restart\n"); return PTR_ERR(qproc->mss_restart); @@ -1038,6 +1040,7 @@ static int q6v5_probe(struct platform_device *pdev) } qcom_add_smd_subdev(rproc, &qproc->smd_subdev); + qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); ret = rproc_add(rproc); if (ret) @@ -1058,6 +1061,7 @@ static int q6v5_remove(struct platform_device *pdev) rproc_del(qproc->rproc); qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); + qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); rproc_free(qproc->rproc); return 0; diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 564061dcc019..eab14b414bf0 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -794,7 +794,7 @@ static void rproc_remove_subdevices(struct rproc *rproc) { struct rproc_subdev *subdev; - list_for_each_entry(subdev, &rproc->subdevs, node) + list_for_each_entry_reverse(subdev, &rproc->subdevs, node) subdev->remove(subdev); } @@ -1119,7 +1119,7 @@ static void rproc_crash_handler_work(struct work_struct *work) } /** - * __rproc_boot() - boot a remote processor + * rproc_boot() - boot a remote processor * @rproc: handle of a remote processor * * Boot a remote processor (i.e. load its firmware, power it on, ...). @@ -1129,7 +1129,7 @@ static void rproc_crash_handler_work(struct work_struct *work) * * Returns 0 on success, and an appropriate error value otherwise. */ -static int __rproc_boot(struct rproc *rproc) +int rproc_boot(struct rproc *rproc) { const struct firmware *firmware_p; struct device *dev; @@ -1180,15 +1180,6 @@ unlock_mutex: mutex_unlock(&rproc->lock); return ret; } - -/** - * rproc_boot() - boot a remote processor - * @rproc: handle of a remote processor - */ -int rproc_boot(struct rproc *rproc) -{ - return __rproc_boot(rproc); -} EXPORT_SYMBOL(rproc_boot); /** @@ -1369,7 +1360,7 @@ static void rproc_type_release(struct device *dev) kfree(rproc); } -static struct device_type rproc_type = { +static const struct device_type rproc_type = { .name = "remoteproc", .release = rproc_type_release, }; @@ -1440,6 +1431,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, rproc->dev.parent = dev; rproc->dev.type = &rproc_type; rproc->dev.class = &rproc_class; + rproc->dev.driver_data = rproc; /* Assign a unique device index and name */ rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL); @@ -1579,6 +1571,23 @@ void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev) EXPORT_SYMBOL(rproc_remove_subdev); /** + * rproc_get_by_child() - acquire rproc handle of @dev's ancestor + * @dev: child device to find ancestor of + * + * Returns the ancestor rproc instance, or NULL if not found. + */ +struct rproc *rproc_get_by_child(struct device *dev) +{ + for (dev = dev->parent; dev; dev = dev->parent) { + if (dev->type == &rproc_type) + return dev->driver_data; + } + + return NULL; +} +EXPORT_SYMBOL(rproc_get_by_child); + +/** * rproc_report_crash() - rproc crash reporter function * @rproc: remote processor * @type: crash type diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 1e9e5b3f021c..c1077bec5d0b 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -48,7 +48,6 @@ struct rproc_fw_ops { /* from remoteproc_core.c */ void rproc_release(struct kref *kref); irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); -int rproc_boot_nowait(struct rproc *rproc); void rproc_vdev_release(struct kref *ref); /* from remoteproc_virtio.c */ diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index d534bf23dc56..aacef0ea3b90 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c @@ -212,7 +212,8 @@ static int st_rproc_parse_dt(struct platform_device *pdev) int err; if (ddata->config->sw_reset) { - ddata->sw_reset = devm_reset_control_get(dev, "sw_reset"); + ddata->sw_reset = devm_reset_control_get_exclusive(dev, + "sw_reset"); if (IS_ERR(ddata->sw_reset)) { dev_err(dev, "Failed to get S/W Reset\n"); return PTR_ERR(ddata->sw_reset); @@ -220,7 +221,8 @@ static int st_rproc_parse_dt(struct platform_device *pdev) } if (ddata->config->pwr_reset) { - ddata->pwr_reset = devm_reset_control_get(dev, "pwr_reset"); + ddata->pwr_reset = devm_reset_control_get_exclusive(dev, + "pwr_reset"); if (IS_ERR(ddata->pwr_reset)) { dev_err(dev, "Failed to get Power Reset\n"); return PTR_ERR(ddata->pwr_reset); diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 608c071e4bbf..52d5251660b9 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -34,12 +34,11 @@ config RESET_BERLIN help This enables the reset controller driver for Marvell Berlin SoCs. -config RESET_GEMINI - bool "Gemini Reset Driver" if COMPILE_TEST - default ARCH_GEMINI - select MFD_SYSCON +config RESET_HSDK_V1 + bool "HSDK v1 Reset Driver" + default n help - This enables the reset controller driver for Cortina Systems Gemini. + This enables the reset controller driver for HSDK v1. config RESET_IMX7 bool "i.MX7 Reset Driver" if COMPILE_TEST diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 7081f9da2599..b62783f50fe5 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o obj-$(CONFIG_RESET_ATH79) += reset-ath79.o obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o -obj-$(CONFIG_RESET_GEMINI) += reset-gemini.o +obj-$(CONFIG_RESET_HSDK_V1) += reset-hsdk-v1.o obj-$(CONFIG_RESET_IMX7) += reset-imx7.o obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o obj-$(CONFIG_RESET_MESON) += reset-meson.o diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 0090784ff410..1d21c6f7d56c 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -43,11 +43,24 @@ struct reset_control { unsigned int id; struct kref refcnt; bool shared; + bool array; atomic_t deassert_count; atomic_t triggered_count; }; /** + * struct reset_control_array - an array of reset controls + * @base: reset control for compatibility with reset control API functions + * @num_rstcs: number of reset controls + * @rstc: array of reset controls + */ +struct reset_control_array { + struct reset_control base; + unsigned int num_rstcs; + struct reset_control *rstc[]; +}; + +/** * of_reset_simple_xlate - translate reset_spec to the reset line number * @rcdev: a pointer to the reset controller device * @reset_spec: reset line specifier as found in the device tree @@ -135,6 +148,65 @@ int devm_reset_controller_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_reset_controller_register); +static inline struct reset_control_array * +rstc_to_array(struct reset_control *rstc) { + return container_of(rstc, struct reset_control_array, base); +} + +static int reset_control_array_reset(struct reset_control_array *resets) +{ + int ret, i; + + for (i = 0; i < resets->num_rstcs; i++) { + ret = reset_control_reset(resets->rstc[i]); + if (ret) + return ret; + } + + return 0; +} + +static int reset_control_array_assert(struct reset_control_array *resets) +{ + int ret, i; + + for (i = 0; i < resets->num_rstcs; i++) { + ret = reset_control_assert(resets->rstc[i]); + if (ret) + goto err; + } + + return 0; + +err: + while (i--) + reset_control_deassert(resets->rstc[i]); + return ret; +} + +static int reset_control_array_deassert(struct reset_control_array *resets) +{ + int ret, i; + + for (i = 0; i < resets->num_rstcs; i++) { + ret = reset_control_deassert(resets->rstc[i]); + if (ret) + goto err; + } + + return 0; + +err: + while (i--) + reset_control_assert(resets->rstc[i]); + return ret; +} + +static inline bool reset_control_is_array(struct reset_control *rstc) +{ + return rstc->array; +} + /** * reset_control_reset - reset the controlled device * @rstc: reset controller @@ -158,6 +230,9 @@ int reset_control_reset(struct reset_control *rstc) if (WARN_ON(IS_ERR(rstc))) return -EINVAL; + if (reset_control_is_array(rstc)) + return reset_control_array_reset(rstc_to_array(rstc)); + if (!rstc->rcdev->ops->reset) return -ENOTSUPP; @@ -202,8 +277,8 @@ int reset_control_assert(struct reset_control *rstc) if (WARN_ON(IS_ERR(rstc))) return -EINVAL; - if (!rstc->rcdev->ops->assert) - return -ENOTSUPP; + if (reset_control_is_array(rstc)) + return reset_control_array_assert(rstc_to_array(rstc)); if (rstc->shared) { if (WARN_ON(atomic_read(&rstc->triggered_count) != 0)) @@ -214,6 +289,21 @@ int reset_control_assert(struct reset_control *rstc) if (atomic_dec_return(&rstc->deassert_count) != 0) return 0; + + /* + * Shared reset controls allow the reset line to be in any state + * after this call, so doing nothing is a valid option. + */ + if (!rstc->rcdev->ops->assert) + return 0; + } else { + /* + * If the reset controller does not implement .assert(), there + * is no way to guarantee that the reset line is asserted after + * this call. + */ + if (!rstc->rcdev->ops->assert) + return -ENOTSUPP; } return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); @@ -240,8 +330,8 @@ int reset_control_deassert(struct reset_control *rstc) if (WARN_ON(IS_ERR(rstc))) return -EINVAL; - if (!rstc->rcdev->ops->deassert) - return -ENOTSUPP; + if (reset_control_is_array(rstc)) + return reset_control_array_deassert(rstc_to_array(rstc)); if (rstc->shared) { if (WARN_ON(atomic_read(&rstc->triggered_count) != 0)) @@ -251,6 +341,16 @@ int reset_control_deassert(struct reset_control *rstc) return 0; } + /* + * If the reset controller does not implement .deassert(), we assume + * that it handles self-deasserting reset lines via .reset(). In that + * case, the reset lines are deasserted by default. If that is not the + * case, the reset controller driver should implement .deassert() and + * return -ENOTSUPP. + */ + if (!rstc->rcdev->ops->deassert) + return 0; + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_deassert); @@ -266,7 +366,7 @@ int reset_control_status(struct reset_control *rstc) if (!rstc) return 0; - if (WARN_ON(IS_ERR(rstc))) + if (WARN_ON(IS_ERR(rstc)) || reset_control_is_array(rstc)) return -EINVAL; if (rstc->rcdev->ops->status) @@ -404,6 +504,16 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id, } EXPORT_SYMBOL_GPL(__reset_control_get); +static void reset_control_array_put(struct reset_control_array *resets) +{ + int i; + + mutex_lock(&reset_list_mutex); + for (i = 0; i < resets->num_rstcs; i++) + __reset_control_put_internal(resets->rstc[i]); + mutex_unlock(&reset_list_mutex); +} + /** * reset_control_put - free the reset controller * @rstc: reset controller @@ -413,6 +523,11 @@ void reset_control_put(struct reset_control *rstc) if (IS_ERR_OR_NULL(rstc)) return; + if (reset_control_is_array(rstc)) { + reset_control_array_put(rstc_to_array(rstc)); + return; + } + mutex_lock(&reset_list_mutex); __reset_control_put_internal(rstc); mutex_unlock(&reset_list_mutex); @@ -472,3 +587,116 @@ int device_reset(struct device *dev) return ret; } EXPORT_SYMBOL_GPL(device_reset); + +/** + * APIs to manage an array of reset controls. + */ +/** + * of_reset_control_get_count - Count number of resets available with a device + * + * @node: device node that contains 'resets'. + * + * Returns positive reset count on success, or error number on failure and + * on count being zero. + */ +static int of_reset_control_get_count(struct device_node *node) +{ + int count; + + if (!node) + return -EINVAL; + + count = of_count_phandle_with_args(node, "resets", "#reset-cells"); + if (count == 0) + count = -ENOENT; + + return count; +} + +/** + * of_reset_control_array_get - Get a list of reset controls using + * device node. + * + * @np: device node for the device that requests the reset controls array + * @shared: whether reset controls are shared or not + * @optional: whether it is optional to get the reset controls + * + * Returns pointer to allocated reset_control_array on success or + * error on failure + */ +struct reset_control * +of_reset_control_array_get(struct device_node *np, bool shared, bool optional) +{ + struct reset_control_array *resets; + struct reset_control *rstc; + int num, i; + + num = of_reset_control_get_count(np); + if (num < 0) + return optional ? NULL : ERR_PTR(num); + + resets = kzalloc(sizeof(*resets) + sizeof(resets->rstc[0]) * num, + GFP_KERNEL); + if (!resets) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num; i++) { + rstc = __of_reset_control_get(np, NULL, i, shared, optional); + if (IS_ERR(rstc)) + goto err_rst; + resets->rstc[i] = rstc; + } + resets->num_rstcs = num; + resets->base.array = true; + + return &resets->base; + +err_rst: + mutex_lock(&reset_list_mutex); + while (--i >= 0) + __reset_control_put_internal(resets->rstc[i]); + mutex_unlock(&reset_list_mutex); + + kfree(resets); + + return rstc; +} +EXPORT_SYMBOL_GPL(of_reset_control_array_get); + +/** + * devm_reset_control_array_get - Resource managed reset control array get + * + * @dev: device that requests the list of reset controls + * @shared: whether reset controls are shared or not + * @optional: whether it is optional to get the reset controls + * + * The reset control array APIs are intended for a list of resets + * that just have to be asserted or deasserted, without any + * requirements on the order. + * + * Returns pointer to allocated reset_control_array on success or + * error on failure + */ +struct reset_control * +devm_reset_control_array_get(struct device *dev, bool shared, bool optional) +{ + struct reset_control **devres; + struct reset_control *rstc; + + devres = devres_alloc(devm_reset_control_release, sizeof(*devres), + GFP_KERNEL); + if (!devres) + return ERR_PTR(-ENOMEM); + + rstc = of_reset_control_array_get(dev->of_node, shared, optional); + if (IS_ERR(rstc)) { + devres_free(devres); + return rstc; + } + + *devres = rstc; + devres_add(dev, devres); + + return rstc; +} +EXPORT_SYMBOL_GPL(devm_reset_control_array_get); diff --git a/drivers/reset/reset-gemini.c b/drivers/reset/reset-gemini.c deleted file mode 100644 index a2478997c75b..000000000000 --- a/drivers/reset/reset-gemini.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Cortina Gemini Reset controller driver - * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/err.h> -#include <linux/init.h> -#include <linux/mfd/syscon.h> -#include <linux/regmap.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/reset-controller.h> -#include <dt-bindings/reset/cortina,gemini-reset.h> - -/** - * struct gemini_reset - gemini reset controller - * @map: regmap to access the containing system controller - * @rcdev: reset controller device - */ -struct gemini_reset { - struct regmap *map; - struct reset_controller_dev rcdev; -}; - -#define GEMINI_GLOBAL_SOFT_RESET 0x0c - -#define to_gemini_reset(p) \ - container_of((p), struct gemini_reset, rcdev) - -/* - * This is a self-deasserting reset controller. - */ -static int gemini_reset(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct gemini_reset *gr = to_gemini_reset(rcdev); - - /* Manual says to always set BIT 30 (CPU1) to 1 */ - return regmap_write(gr->map, - GEMINI_GLOBAL_SOFT_RESET, - BIT(GEMINI_RESET_CPU1) | BIT(id)); -} - -static int gemini_reset_status(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct gemini_reset *gr = to_gemini_reset(rcdev); - u32 val; - int ret; - - ret = regmap_read(gr->map, GEMINI_GLOBAL_SOFT_RESET, &val); - if (ret) - return ret; - - return !!(val & BIT(id)); -} - -static const struct reset_control_ops gemini_reset_ops = { - .reset = gemini_reset, - .status = gemini_reset_status, -}; - -static int gemini_reset_probe(struct platform_device *pdev) -{ - struct gemini_reset *gr; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - int ret; - - gr = devm_kzalloc(dev, sizeof(*gr), GFP_KERNEL); - if (!gr) - return -ENOMEM; - - gr->map = syscon_node_to_regmap(np); - if (IS_ERR(gr->map)) { - ret = PTR_ERR(gr->map); - dev_err(dev, "unable to get regmap (%d)", ret); - return ret; - } - gr->rcdev.owner = THIS_MODULE; - gr->rcdev.nr_resets = 32; - gr->rcdev.ops = &gemini_reset_ops; - gr->rcdev.of_node = pdev->dev.of_node; - - ret = devm_reset_controller_register(&pdev->dev, &gr->rcdev); - if (ret) - return ret; - - dev_info(dev, "registered Gemini reset controller\n"); - return 0; -} - -static const struct of_device_id gemini_reset_dt_ids[] = { - { .compatible = "cortina,gemini-syscon", }, - { /* sentinel */ }, -}; - -static struct platform_driver gemini_reset_driver = { - .probe = gemini_reset_probe, - .driver = { - .name = "gemini-reset", - .of_match_table = gemini_reset_dt_ids, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(gemini_reset_driver); diff --git a/drivers/reset/reset-hsdk-v1.c b/drivers/reset/reset-hsdk-v1.c new file mode 100644 index 000000000000..bca13e4bf622 --- /dev/null +++ b/drivers/reset/reset-hsdk-v1.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2017 Synopsys. + * + * Synopsys HSDKv1 SDP reset driver. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define to_hsdkv1_rst(p) container_of((p), struct hsdkv1_rst, rcdev) + +struct hsdkv1_rst { + void __iomem *regs_ctl; + void __iomem *regs_rst; + spinlock_t lock; + struct reset_controller_dev rcdev; +}; + +static const u32 rst_map[] = { + BIT(16), /* APB_RST */ + BIT(17), /* AXI_RST */ + BIT(18), /* ETH_RST */ + BIT(19), /* USB_RST */ + BIT(20), /* SDIO_RST */ + BIT(21), /* HDMI_RST */ + BIT(22), /* GFX_RST */ + BIT(25), /* DMAC_RST */ + BIT(31), /* EBI_RST */ +}; + +#define HSDK_MAX_RESETS ARRAY_SIZE(rst_map) + +#define CGU_SYS_RST_CTRL 0x0 +#define CGU_IP_SW_RESET 0x0 +#define CGU_IP_SW_RESET_DELAY_SHIFT 16 +#define CGU_IP_SW_RESET_DELAY_MASK GENMASK(31, CGU_IP_SW_RESET_DELAY_SHIFT) +#define CGU_IP_SW_RESET_DELAY 0 +#define CGU_IP_SW_RESET_RESET BIT(0) +#define SW_RESET_TIMEOUT 10000 + +static void hsdkv1_reset_config(struct hsdkv1_rst *rst, unsigned long id) +{ + writel(rst_map[id], rst->regs_ctl + CGU_SYS_RST_CTRL); +} + +static int hsdkv1_reset_do(struct hsdkv1_rst *rst) +{ + u32 reg; + + reg = readl(rst->regs_rst + CGU_IP_SW_RESET); + reg &= ~CGU_IP_SW_RESET_DELAY_MASK; + reg |= CGU_IP_SW_RESET_DELAY << CGU_IP_SW_RESET_DELAY_SHIFT; + reg |= CGU_IP_SW_RESET_RESET; + writel(reg, rst->regs_rst + CGU_IP_SW_RESET); + + /* wait till reset bit is back to 0 */ + return readl_poll_timeout_atomic(rst->regs_rst + CGU_IP_SW_RESET, reg, + !(reg & CGU_IP_SW_RESET_RESET), 5, SW_RESET_TIMEOUT); +} + +static int hsdkv1_reset_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct hsdkv1_rst *rst = to_hsdkv1_rst(rcdev); + unsigned long flags; + int ret; + + spin_lock_irqsave(&rst->lock, flags); + hsdkv1_reset_config(rst, id); + ret = hsdkv1_reset_do(rst); + spin_unlock_irqrestore(&rst->lock, flags); + + return ret; +} + +static const struct reset_control_ops hsdkv1_reset_ops = { + .reset = hsdkv1_reset_reset, +}; + +static int hsdkv1_reset_probe(struct platform_device *pdev) +{ + struct hsdkv1_rst *rst; + struct resource *mem; + + rst = devm_kzalloc(&pdev->dev, sizeof(*rst), GFP_KERNEL); + if (!rst) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rst->regs_ctl = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(rst->regs_ctl)) + return PTR_ERR(rst->regs_ctl); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + rst->regs_rst = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(rst->regs_rst)) + return PTR_ERR(rst->regs_rst); + + spin_lock_init(&rst->lock); + + rst->rcdev.owner = THIS_MODULE; + rst->rcdev.ops = &hsdkv1_reset_ops; + rst->rcdev.of_node = pdev->dev.of_node; + rst->rcdev.nr_resets = HSDK_MAX_RESETS; + rst->rcdev.of_reset_n_cells = 1; + + return reset_controller_register(&rst->rcdev); +} + +static const struct of_device_id hsdkv1_reset_dt_match[] = { + { .compatible = "snps,hsdk-v1.0-reset" }, + { }, +}; + +static struct platform_driver hsdkv1_reset_driver = { + .probe = hsdkv1_reset_probe, + .driver = { + .name = "hsdk-v1.0-reset", + .of_match_table = hsdkv1_reset_dt_match, + }, +}; +builtin_platform_driver(hsdkv1_reset_driver); + +MODULE_AUTHOR("Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>"); +MODULE_DESCRIPTION("Synopsys HSDKv1 SDP reset driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c index cd585cd2f04d..2c7dd1fd08df 100644 --- a/drivers/reset/reset-sunxi.c +++ b/drivers/reset/reset-sunxi.c @@ -107,7 +107,7 @@ static int sunxi_reset_init(struct device_node *np) spin_lock_init(&data->lock); data->rcdev.owner = THIS_MODULE; - data->rcdev.nr_resets = size * 32; + data->rcdev.nr_resets = size * 8; data->rcdev.ops = &sunxi_reset_ops; data->rcdev.of_node = np; @@ -162,7 +162,7 @@ static int sunxi_reset_probe(struct platform_device *pdev) spin_lock_init(&data->lock); data->rcdev.owner = THIS_MODULE; - data->rcdev.nr_resets = resource_size(res) * 32; + data->rcdev.nr_resets = resource_size(res) * 8; data->rcdev.ops = &sunxi_reset_ops; data->rcdev.of_node = pdev->dev.of_node; diff --git a/drivers/reset/reset-uniphier.c b/drivers/reset/reset-uniphier.c index c4ba89832796..bda2dd196ae5 100644 --- a/drivers/reset/reset-uniphier.c +++ b/drivers/reset/reset-uniphier.c @@ -50,59 +50,35 @@ struct uniphier_reset_data { } /* System reset data */ -#define UNIPHIER_SLD3_SYS_RESET_NAND(id) \ - UNIPHIER_RESETX((id), 0x2004, 2) - -#define UNIPHIER_LD11_SYS_RESET_NAND(id) \ - UNIPHIER_RESETX((id), 0x200c, 0) - -#define UNIPHIER_LD11_SYS_RESET_EMMC(id) \ - UNIPHIER_RESETX((id), 0x200c, 2) - -#define UNIPHIER_SLD3_SYS_RESET_STDMAC(id) \ - UNIPHIER_RESETX((id), 0x2000, 10) - -#define UNIPHIER_LD11_SYS_RESET_STDMAC(id) \ - UNIPHIER_RESETX((id), 0x200c, 8) - -#define UNIPHIER_PRO4_SYS_RESET_GIO(id) \ - UNIPHIER_RESETX((id), 0x2000, 6) - -#define UNIPHIER_LD20_SYS_RESET_GIO(id) \ - UNIPHIER_RESETX((id), 0x200c, 5) - -#define UNIPHIER_PRO4_SYS_RESET_USB3(id, ch) \ - UNIPHIER_RESETX((id), 0x2000 + 0x4 * (ch), 17) - -static const struct uniphier_reset_data uniphier_sld3_sys_reset_data[] = { - UNIPHIER_SLD3_SYS_RESET_NAND(2), - UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* Ether, HSC, MIO */ +static const struct uniphier_reset_data uniphier_ld4_sys_reset_data[] = { + UNIPHIER_RESETX(2, 0x2000, 2), /* NAND */ + UNIPHIER_RESETX(8, 0x2000, 10), /* STDMAC (Ether, HSC, MIO) */ UNIPHIER_RESET_END, }; static const struct uniphier_reset_data uniphier_pro4_sys_reset_data[] = { - UNIPHIER_SLD3_SYS_RESET_NAND(2), - UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC, MIO, RLE */ - UNIPHIER_PRO4_SYS_RESET_GIO(12), /* Ether, SATA, USB3 */ - UNIPHIER_PRO4_SYS_RESET_USB3(14, 0), - UNIPHIER_PRO4_SYS_RESET_USB3(15, 1), + UNIPHIER_RESETX(2, 0x2000, 2), /* NAND */ + UNIPHIER_RESETX(8, 0x2000, 10), /* STDMAC (HSC, MIO, RLE) */ + UNIPHIER_RESETX(12, 0x2000, 6), /* GIO (Ether, SATA, USB3) */ + UNIPHIER_RESETX(14, 0x2000, 17), /* USB30 */ + UNIPHIER_RESETX(15, 0x2004, 17), /* USB31 */ UNIPHIER_RESET_END, }; static const struct uniphier_reset_data uniphier_pro5_sys_reset_data[] = { - UNIPHIER_SLD3_SYS_RESET_NAND(2), - UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC */ - UNIPHIER_PRO4_SYS_RESET_GIO(12), /* PCIe, USB3 */ - UNIPHIER_PRO4_SYS_RESET_USB3(14, 0), - UNIPHIER_PRO4_SYS_RESET_USB3(15, 1), + UNIPHIER_RESETX(2, 0x2000, 2), /* NAND */ + UNIPHIER_RESETX(8, 0x2000, 10), /* STDMAC (HSC) */ + UNIPHIER_RESETX(12, 0x2000, 6), /* GIO (PCIe, USB3) */ + UNIPHIER_RESETX(14, 0x2000, 17), /* USB30 */ + UNIPHIER_RESETX(15, 0x2004, 17), /* USB31 */ UNIPHIER_RESET_END, }; static const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = { - UNIPHIER_SLD3_SYS_RESET_NAND(2), - UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC, RLE */ - UNIPHIER_PRO4_SYS_RESET_USB3(14, 0), - UNIPHIER_PRO4_SYS_RESET_USB3(15, 1), + UNIPHIER_RESETX(2, 0x2000, 2), /* NAND */ + UNIPHIER_RESETX(8, 0x2000, 10), /* STDMAC (HSC, RLE) */ + UNIPHIER_RESETX(14, 0x2000, 17), /* USB30 */ + UNIPHIER_RESETX(15, 0x2004, 17), /* USB31 */ UNIPHIER_RESETX(16, 0x2014, 4), /* USB30-PHY0 */ UNIPHIER_RESETX(17, 0x2014, 0), /* USB30-PHY1 */ UNIPHIER_RESETX(18, 0x2014, 2), /* USB30-PHY2 */ @@ -114,21 +90,27 @@ static const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = { }; static const struct uniphier_reset_data uniphier_ld11_sys_reset_data[] = { - UNIPHIER_LD11_SYS_RESET_NAND(2), - UNIPHIER_LD11_SYS_RESET_EMMC(4), - UNIPHIER_LD11_SYS_RESET_STDMAC(8), /* HSC, MIO */ + UNIPHIER_RESETX(2, 0x200c, 0), /* NAND */ + UNIPHIER_RESETX(4, 0x200c, 2), /* eMMC */ + UNIPHIER_RESETX(8, 0x200c, 8), /* STDMAC (HSC, MIO) */ + UNIPHIER_RESETX(40, 0x2008, 0), /* AIO */ + UNIPHIER_RESETX(41, 0x2008, 1), /* EVEA */ + UNIPHIER_RESETX(42, 0x2010, 2), /* EXIV */ UNIPHIER_RESET_END, }; static const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = { - UNIPHIER_LD11_SYS_RESET_NAND(2), - UNIPHIER_LD11_SYS_RESET_EMMC(4), - UNIPHIER_LD11_SYS_RESET_STDMAC(8), /* HSC */ - UNIPHIER_LD20_SYS_RESET_GIO(12), /* PCIe, USB3 */ + UNIPHIER_RESETX(2, 0x200c, 0), /* NAND */ + UNIPHIER_RESETX(4, 0x200c, 2), /* eMMC */ + UNIPHIER_RESETX(8, 0x200c, 8), /* STDMAC (HSC) */ + UNIPHIER_RESETX(12, 0x200c, 5), /* GIO (PCIe, USB3) */ UNIPHIER_RESETX(16, 0x200c, 12), /* USB30-PHY0 */ UNIPHIER_RESETX(17, 0x200c, 13), /* USB30-PHY1 */ UNIPHIER_RESETX(18, 0x200c, 14), /* USB30-PHY2 */ UNIPHIER_RESETX(19, 0x200c, 15), /* USB30-PHY3 */ + UNIPHIER_RESETX(40, 0x2008, 0), /* AIO */ + UNIPHIER_RESETX(41, 0x2008, 1), /* EVEA */ + UNIPHIER_RESETX(42, 0x2010, 2), /* EXIV */ UNIPHIER_RESET_END, }; @@ -151,7 +133,7 @@ static const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = { #define UNIPHIER_MIO_RESET_DMAC(id) \ UNIPHIER_RESETX((id), 0x110, 17) -static const struct uniphier_reset_data uniphier_sld3_mio_reset_data[] = { +static const struct uniphier_reset_data uniphier_ld4_mio_reset_data[] = { UNIPHIER_MIO_RESET_SD(0, 0), UNIPHIER_MIO_RESET_SD(1, 1), UNIPHIER_MIO_RESET_SD(2, 2), @@ -163,11 +145,9 @@ static const struct uniphier_reset_data uniphier_sld3_mio_reset_data[] = { UNIPHIER_MIO_RESET_USB2(8, 0), UNIPHIER_MIO_RESET_USB2(9, 1), UNIPHIER_MIO_RESET_USB2(10, 2), - UNIPHIER_MIO_RESET_USB2(11, 3), UNIPHIER_MIO_RESET_USB2_BRIDGE(12, 0), UNIPHIER_MIO_RESET_USB2_BRIDGE(13, 1), UNIPHIER_MIO_RESET_USB2_BRIDGE(14, 2), - UNIPHIER_MIO_RESET_USB2_BRIDGE(15, 3), UNIPHIER_RESET_END, }; @@ -216,6 +196,12 @@ static const struct uniphier_reset_data uniphier_pro4_peri_reset_data[] = { UNIPHIER_RESET_END, }; +/* Analog signal amplifiers reset data */ +static const struct uniphier_reset_data uniphier_ld11_adamv_reset_data[] = { + UNIPHIER_RESETX(0, 0x10, 6), /* EVEA */ + UNIPHIER_RESET_END, +}; + /* core implementaton */ struct uniphier_reset_priv { struct reset_controller_dev rcdev; @@ -346,12 +332,8 @@ static int uniphier_reset_probe(struct platform_device *pdev) static const struct of_device_id uniphier_reset_match[] = { /* System reset */ { - .compatible = "socionext,uniphier-sld3-reset", - .data = uniphier_sld3_sys_reset_data, - }, - { .compatible = "socionext,uniphier-ld4-reset", - .data = uniphier_sld3_sys_reset_data, + .data = uniphier_ld4_sys_reset_data, }, { .compatible = "socionext,uniphier-pro4-reset", @@ -359,7 +341,7 @@ static const struct of_device_id uniphier_reset_match[] = { }, { .compatible = "socionext,uniphier-sld8-reset", - .data = uniphier_sld3_sys_reset_data, + .data = uniphier_ld4_sys_reset_data, }, { .compatible = "socionext,uniphier-pro5-reset", @@ -379,20 +361,16 @@ static const struct of_device_id uniphier_reset_match[] = { }, /* Media I/O reset, SD reset */ { - .compatible = "socionext,uniphier-sld3-mio-reset", - .data = uniphier_sld3_mio_reset_data, - }, - { .compatible = "socionext,uniphier-ld4-mio-reset", - .data = uniphier_sld3_mio_reset_data, + .data = uniphier_ld4_mio_reset_data, }, { .compatible = "socionext,uniphier-pro4-mio-reset", - .data = uniphier_sld3_mio_reset_data, + .data = uniphier_ld4_mio_reset_data, }, { .compatible = "socionext,uniphier-sld8-mio-reset", - .data = uniphier_sld3_mio_reset_data, + .data = uniphier_ld4_mio_reset_data, }, { .compatible = "socionext,uniphier-pro5-sd-reset", @@ -404,7 +382,7 @@ static const struct of_device_id uniphier_reset_match[] = { }, { .compatible = "socionext,uniphier-ld11-mio-reset", - .data = uniphier_sld3_mio_reset_data, + .data = uniphier_ld4_mio_reset_data, }, { .compatible = "socionext,uniphier-ld11-sd-reset", @@ -443,6 +421,15 @@ static const struct of_device_id uniphier_reset_match[] = { .compatible = "socionext,uniphier-ld20-peri-reset", .data = uniphier_pro4_peri_reset_data, }, + /* Analog signal amplifiers reset */ + { + .compatible = "socionext,uniphier-ld11-adamv-reset", + .data = uniphier_ld11_adamv_reset_data, + }, + { + .compatible = "socionext,uniphier-ld20-adamv-reset", + .data = uniphier_ld11_adamv_reset_data, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, uniphier_reset_match); diff --git a/drivers/reset/reset-zx2967.c b/drivers/reset/reset-zx2967.c index 4dabb9ec4841..4f319f7753d4 100644 --- a/drivers/reset/reset-zx2967.c +++ b/drivers/reset/reset-zx2967.c @@ -55,7 +55,7 @@ static int zx2967_reset_deassert(struct reset_controller_dev *rcdev, return zx2967_reset_act(rcdev, id, false); } -static struct reset_control_ops zx2967_reset_ops = { +static const struct reset_control_ops zx2967_reset_ops = { .assert = zx2967_reset_assert, .deassert = zx2967_reset_deassert, }; diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index 1323a245763b..0fe6eac46512 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -13,9 +13,13 @@ config RPMSG_CHAR in /dev. They make it possible for user-space programs to send and receive rpmsg packets. +config RPMSG_QCOM_GLINK_NATIVE + tristate + select RPMSG + config RPMSG_QCOM_GLINK_RPM tristate "Qualcomm RPM Glink driver" - select RPMSG + select RPMSG_QCOM_GLINK_NATIVE depends on HAS_IOMEM depends on MAILBOX help @@ -23,6 +27,16 @@ config RPMSG_QCOM_GLINK_RPM which serves as a channel for communication with the RPM in GLINK enabled systems. +config RPMSG_QCOM_GLINK_SMEM + tristate "Qualcomm SMEM Glink driver" + select RPMSG_QCOM_GLINK_NATIVE + depends on MAILBOX + depends on QCOM_SMEM + help + Say y here to enable support for the GLINK SMEM communication driver, + which provides support for using the GLINK communication protocol + over SMEM. + config RPMSG_QCOM_SMD tristate "Qualcomm Shared Memory Driver (SMD)" depends on QCOM_SMEM diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index 28cc19088cc0..c71f4ab1ae17 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -1,5 +1,7 @@ obj-$(CONFIG_RPMSG) += rpmsg_core.o obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o +obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o +obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c new file mode 100644 index 000000000000..5a5e927ea50f --- /dev/null +++ b/drivers/rpmsg/qcom_glink_native.c @@ -0,0 +1,1612 @@ +/* + * Copyright (c) 2016-2017, Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/idr.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/rpmsg.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/mailbox_client.h> + +#include "rpmsg_internal.h" +#include "qcom_glink_native.h" + +#define GLINK_NAME_SIZE 32 +#define GLINK_VERSION_1 1 + +#define RPM_GLINK_CID_MIN 1 +#define RPM_GLINK_CID_MAX 65536 + +struct glink_msg { + __le16 cmd; + __le16 param1; + __le32 param2; + u8 data[]; +} __packed; + +/** + * struct glink_defer_cmd - deferred incoming control message + * @node: list node + * @msg: message header + * data: payload of the message + * + * Copy of a received control message, to be added to @rx_queue and processed + * by @rx_work of @qcom_glink. + */ +struct glink_defer_cmd { + struct list_head node; + + struct glink_msg msg; + u8 data[]; +}; + +/** + * struct glink_core_rx_intent - RX intent + * RX intent + * + * data: pointer to the data (may be NULL for zero-copy) + * id: remote or local intent ID + * size: size of the original intent (do not modify) + * reuse: To mark if the intent can be reused after first use + * in_use: To mark if intent is already in use for the channel + * offset: next write offset (initially 0) + */ +struct glink_core_rx_intent { + void *data; + u32 id; + size_t size; + bool reuse; + bool in_use; + u32 offset; + + struct list_head node; +}; + +/** + * struct qcom_glink - driver context, relates to one remote subsystem + * @dev: reference to the associated struct device + * @mbox_client: mailbox client + * @mbox_chan: mailbox channel + * @rx_pipe: pipe object for receive FIFO + * @tx_pipe: pipe object for transmit FIFO + * @irq: IRQ for signaling incoming events + * @rx_work: worker for handling received control messages + * @rx_lock: protects the @rx_queue + * @rx_queue: queue of received control messages to be processed in @rx_work + * @tx_lock: synchronizes operations on the tx fifo + * @idr_lock: synchronizes @lcids and @rcids modifications + * @lcids: idr of all channels with a known local channel id + * @rcids: idr of all channels with a known remote channel id + */ +struct qcom_glink { + struct device *dev; + + struct mbox_client mbox_client; + struct mbox_chan *mbox_chan; + + struct qcom_glink_pipe *rx_pipe; + struct qcom_glink_pipe *tx_pipe; + + int irq; + + struct work_struct rx_work; + spinlock_t rx_lock; + struct list_head rx_queue; + + struct mutex tx_lock; + + spinlock_t idr_lock; + struct idr lcids; + struct idr rcids; + unsigned long features; + + bool intentless; +}; + +enum { + GLINK_STATE_CLOSED, + GLINK_STATE_OPENING, + GLINK_STATE_OPEN, + GLINK_STATE_CLOSING, +}; + +/** + * struct glink_channel - internal representation of a channel + * @rpdev: rpdev reference, only used for primary endpoints + * @ept: rpmsg endpoint this channel is associated with + * @glink: qcom_glink context handle + * @refcount: refcount for the channel object + * @recv_lock: guard for @ept.cb + * @name: unique channel name/identifier + * @lcid: channel id, in local space + * @rcid: channel id, in remote space + * @intent_lock: lock for protection of @liids, @riids + * @liids: idr of all local intents + * @riids: idr of all remote intents + * @intent_work: worker responsible for transmitting rx_done packets + * @done_intents: list of intents that needs to be announced rx_done + * @buf: receive buffer, for gathering fragments + * @buf_offset: write offset in @buf + * @buf_size: size of current @buf + * @open_ack: completed once remote has acked the open-request + * @open_req: completed once open-request has been received + * @intent_req_lock: Synchronises multiple intent requests + * @intent_req_result: Result of intent request + * @intent_req_comp: Completion for intent_req signalling + */ +struct glink_channel { + struct rpmsg_endpoint ept; + + struct rpmsg_device *rpdev; + struct qcom_glink *glink; + + struct kref refcount; + + spinlock_t recv_lock; + + char *name; + unsigned int lcid; + unsigned int rcid; + + spinlock_t intent_lock; + struct idr liids; + struct idr riids; + struct work_struct intent_work; + struct list_head done_intents; + + struct glink_core_rx_intent *buf; + int buf_offset; + int buf_size; + + struct completion open_ack; + struct completion open_req; + + struct mutex intent_req_lock; + bool intent_req_result; + struct completion intent_req_comp; +}; + +#define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) + +static const struct rpmsg_endpoint_ops glink_endpoint_ops; + +#define RPM_CMD_VERSION 0 +#define RPM_CMD_VERSION_ACK 1 +#define RPM_CMD_OPEN 2 +#define RPM_CMD_CLOSE 3 +#define RPM_CMD_OPEN_ACK 4 +#define RPM_CMD_INTENT 5 +#define RPM_CMD_RX_DONE 6 +#define RPM_CMD_RX_INTENT_REQ 7 +#define RPM_CMD_RX_INTENT_REQ_ACK 8 +#define RPM_CMD_TX_DATA 9 +#define RPM_CMD_CLOSE_ACK 11 +#define RPM_CMD_TX_DATA_CONT 12 +#define RPM_CMD_READ_NOTIF 13 +#define RPM_CMD_RX_DONE_W_REUSE 14 + +#define GLINK_FEATURE_INTENTLESS BIT(1) + +static void qcom_glink_rx_done_work(struct work_struct *work); + +static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, + const char *name) +{ + struct glink_channel *channel; + + channel = kzalloc(sizeof(*channel), GFP_KERNEL); + if (!channel) + return ERR_PTR(-ENOMEM); + + /* Setup glink internal glink_channel data */ + spin_lock_init(&channel->recv_lock); + spin_lock_init(&channel->intent_lock); + + channel->glink = glink; + channel->name = kstrdup(name, GFP_KERNEL); + + init_completion(&channel->open_req); + init_completion(&channel->open_ack); + + INIT_LIST_HEAD(&channel->done_intents); + INIT_WORK(&channel->intent_work, qcom_glink_rx_done_work); + + idr_init(&channel->liids); + idr_init(&channel->riids); + kref_init(&channel->refcount); + + return channel; +} + +static void qcom_glink_channel_release(struct kref *ref) +{ + struct glink_channel *channel = container_of(ref, struct glink_channel, + refcount); + unsigned long flags; + + spin_lock_irqsave(&channel->intent_lock, flags); + idr_destroy(&channel->liids); + idr_destroy(&channel->riids); + spin_unlock_irqrestore(&channel->intent_lock, flags); + + kfree(channel->name); + kfree(channel); +} + +static size_t qcom_glink_rx_avail(struct qcom_glink *glink) +{ + return glink->rx_pipe->avail(glink->rx_pipe); +} + +static void qcom_glink_rx_peak(struct qcom_glink *glink, + void *data, unsigned int offset, size_t count) +{ + glink->rx_pipe->peak(glink->rx_pipe, data, offset, count); +} + +static void qcom_glink_rx_advance(struct qcom_glink *glink, size_t count) +{ + glink->rx_pipe->advance(glink->rx_pipe, count); +} + +static size_t qcom_glink_tx_avail(struct qcom_glink *glink) +{ + return glink->tx_pipe->avail(glink->tx_pipe); +} + +static void qcom_glink_tx_write(struct qcom_glink *glink, + const void *hdr, size_t hlen, + const void *data, size_t dlen) +{ + glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen); +} + +static int qcom_glink_tx(struct qcom_glink *glink, + const void *hdr, size_t hlen, + const void *data, size_t dlen, bool wait) +{ + unsigned int tlen = hlen + dlen; + int ret; + + /* Reject packets that are too big */ + if (tlen >= glink->tx_pipe->length) + return -EINVAL; + + ret = mutex_lock_interruptible(&glink->tx_lock); + if (ret) + return ret; + + while (qcom_glink_tx_avail(glink) < tlen) { + if (!wait) { + ret = -EAGAIN; + goto out; + } + + usleep_range(10000, 15000); + } + + qcom_glink_tx_write(glink, hdr, hlen, data, dlen); + + mbox_send_message(glink->mbox_chan, NULL); + mbox_client_txdone(glink->mbox_chan, 0); + +out: + mutex_unlock(&glink->tx_lock); + + return ret; +} + +static int qcom_glink_send_version(struct qcom_glink *glink) +{ + struct glink_msg msg; + + msg.cmd = cpu_to_le16(RPM_CMD_VERSION); + msg.param1 = cpu_to_le16(GLINK_VERSION_1); + msg.param2 = cpu_to_le32(glink->features); + + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static void qcom_glink_send_version_ack(struct qcom_glink *glink) +{ + struct glink_msg msg; + + msg.cmd = cpu_to_le16(RPM_CMD_VERSION_ACK); + msg.param1 = cpu_to_le16(GLINK_VERSION_1); + msg.param2 = cpu_to_le32(glink->features); + + qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static void qcom_glink_send_open_ack(struct qcom_glink *glink, + struct glink_channel *channel) +{ + struct glink_msg msg; + + msg.cmd = cpu_to_le16(RPM_CMD_OPEN_ACK); + msg.param1 = cpu_to_le16(channel->rcid); + msg.param2 = cpu_to_le32(0); + + qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static void qcom_glink_handle_intent_req_ack(struct qcom_glink *glink, + unsigned int cid, bool granted) +{ + struct glink_channel *channel; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, cid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "unable to find channel\n"); + return; + } + + channel->intent_req_result = granted; + complete(&channel->intent_req_comp); +} + +/** + * qcom_glink_send_open_req() - send a RPM_CMD_OPEN request to the remote + * @glink: Ptr to the glink edge + * @channel: Ptr to the channel that the open req is sent + * + * Allocates a local channel id and sends a RPM_CMD_OPEN message to the remote. + * Will return with refcount held, regardless of outcome. + * + * Returns 0 on success, negative errno otherwise. + */ +static int qcom_glink_send_open_req(struct qcom_glink *glink, + struct glink_channel *channel) +{ + struct { + struct glink_msg msg; + u8 name[GLINK_NAME_SIZE]; + } __packed req; + int name_len = strlen(channel->name) + 1; + int req_len = ALIGN(sizeof(req.msg) + name_len, 8); + int ret; + unsigned long flags; + + kref_get(&channel->refcount); + + spin_lock_irqsave(&glink->idr_lock, flags); + ret = idr_alloc_cyclic(&glink->lcids, channel, + RPM_GLINK_CID_MIN, RPM_GLINK_CID_MAX, + GFP_ATOMIC); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (ret < 0) + return ret; + + channel->lcid = ret; + + req.msg.cmd = cpu_to_le16(RPM_CMD_OPEN); + req.msg.param1 = cpu_to_le16(channel->lcid); + req.msg.param2 = cpu_to_le32(name_len); + strcpy(req.name, channel->name); + + ret = qcom_glink_tx(glink, &req, req_len, NULL, 0, true); + if (ret) + goto remove_idr; + + return 0; + +remove_idr: + spin_lock_irqsave(&glink->idr_lock, flags); + idr_remove(&glink->lcids, channel->lcid); + channel->lcid = 0; + spin_unlock_irqrestore(&glink->idr_lock, flags); + + return ret; +} + +static void qcom_glink_send_close_req(struct qcom_glink *glink, + struct glink_channel *channel) +{ + struct glink_msg req; + + req.cmd = cpu_to_le16(RPM_CMD_CLOSE); + req.param1 = cpu_to_le16(channel->lcid); + req.param2 = 0; + + qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true); +} + +static void qcom_glink_send_close_ack(struct qcom_glink *glink, + unsigned int rcid) +{ + struct glink_msg req; + + req.cmd = cpu_to_le16(RPM_CMD_CLOSE_ACK); + req.param1 = cpu_to_le16(rcid); + req.param2 = 0; + + qcom_glink_tx(glink, &req, sizeof(req), NULL, 0, true); +} + +static void qcom_glink_rx_done_work(struct work_struct *work) +{ + struct glink_channel *channel = container_of(work, struct glink_channel, + intent_work); + struct qcom_glink *glink = channel->glink; + struct glink_core_rx_intent *intent, *tmp; + struct { + u16 id; + u16 lcid; + u32 liid; + } __packed cmd; + + unsigned int cid = channel->lcid; + unsigned int iid; + bool reuse; + unsigned long flags; + + spin_lock_irqsave(&channel->intent_lock, flags); + list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) { + list_del(&intent->node); + spin_unlock_irqrestore(&channel->intent_lock, flags); + iid = intent->id; + reuse = intent->reuse; + + cmd.id = reuse ? RPM_CMD_RX_DONE_W_REUSE : RPM_CMD_RX_DONE; + cmd.lcid = cid; + cmd.liid = iid; + + qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); + if (!reuse) { + kfree(intent->data); + kfree(intent); + } + spin_lock_irqsave(&channel->intent_lock, flags); + } + spin_unlock_irqrestore(&channel->intent_lock, flags); +} + +static void qcom_glink_rx_done(struct qcom_glink *glink, + struct glink_channel *channel, + struct glink_core_rx_intent *intent) +{ + /* We don't send RX_DONE to intentless systems */ + if (glink->intentless) { + kfree(intent->data); + kfree(intent); + return; + } + + /* Take it off the tree of receive intents */ + if (!intent->reuse) { + spin_lock(&channel->intent_lock); + idr_remove(&channel->liids, intent->id); + spin_unlock(&channel->intent_lock); + } + + /* Schedule the sending of a rx_done indication */ + spin_lock(&channel->intent_lock); + list_add_tail(&intent->node, &channel->done_intents); + spin_unlock(&channel->intent_lock); + + schedule_work(&channel->intent_work); +} + +/** + * qcom_glink_receive_version() - receive version/features from remote system + * + * @glink: pointer to transport interface + * @r_version: remote version + * @r_features: remote features + * + * This function is called in response to a remote-initiated version/feature + * negotiation sequence. + */ +static void qcom_glink_receive_version(struct qcom_glink *glink, + u32 version, + u32 features) +{ + switch (version) { + case 0: + break; + case GLINK_VERSION_1: + glink->features &= features; + /* FALLTHROUGH */ + default: + qcom_glink_send_version_ack(glink); + break; + } +} + +/** + * qcom_glink_receive_version_ack() - receive negotiation ack from remote system + * + * @glink: pointer to transport interface + * @r_version: remote version response + * @r_features: remote features response + * + * This function is called in response to a local-initiated version/feature + * negotiation sequence and is the counter-offer from the remote side based + * upon the initial version and feature set requested. + */ +static void qcom_glink_receive_version_ack(struct qcom_glink *glink, + u32 version, + u32 features) +{ + switch (version) { + case 0: + /* Version negotiation failed */ + break; + case GLINK_VERSION_1: + if (features == glink->features) + break; + + glink->features &= features; + /* FALLTHROUGH */ + default: + qcom_glink_send_version(glink); + break; + } +} + +/** + * qcom_glink_send_intent_req_ack() - convert an rx intent request ack cmd to + wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @granted: The request response to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_send_intent_req_ack(struct qcom_glink *glink, + struct glink_channel *channel, + bool granted) +{ + struct glink_msg msg; + + msg.cmd = cpu_to_le16(RPM_CMD_RX_INTENT_REQ_ACK); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(granted); + + qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); + + return 0; +} + +/** + * qcom_glink_advertise_intent - convert an rx intent cmd to wire format and + * transmit + * @glink: The transport to transmit on. + * @channel: The local channel + * @size: The intent to pass on to remote. + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_advertise_intent(struct qcom_glink *glink, + struct glink_channel *channel, + struct glink_core_rx_intent *intent) +{ + struct command { + u16 id; + u16 lcid; + u32 count; + u32 size; + u32 liid; + } __packed; + struct command cmd; + + cmd.id = cpu_to_le16(RPM_CMD_INTENT); + cmd.lcid = cpu_to_le16(channel->lcid); + cmd.count = cpu_to_le32(1); + cmd.size = cpu_to_le32(intent->size); + cmd.liid = cpu_to_le32(intent->id); + + qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); + + return 0; +} + +static struct glink_core_rx_intent * +qcom_glink_alloc_intent(struct qcom_glink *glink, + struct glink_channel *channel, + size_t size, + bool reuseable) +{ + struct glink_core_rx_intent *intent; + int ret; + unsigned long flags; + + intent = kzalloc(sizeof(*intent), GFP_KERNEL); + + if (!intent) + return NULL; + + intent->data = kzalloc(size, GFP_KERNEL); + if (!intent->data) + return NULL; + + spin_lock_irqsave(&channel->intent_lock, flags); + ret = idr_alloc_cyclic(&channel->liids, intent, 1, -1, GFP_ATOMIC); + if (ret < 0) { + spin_unlock_irqrestore(&channel->intent_lock, flags); + return NULL; + } + spin_unlock_irqrestore(&channel->intent_lock, flags); + + intent->id = ret; + intent->size = size; + intent->reuse = reuseable; + + return intent; +} + +static void qcom_glink_handle_rx_done(struct qcom_glink *glink, + u32 cid, uint32_t iid, + bool reuse) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, cid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "invalid channel id received\n"); + return; + } + + spin_lock_irqsave(&channel->intent_lock, flags); + intent = idr_find(&channel->riids, iid); + + if (!intent) { + spin_unlock_irqrestore(&channel->intent_lock, flags); + dev_err(glink->dev, "invalid intent id received\n"); + return; + } + + intent->in_use = false; + + if (!reuse) { + idr_remove(&channel->riids, intent->id); + kfree(intent); + } + spin_unlock_irqrestore(&channel->intent_lock, flags); +} + +/** + * qcom_glink_handle_intent_req() - Receive a request for rx_intent + * from remote side + * if_ptr: Pointer to the transport interface + * rcid: Remote channel ID + * size: size of the intent + * + * The function searches for the local channel to which the request for + * rx_intent has arrived and allocates and notifies the remote back + */ +static void qcom_glink_handle_intent_req(struct qcom_glink *glink, + u32 cid, size_t size) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, cid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + + if (!channel) { + pr_err("%s channel not found for cid %d\n", __func__, cid); + return; + } + + intent = qcom_glink_alloc_intent(glink, channel, size, false); + if (intent) + qcom_glink_advertise_intent(glink, channel, intent); + + qcom_glink_send_intent_req_ack(glink, channel, !!intent); +} + +static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra) +{ + struct glink_defer_cmd *dcmd; + + extra = ALIGN(extra, 8); + + if (qcom_glink_rx_avail(glink) < sizeof(struct glink_msg) + extra) { + dev_dbg(glink->dev, "Insufficient data in rx fifo"); + return -ENXIO; + } + + dcmd = kzalloc(sizeof(*dcmd) + extra, GFP_ATOMIC); + if (!dcmd) + return -ENOMEM; + + INIT_LIST_HEAD(&dcmd->node); + + qcom_glink_rx_peak(glink, &dcmd->msg, 0, sizeof(dcmd->msg) + extra); + + spin_lock(&glink->rx_lock); + list_add_tail(&dcmd->node, &glink->rx_queue); + spin_unlock(&glink->rx_lock); + + schedule_work(&glink->rx_work); + qcom_glink_rx_advance(glink, sizeof(dcmd->msg) + extra); + + return 0; +} + +static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + struct { + struct glink_msg msg; + __le32 chunk_size; + __le32 left_size; + } __packed hdr; + unsigned int chunk_size; + unsigned int left_size; + unsigned int rcid; + unsigned int liid; + int ret = 0; + unsigned long flags; + + if (avail < sizeof(hdr)) { + dev_dbg(glink->dev, "Not enough data in fifo\n"); + return -EAGAIN; + } + + qcom_glink_rx_peak(glink, &hdr, 0, sizeof(hdr)); + chunk_size = le32_to_cpu(hdr.chunk_size); + left_size = le32_to_cpu(hdr.left_size); + + if (avail < sizeof(hdr) + chunk_size) { + dev_dbg(glink->dev, "Payload not yet in fifo\n"); + return -EAGAIN; + } + + if (WARN(chunk_size % 4, "Incoming data must be word aligned\n")) + return -EINVAL; + + rcid = le16_to_cpu(hdr.msg.param1); + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_dbg(glink->dev, "Data on non-existing channel\n"); + + /* Drop the message */ + goto advance_rx; + } + + if (glink->intentless) { + /* Might have an ongoing, fragmented, message to append */ + if (!channel->buf) { + intent = kzalloc(sizeof(*intent), GFP_ATOMIC); + if (!intent) + return -ENOMEM; + + intent->data = kmalloc(chunk_size + left_size, + GFP_ATOMIC); + if (!intent->data) { + kfree(intent); + return -ENOMEM; + } + + intent->id = 0xbabababa; + intent->size = chunk_size + left_size; + intent->offset = 0; + + channel->buf = intent; + } else { + intent = channel->buf; + } + } else { + liid = le32_to_cpu(hdr.msg.param2); + + spin_lock_irqsave(&channel->intent_lock, flags); + intent = idr_find(&channel->liids, liid); + spin_unlock_irqrestore(&channel->intent_lock, flags); + + if (!intent) { + dev_err(glink->dev, + "no intent found for channel %s intent %d", + channel->name, liid); + goto advance_rx; + } + } + + if (intent->size - intent->offset < chunk_size) { + dev_err(glink->dev, "Insufficient space in intent\n"); + + /* The packet header lied, drop payload */ + goto advance_rx; + } + + qcom_glink_rx_peak(glink, intent->data + intent->offset, + sizeof(hdr), chunk_size); + intent->offset += chunk_size; + + /* Handle message when no fragments remain to be received */ + if (!left_size) { + spin_lock(&channel->recv_lock); + if (channel->ept.cb) { + channel->ept.cb(channel->ept.rpdev, + intent->data, + intent->offset, + channel->ept.priv, + RPMSG_ADDR_ANY); + } + spin_unlock(&channel->recv_lock); + + intent->offset = 0; + channel->buf = NULL; + + qcom_glink_rx_done(glink, channel, intent); + } + +advance_rx: + qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr) + chunk_size, 8)); + + return ret; +} + +static void qcom_glink_handle_intent(struct qcom_glink *glink, + unsigned int cid, + unsigned int count, + size_t avail) +{ + struct glink_core_rx_intent *intent; + struct glink_channel *channel; + struct intent_pair { + __le32 size; + __le32 iid; + }; + + struct { + struct glink_msg msg; + struct intent_pair intents[]; + } __packed * msg; + + const size_t msglen = sizeof(*msg) + sizeof(struct intent_pair) * count; + int ret; + int i; + unsigned long flags; + + if (avail < msglen) { + dev_dbg(glink->dev, "Not enough data in fifo\n"); + return; + } + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, cid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "intents for non-existing channel\n"); + return; + } + + msg = kmalloc(msglen, GFP_ATOMIC); + if (!msg) + return; + + qcom_glink_rx_peak(glink, msg, 0, msglen); + + for (i = 0; i < count; ++i) { + intent = kzalloc(sizeof(*intent), GFP_ATOMIC); + if (!intent) + break; + + intent->id = le32_to_cpu(msg->intents[i].iid); + intent->size = le32_to_cpu(msg->intents[i].size); + + spin_lock_irqsave(&channel->intent_lock, flags); + ret = idr_alloc(&channel->riids, intent, + intent->id, intent->id + 1, GFP_ATOMIC); + spin_unlock_irqrestore(&channel->intent_lock, flags); + + if (ret < 0) + dev_err(glink->dev, "failed to store remote intent\n"); + } + + kfree(msg); + qcom_glink_rx_advance(glink, ALIGN(msglen, 8)); +} + +static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) +{ + struct glink_channel *channel; + + spin_lock(&glink->idr_lock); + channel = idr_find(&glink->lcids, lcid); + spin_unlock(&glink->idr_lock); + if (!channel) { + dev_err(glink->dev, "Invalid open ack packet\n"); + return -EINVAL; + } + + complete(&channel->open_ack); + + return 0; +} + +static irqreturn_t qcom_glink_native_intr(int irq, void *data) +{ + struct qcom_glink *glink = data; + struct glink_msg msg; + unsigned int param1; + unsigned int param2; + unsigned int avail; + unsigned int cmd; + int ret = 0; + + for (;;) { + avail = qcom_glink_rx_avail(glink); + if (avail < sizeof(msg)) + break; + + qcom_glink_rx_peak(glink, &msg, 0, sizeof(msg)); + + cmd = le16_to_cpu(msg.cmd); + param1 = le16_to_cpu(msg.param1); + param2 = le32_to_cpu(msg.param2); + + switch (cmd) { + case RPM_CMD_VERSION: + case RPM_CMD_VERSION_ACK: + case RPM_CMD_CLOSE: + case RPM_CMD_CLOSE_ACK: + case RPM_CMD_RX_INTENT_REQ: + ret = qcom_glink_rx_defer(glink, 0); + break; + case RPM_CMD_OPEN_ACK: + ret = qcom_glink_rx_open_ack(glink, param1); + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + break; + case RPM_CMD_OPEN: + ret = qcom_glink_rx_defer(glink, param2); + break; + case RPM_CMD_TX_DATA: + case RPM_CMD_TX_DATA_CONT: + ret = qcom_glink_rx_data(glink, avail); + break; + case RPM_CMD_READ_NOTIF: + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + + mbox_send_message(glink->mbox_chan, NULL); + mbox_client_txdone(glink->mbox_chan, 0); + break; + case RPM_CMD_INTENT: + qcom_glink_handle_intent(glink, param1, param2, avail); + break; + case RPM_CMD_RX_DONE: + qcom_glink_handle_rx_done(glink, param1, param2, false); + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + break; + case RPM_CMD_RX_DONE_W_REUSE: + qcom_glink_handle_rx_done(glink, param1, param2, true); + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + break; + case RPM_CMD_RX_INTENT_REQ_ACK: + qcom_glink_handle_intent_req_ack(glink, param1, param2); + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + break; + default: + dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd); + ret = -EINVAL; + break; + } + + if (ret) + break; + } + + return IRQ_HANDLED; +} + +/* Locally initiated rpmsg_create_ept */ +static struct glink_channel *qcom_glink_create_local(struct qcom_glink *glink, + const char *name) +{ + struct glink_channel *channel; + int ret; + unsigned long flags; + + channel = qcom_glink_alloc_channel(glink, name); + if (IS_ERR(channel)) + return ERR_CAST(channel); + + ret = qcom_glink_send_open_req(glink, channel); + if (ret) + goto release_channel; + + ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ); + if (!ret) + goto err_timeout; + + ret = wait_for_completion_timeout(&channel->open_req, 5 * HZ); + if (!ret) + goto err_timeout; + + qcom_glink_send_open_ack(glink, channel); + + return channel; + +err_timeout: + /* qcom_glink_send_open_req() did register the channel in lcids*/ + spin_lock_irqsave(&glink->idr_lock, flags); + idr_remove(&glink->lcids, channel->lcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + +release_channel: + /* Release qcom_glink_send_open_req() reference */ + kref_put(&channel->refcount, qcom_glink_channel_release); + /* Release qcom_glink_alloc_channel() reference */ + kref_put(&channel->refcount, qcom_glink_channel_release); + + return ERR_PTR(-ETIMEDOUT); +} + +/* Remote initiated rpmsg_create_ept */ +static int qcom_glink_create_remote(struct qcom_glink *glink, + struct glink_channel *channel) +{ + int ret; + + qcom_glink_send_open_ack(glink, channel); + + ret = qcom_glink_send_open_req(glink, channel); + if (ret) + goto close_link; + + ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ); + if (!ret) { + ret = -ETIMEDOUT; + goto close_link; + } + + return 0; + +close_link: + /* + * Send a close request to "undo" our open-ack. The close-ack will + * release the last reference. + */ + qcom_glink_send_close_req(glink, channel); + + /* Release qcom_glink_send_open_req() reference */ + kref_put(&channel->refcount, qcom_glink_channel_release); + + return ret; +} + +static struct rpmsg_endpoint *qcom_glink_create_ept(struct rpmsg_device *rpdev, + rpmsg_rx_cb_t cb, + void *priv, + struct rpmsg_channel_info + chinfo) +{ + struct glink_channel *parent = to_glink_channel(rpdev->ept); + struct glink_channel *channel; + struct qcom_glink *glink = parent->glink; + struct rpmsg_endpoint *ept; + const char *name = chinfo.name; + int cid; + int ret; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + idr_for_each_entry(&glink->rcids, channel, cid) { + if (!strcmp(channel->name, name)) + break; + } + spin_unlock_irqrestore(&glink->idr_lock, flags); + + if (!channel) { + channel = qcom_glink_create_local(glink, name); + if (IS_ERR(channel)) + return NULL; + } else { + ret = qcom_glink_create_remote(glink, channel); + if (ret) + return NULL; + } + + ept = &channel->ept; + ept->rpdev = rpdev; + ept->cb = cb; + ept->priv = priv; + ept->ops = &glink_endpoint_ops; + + return ept; +} + +static int qcom_glink_announce_create(struct rpmsg_device *rpdev) +{ + struct glink_channel *channel = to_glink_channel(rpdev->ept); + struct glink_core_rx_intent *intent; + struct qcom_glink *glink = channel->glink; + int num_intents = glink->intentless ? 0 : 5; + + /* Channel is now open, advertise base set of intents */ + while (num_intents--) { + intent = qcom_glink_alloc_intent(glink, channel, SZ_1K, true); + if (!intent) + break; + + qcom_glink_advertise_intent(glink, channel, intent); + } + + return 0; +} + +static void qcom_glink_destroy_ept(struct rpmsg_endpoint *ept) +{ + struct glink_channel *channel = to_glink_channel(ept); + struct qcom_glink *glink = channel->glink; + unsigned long flags; + + spin_lock_irqsave(&channel->recv_lock, flags); + channel->ept.cb = NULL; + spin_unlock_irqrestore(&channel->recv_lock, flags); + + /* Decouple the potential rpdev from the channel */ + channel->rpdev = NULL; + + qcom_glink_send_close_req(glink, channel); +} + +static int qcom_glink_request_intent(struct qcom_glink *glink, + struct glink_channel *channel, + size_t size) +{ + struct { + u16 id; + u16 cid; + u32 size; + } __packed cmd; + + int ret; + + mutex_lock(&channel->intent_req_lock); + + reinit_completion(&channel->intent_req_comp); + + cmd.id = RPM_CMD_RX_INTENT_REQ; + cmd.cid = channel->lcid; + cmd.size = size; + + ret = qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&channel->intent_req_comp, 10 * HZ); + if (!ret) { + dev_err(glink->dev, "intent request timed out\n"); + ret = -ETIMEDOUT; + } else { + ret = channel->intent_req_result ? 0 : -ECANCELED; + } + + mutex_unlock(&channel->intent_req_lock); + return ret; +} + +static int __qcom_glink_send(struct glink_channel *channel, + void *data, int len, bool wait) +{ + struct qcom_glink *glink = channel->glink; + struct glink_core_rx_intent *intent = NULL; + struct glink_core_rx_intent *tmp; + int iid = 0; + struct { + struct glink_msg msg; + __le32 chunk_size; + __le32 left_size; + } __packed req; + int ret; + unsigned long flags; + + if (!glink->intentless) { + while (!intent) { + spin_lock_irqsave(&channel->intent_lock, flags); + idr_for_each_entry(&channel->riids, tmp, iid) { + if (tmp->size >= len && !tmp->in_use) { + tmp->in_use = true; + intent = tmp; + break; + } + } + spin_unlock_irqrestore(&channel->intent_lock, flags); + + /* We found an available intent */ + if (intent) + break; + + if (!wait) + return -EBUSY; + + ret = qcom_glink_request_intent(glink, channel, len); + if (ret < 0) + return ret; + } + + iid = intent->id; + } + + req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA); + req.msg.param1 = cpu_to_le16(channel->lcid); + req.msg.param2 = cpu_to_le32(iid); + req.chunk_size = cpu_to_le32(len); + req.left_size = cpu_to_le32(0); + + ret = qcom_glink_tx(glink, &req, sizeof(req), data, len, wait); + + /* Mark intent available if we failed */ + if (ret && intent) + intent->in_use = false; + + return ret; +} + +static int qcom_glink_send(struct rpmsg_endpoint *ept, void *data, int len) +{ + struct glink_channel *channel = to_glink_channel(ept); + + return __qcom_glink_send(channel, data, len, true); +} + +static int qcom_glink_trysend(struct rpmsg_endpoint *ept, void *data, int len) +{ + struct glink_channel *channel = to_glink_channel(ept); + + return __qcom_glink_send(channel, data, len, false); +} + +/* + * Finds the device_node for the glink child interested in this channel. + */ +static struct device_node *qcom_glink_match_channel(struct device_node *node, + const char *channel) +{ + struct device_node *child; + const char *name; + const char *key; + int ret; + + for_each_available_child_of_node(node, child) { + key = "qcom,glink-channels"; + ret = of_property_read_string(child, key, &name); + if (ret) + continue; + + if (strcmp(name, channel) == 0) + return child; + } + + return NULL; +} + +static const struct rpmsg_device_ops glink_device_ops = { + .create_ept = qcom_glink_create_ept, + .announce_create = qcom_glink_announce_create, +}; + +static const struct rpmsg_endpoint_ops glink_endpoint_ops = { + .destroy_ept = qcom_glink_destroy_ept, + .send = qcom_glink_send, + .trysend = qcom_glink_trysend, +}; + +static void qcom_glink_rpdev_release(struct device *dev) +{ + struct rpmsg_device *rpdev = to_rpmsg_device(dev); + struct glink_channel *channel = to_glink_channel(rpdev->ept); + + channel->rpdev = NULL; + kfree(rpdev); +} + +static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, + char *name) +{ + struct glink_channel *channel; + struct rpmsg_device *rpdev; + bool create_device = false; + struct device_node *node; + int lcid; + int ret; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + idr_for_each_entry(&glink->lcids, channel, lcid) { + if (!strcmp(channel->name, name)) + break; + } + spin_unlock_irqrestore(&glink->idr_lock, flags); + + if (!channel) { + channel = qcom_glink_alloc_channel(glink, name); + if (IS_ERR(channel)) + return PTR_ERR(channel); + + /* The opening dance was initiated by the remote */ + create_device = true; + } + + spin_lock_irqsave(&glink->idr_lock, flags); + ret = idr_alloc(&glink->rcids, channel, rcid, rcid + 1, GFP_ATOMIC); + if (ret < 0) { + dev_err(glink->dev, "Unable to insert channel into rcid list\n"); + spin_unlock_irqrestore(&glink->idr_lock, flags); + goto free_channel; + } + channel->rcid = ret; + spin_unlock_irqrestore(&glink->idr_lock, flags); + + complete(&channel->open_req); + + if (create_device) { + rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); + if (!rpdev) { + ret = -ENOMEM; + goto rcid_remove; + } + + rpdev->ept = &channel->ept; + strncpy(rpdev->id.name, name, RPMSG_NAME_SIZE); + rpdev->src = RPMSG_ADDR_ANY; + rpdev->dst = RPMSG_ADDR_ANY; + rpdev->ops = &glink_device_ops; + + node = qcom_glink_match_channel(glink->dev->of_node, name); + rpdev->dev.of_node = node; + rpdev->dev.parent = glink->dev; + rpdev->dev.release = qcom_glink_rpdev_release; + + ret = rpmsg_register_device(rpdev); + if (ret) + goto free_rpdev; + + channel->rpdev = rpdev; + } + + return 0; + +free_rpdev: + kfree(rpdev); +rcid_remove: + spin_lock_irqsave(&glink->idr_lock, flags); + idr_remove(&glink->rcids, channel->rcid); + channel->rcid = 0; + spin_unlock_irqrestore(&glink->idr_lock, flags); +free_channel: + /* Release the reference, iff we took it */ + if (create_device) + kref_put(&channel->refcount, qcom_glink_channel_release); + + return ret; +} + +static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid) +{ + struct rpmsg_channel_info chinfo; + struct glink_channel *channel; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (WARN(!channel, "close request on unknown channel\n")) + return; + + /* cancel pending rx_done work */ + cancel_work_sync(&channel->intent_work); + + if (channel->rpdev) { + strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = RPMSG_ADDR_ANY; + + rpmsg_unregister_device(glink->dev, &chinfo); + } + + qcom_glink_send_close_ack(glink, channel->rcid); + + spin_lock_irqsave(&glink->idr_lock, flags); + idr_remove(&glink->rcids, channel->rcid); + channel->rcid = 0; + spin_unlock_irqrestore(&glink->idr_lock, flags); + + kref_put(&channel->refcount, qcom_glink_channel_release); +} + +static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid) +{ + struct glink_channel *channel; + unsigned long flags; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->lcids, lcid); + if (WARN(!channel, "close ack on unknown channel\n")) { + spin_unlock_irqrestore(&glink->idr_lock, flags); + return; + } + + idr_remove(&glink->lcids, channel->lcid); + channel->lcid = 0; + spin_unlock_irqrestore(&glink->idr_lock, flags); + + kref_put(&channel->refcount, qcom_glink_channel_release); +} + +static void qcom_glink_work(struct work_struct *work) +{ + struct qcom_glink *glink = container_of(work, struct qcom_glink, + rx_work); + struct glink_defer_cmd *dcmd; + struct glink_msg *msg; + unsigned long flags; + unsigned int param1; + unsigned int param2; + unsigned int cmd; + + for (;;) { + spin_lock_irqsave(&glink->rx_lock, flags); + if (list_empty(&glink->rx_queue)) { + spin_unlock_irqrestore(&glink->rx_lock, flags); + break; + } + dcmd = list_first_entry(&glink->rx_queue, + struct glink_defer_cmd, node); + list_del(&dcmd->node); + spin_unlock_irqrestore(&glink->rx_lock, flags); + + msg = &dcmd->msg; + cmd = le16_to_cpu(msg->cmd); + param1 = le16_to_cpu(msg->param1); + param2 = le32_to_cpu(msg->param2); + + switch (cmd) { + case RPM_CMD_VERSION: + qcom_glink_receive_version(glink, param1, param2); + break; + case RPM_CMD_VERSION_ACK: + qcom_glink_receive_version_ack(glink, param1, param2); + break; + case RPM_CMD_OPEN: + qcom_glink_rx_open(glink, param1, msg->data); + break; + case RPM_CMD_CLOSE: + qcom_glink_rx_close(glink, param1); + break; + case RPM_CMD_CLOSE_ACK: + qcom_glink_rx_close_ack(glink, param1); + break; + case RPM_CMD_RX_INTENT_REQ: + qcom_glink_handle_intent_req(glink, param1, param2); + break; + default: + WARN(1, "Unknown defer object %d\n", cmd); + break; + } + + kfree(dcmd); + } +} + +struct qcom_glink *qcom_glink_native_probe(struct device *dev, + unsigned long features, + struct qcom_glink_pipe *rx, + struct qcom_glink_pipe *tx, + bool intentless) +{ + int irq; + int ret; + struct qcom_glink *glink; + + glink = devm_kzalloc(dev, sizeof(*glink), GFP_KERNEL); + if (!glink) + return ERR_PTR(-ENOMEM); + + glink->dev = dev; + glink->tx_pipe = tx; + glink->rx_pipe = rx; + + glink->features = features; + glink->intentless = intentless; + + mutex_init(&glink->tx_lock); + spin_lock_init(&glink->rx_lock); + INIT_LIST_HEAD(&glink->rx_queue); + INIT_WORK(&glink->rx_work, qcom_glink_work); + + spin_lock_init(&glink->idr_lock); + idr_init(&glink->lcids); + idr_init(&glink->rcids); + + glink->mbox_client.dev = dev; + glink->mbox_chan = mbox_request_channel(&glink->mbox_client, 0); + if (IS_ERR(glink->mbox_chan)) { + if (PTR_ERR(glink->mbox_chan) != -EPROBE_DEFER) + dev_err(dev, "failed to acquire IPC channel\n"); + return ERR_CAST(glink->mbox_chan); + } + + irq = of_irq_get(dev->of_node, 0); + ret = devm_request_irq(dev, irq, + qcom_glink_native_intr, + IRQF_NO_SUSPEND | IRQF_SHARED, + "glink-native", glink); + if (ret) { + dev_err(dev, "failed to request IRQ\n"); + return ERR_PTR(ret); + } + + glink->irq = irq; + + ret = qcom_glink_send_version(glink); + if (ret) + return ERR_PTR(ret); + + return glink; +} +EXPORT_SYMBOL_GPL(qcom_glink_native_probe); + +static int qcom_glink_remove_device(struct device *dev, void *data) +{ + device_unregister(dev); + + return 0; +} + +void qcom_glink_native_remove(struct qcom_glink *glink) +{ + struct glink_channel *channel; + int cid; + int ret; + unsigned long flags; + + disable_irq(glink->irq); + cancel_work_sync(&glink->rx_work); + + ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device); + if (ret) + dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret); + + spin_lock_irqsave(&glink->idr_lock, flags); + /* Release any defunct local channels, waiting for close-ack */ + idr_for_each_entry(&glink->lcids, channel, cid) + kref_put(&channel->refcount, qcom_glink_channel_release); + + idr_destroy(&glink->lcids); + idr_destroy(&glink->rcids); + spin_unlock_irqrestore(&glink->idr_lock, flags); + mbox_free_channel(glink->mbox_chan); +} +EXPORT_SYMBOL_GPL(qcom_glink_native_remove); + +void qcom_glink_native_unregister(struct qcom_glink *glink) +{ + device_unregister(glink->dev); +} +EXPORT_SYMBOL_GPL(qcom_glink_native_unregister); diff --git a/drivers/rpmsg/qcom_glink_native.h b/drivers/rpmsg/qcom_glink_native.h new file mode 100644 index 000000000000..0cae8a8199f8 --- /dev/null +++ b/drivers/rpmsg/qcom_glink_native.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016-2017, Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __QCOM_GLINK_NATIVE_H__ +#define __QCOM_GLINK_NATIVE_H__ + +#define GLINK_FEATURE_INTENT_REUSE BIT(0) +#define GLINK_FEATURE_MIGRATION BIT(1) +#define GLINK_FEATURE_TRACER_PKT BIT(2) + +struct qcom_glink_pipe { + size_t length; + + size_t (*avail)(struct qcom_glink_pipe *glink_pipe); + + void (*peak)(struct qcom_glink_pipe *glink_pipe, void *data, + unsigned int offset, size_t count); + void (*advance)(struct qcom_glink_pipe *glink_pipe, size_t count); + + void (*write)(struct qcom_glink_pipe *glink_pipe, + const void *hdr, size_t hlen, + const void *data, size_t dlen); +}; + +struct qcom_glink; + +struct qcom_glink *qcom_glink_native_probe(struct device *dev, + unsigned long features, + struct qcom_glink_pipe *rx, + struct qcom_glink_pipe *tx, + bool intentless); +void qcom_glink_native_remove(struct qcom_glink *glink); + +void qcom_glink_native_unregister(struct qcom_glink *glink); +#endif diff --git a/drivers/rpmsg/qcom_glink_rpm.c b/drivers/rpmsg/qcom_glink_rpm.c index 3559a3e84c1e..69b25d157d0f 100644 --- a/drivers/rpmsg/qcom_glink_rpm.c +++ b/drivers/rpmsg/qcom_glink_rpm.c @@ -27,6 +27,7 @@ #include <linux/mailbox_client.h> #include "rpmsg_internal.h" +#include "qcom_glink_native.h" #define RPM_TOC_SIZE 256 #define RPM_TOC_MAGIC 0x67727430 /* grt0 */ @@ -36,10 +37,7 @@ #define RPM_TX_FIFO_ID 0x61703272 /* ap2r */ #define RPM_RX_FIFO_ID 0x72326170 /* r2ap */ -#define GLINK_NAME_SIZE 32 - -#define RPM_GLINK_CID_MIN 1 -#define RPM_GLINK_CID_MAX 65536 +#define to_rpm_pipe(p) container_of(p, struct glink_rpm_pipe, native) struct rpm_toc_entry { __le32 id; @@ -54,170 +52,18 @@ struct rpm_toc { struct rpm_toc_entry entries[]; } __packed; -struct glink_msg { - __le16 cmd; - __le16 param1; - __le32 param2; - u8 data[]; -} __packed; - struct glink_rpm_pipe { + struct qcom_glink_pipe native; + void __iomem *tail; void __iomem *head; void __iomem *fifo; - - size_t length; -}; - -/** - * struct glink_defer_cmd - deferred incoming control message - * @node: list node - * @msg: message header - * data: payload of the message - * - * Copy of a received control message, to be added to @rx_queue and processed - * by @rx_work of @glink_rpm. - */ -struct glink_defer_cmd { - struct list_head node; - - struct glink_msg msg; - u8 data[]; -}; - -/** - * struct glink_rpm - driver context, relates to one remote subsystem - * @dev: reference to the associated struct device - * @doorbell: "rpm_hlos" ipc doorbell - * @rx_pipe: pipe object for receive FIFO - * @tx_pipe: pipe object for transmit FIFO - * @irq: IRQ for signaling incoming events - * @rx_work: worker for handling received control messages - * @rx_lock: protects the @rx_queue - * @rx_queue: queue of received control messages to be processed in @rx_work - * @tx_lock: synchronizes operations on the tx fifo - * @idr_lock: synchronizes @lcids and @rcids modifications - * @lcids: idr of all channels with a known local channel id - * @rcids: idr of all channels with a known remote channel id - */ -struct glink_rpm { - struct device *dev; - - struct mbox_client mbox_client; - struct mbox_chan *mbox_chan; - - struct glink_rpm_pipe rx_pipe; - struct glink_rpm_pipe tx_pipe; - - int irq; - - struct work_struct rx_work; - spinlock_t rx_lock; - struct list_head rx_queue; - - struct mutex tx_lock; - - struct mutex idr_lock; - struct idr lcids; - struct idr rcids; -}; - -enum { - GLINK_STATE_CLOSED, - GLINK_STATE_OPENING, - GLINK_STATE_OPEN, - GLINK_STATE_CLOSING, }; -/** - * struct glink_channel - internal representation of a channel - * @rpdev: rpdev reference, only used for primary endpoints - * @ept: rpmsg endpoint this channel is associated with - * @glink: glink_rpm context handle - * @refcount: refcount for the channel object - * @recv_lock: guard for @ept.cb - * @name: unique channel name/identifier - * @lcid: channel id, in local space - * @rcid: channel id, in remote space - * @buf: receive buffer, for gathering fragments - * @buf_offset: write offset in @buf - * @buf_size: size of current @buf - * @open_ack: completed once remote has acked the open-request - * @open_req: completed once open-request has been received - */ -struct glink_channel { - struct rpmsg_endpoint ept; - - struct rpmsg_device *rpdev; - struct glink_rpm *glink; - - struct kref refcount; - - spinlock_t recv_lock; - - char *name; - unsigned int lcid; - unsigned int rcid; - - void *buf; - int buf_offset; - int buf_size; - - struct completion open_ack; - struct completion open_req; -}; - -#define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) - -static const struct rpmsg_endpoint_ops glink_endpoint_ops; - -#define RPM_CMD_VERSION 0 -#define RPM_CMD_VERSION_ACK 1 -#define RPM_CMD_OPEN 2 -#define RPM_CMD_CLOSE 3 -#define RPM_CMD_OPEN_ACK 4 -#define RPM_CMD_TX_DATA 9 -#define RPM_CMD_CLOSE_ACK 11 -#define RPM_CMD_TX_DATA_CONT 12 -#define RPM_CMD_READ_NOTIF 13 - -#define GLINK_FEATURE_INTENTLESS BIT(1) - -static struct glink_channel *glink_rpm_alloc_channel(struct glink_rpm *glink, - const char *name) -{ - struct glink_channel *channel; - - channel = kzalloc(sizeof(*channel), GFP_KERNEL); - if (!channel) - return ERR_PTR(-ENOMEM); - - /* Setup glink internal glink_channel data */ - spin_lock_init(&channel->recv_lock); - channel->glink = glink; - channel->name = kstrdup(name, GFP_KERNEL); - - init_completion(&channel->open_req); - init_completion(&channel->open_ack); - - kref_init(&channel->refcount); - - return channel; -} - -static void glink_rpm_channel_release(struct kref *ref) -{ - struct glink_channel *channel = container_of(ref, struct glink_channel, - refcount); - - kfree(channel->name); - kfree(channel); -} - -static size_t glink_rpm_rx_avail(struct glink_rpm *glink) +static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe) { - struct glink_rpm_pipe *pipe = &glink->rx_pipe; + struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); unsigned int head; unsigned int tail; @@ -225,21 +71,24 @@ static size_t glink_rpm_rx_avail(struct glink_rpm *glink) tail = readl(pipe->tail); if (head < tail) - return pipe->length - tail + head; + return pipe->native.length - tail + head; else return head - tail; } -static void glink_rpm_rx_peak(struct glink_rpm *glink, - void *data, size_t count) +static void glink_rpm_rx_peak(struct qcom_glink_pipe *glink_pipe, + void *data, unsigned int offset, size_t count) { - struct glink_rpm_pipe *pipe = &glink->rx_pipe; + struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); unsigned int tail; size_t len; tail = readl(pipe->tail); + tail += offset; + if (tail >= pipe->native.length) + tail -= pipe->native.length; - len = min_t(size_t, count, pipe->length - tail); + len = min_t(size_t, count, pipe->native.length - tail); if (len) { __ioread32_copy(data, pipe->fifo + tail, len / sizeof(u32)); @@ -251,24 +100,24 @@ static void glink_rpm_rx_peak(struct glink_rpm *glink, } } -static void glink_rpm_rx_advance(struct glink_rpm *glink, +static void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe, size_t count) { - struct glink_rpm_pipe *pipe = &glink->rx_pipe; + struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); unsigned int tail; tail = readl(pipe->tail); tail += count; - if (tail >= pipe->length) - tail -= pipe->length; + if (tail >= pipe->native.length) + tail -= pipe->native.length; writel(tail, pipe->tail); } -static size_t glink_rpm_tx_avail(struct glink_rpm *glink) +static size_t glink_rpm_tx_avail(struct qcom_glink_pipe *glink_pipe) { - struct glink_rpm_pipe *pipe = &glink->tx_pipe; + struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); unsigned int head; unsigned int tail; @@ -276,19 +125,18 @@ static size_t glink_rpm_tx_avail(struct glink_rpm *glink) tail = readl(pipe->tail); if (tail <= head) - return pipe->length - head + tail; + return pipe->native.length - head + tail; else return tail - head; } -static unsigned int glink_rpm_tx_write(struct glink_rpm *glink, - unsigned int head, - const void *data, size_t count) +static unsigned int glink_rpm_tx_write_one(struct glink_rpm_pipe *pipe, + unsigned int head, + const void *data, size_t count) { - struct glink_rpm_pipe *pipe = &glink->tx_pipe; size_t len; - len = min_t(size_t, count, pipe->length - head); + len = min_t(size_t, count, pipe->native.length - head); if (len) { __iowrite32_copy(pipe->fifo + head, data, len / sizeof(u32)); @@ -300,725 +148,43 @@ static unsigned int glink_rpm_tx_write(struct glink_rpm *glink, } head += count; - if (head >= pipe->length) - head -= pipe->length; + if (head >= pipe->native.length) + head -= pipe->native.length; return head; } -static int glink_rpm_tx(struct glink_rpm *glink, - const void *hdr, size_t hlen, - const void *data, size_t dlen, bool wait) +static void glink_rpm_tx_write(struct qcom_glink_pipe *glink_pipe, + const void *hdr, size_t hlen, + const void *data, size_t dlen) { - struct glink_rpm_pipe *pipe = &glink->tx_pipe; + struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe); + size_t tlen = hlen + dlen; + size_t aligned_dlen; unsigned int head; - unsigned int tlen = hlen + dlen; - int ret; - - /* Reject packets that are too big */ - if (tlen >= glink->tx_pipe.length) - return -EINVAL; - - if (WARN(tlen % 8, "Unaligned TX request")) - return -EINVAL; - - ret = mutex_lock_interruptible(&glink->tx_lock); - if (ret) - return ret; - - while (glink_rpm_tx_avail(glink) < tlen) { - if (!wait) { - ret = -ENOMEM; - goto out; - } - - msleep(10); - } - - head = readl(pipe->head); - head = glink_rpm_tx_write(glink, head, hdr, hlen); - head = glink_rpm_tx_write(glink, head, data, dlen); - writel(head, pipe->head); - - mbox_send_message(glink->mbox_chan, NULL); - mbox_client_txdone(glink->mbox_chan, 0); - -out: - mutex_unlock(&glink->tx_lock); + char padding[8] = {0}; + size_t pad; - return ret; -} - -static int glink_rpm_send_version(struct glink_rpm *glink) -{ - struct glink_msg msg; - - msg.cmd = cpu_to_le16(RPM_CMD_VERSION); - msg.param1 = cpu_to_le16(1); - msg.param2 = cpu_to_le32(GLINK_FEATURE_INTENTLESS); - - return glink_rpm_tx(glink, &msg, sizeof(msg), NULL, 0, true); -} - -static void glink_rpm_send_version_ack(struct glink_rpm *glink) -{ - struct glink_msg msg; - - msg.cmd = cpu_to_le16(RPM_CMD_VERSION_ACK); - msg.param1 = cpu_to_le16(1); - msg.param2 = cpu_to_le32(0); - - glink_rpm_tx(glink, &msg, sizeof(msg), NULL, 0, true); -} - -static void glink_rpm_send_open_ack(struct glink_rpm *glink, - struct glink_channel *channel) -{ - struct glink_msg msg; - - msg.cmd = cpu_to_le16(RPM_CMD_OPEN_ACK); - msg.param1 = cpu_to_le16(channel->rcid); - msg.param2 = cpu_to_le32(0); - - glink_rpm_tx(glink, &msg, sizeof(msg), NULL, 0, true); -} - -/** - * glink_rpm_send_open_req() - send a RPM_CMD_OPEN request to the remote - * @glink: - * @channel: - * - * Allocates a local channel id and sends a RPM_CMD_OPEN message to the remote. - * Will return with refcount held, regardless of outcome. - * - * Returns 0 on success, negative errno otherwise. - */ -static int glink_rpm_send_open_req(struct glink_rpm *glink, - struct glink_channel *channel) -{ - struct { - struct glink_msg msg; - u8 name[GLINK_NAME_SIZE]; - } __packed req; - int name_len = strlen(channel->name) + 1; - int req_len = ALIGN(sizeof(req.msg) + name_len, 8); - int ret; - - kref_get(&channel->refcount); - - mutex_lock(&glink->idr_lock); - ret = idr_alloc_cyclic(&glink->lcids, channel, - RPM_GLINK_CID_MIN, RPM_GLINK_CID_MAX, GFP_KERNEL); - mutex_unlock(&glink->idr_lock); - if (ret < 0) - return ret; - - channel->lcid = ret; - - req.msg.cmd = cpu_to_le16(RPM_CMD_OPEN); - req.msg.param1 = cpu_to_le16(channel->lcid); - req.msg.param2 = cpu_to_le32(name_len); - strcpy(req.name, channel->name); - - ret = glink_rpm_tx(glink, &req, req_len, NULL, 0, true); - if (ret) - goto remove_idr; - - return 0; - -remove_idr: - mutex_lock(&glink->idr_lock); - idr_remove(&glink->lcids, channel->lcid); - channel->lcid = 0; - mutex_unlock(&glink->idr_lock); - - return ret; -} - -static void glink_rpm_send_close_req(struct glink_rpm *glink, - struct glink_channel *channel) -{ - struct glink_msg req; - - req.cmd = cpu_to_le16(RPM_CMD_CLOSE); - req.param1 = cpu_to_le16(channel->lcid); - req.param2 = 0; - - glink_rpm_tx(glink, &req, sizeof(req), NULL, 0, true); -} - -static void glink_rpm_send_close_ack(struct glink_rpm *glink, unsigned int rcid) -{ - struct glink_msg req; - - req.cmd = cpu_to_le16(RPM_CMD_CLOSE_ACK); - req.param1 = cpu_to_le16(rcid); - req.param2 = 0; - - glink_rpm_tx(glink, &req, sizeof(req), NULL, 0, true); -} - -static int glink_rpm_rx_defer(struct glink_rpm *glink, size_t extra) -{ - struct glink_defer_cmd *dcmd; - - extra = ALIGN(extra, 8); - - if (glink_rpm_rx_avail(glink) < sizeof(struct glink_msg) + extra) { - dev_dbg(glink->dev, "Insufficient data in rx fifo"); - return -ENXIO; - } - - dcmd = kzalloc(sizeof(*dcmd) + extra, GFP_ATOMIC); - if (!dcmd) - return -ENOMEM; - - INIT_LIST_HEAD(&dcmd->node); - - glink_rpm_rx_peak(glink, &dcmd->msg, sizeof(dcmd->msg) + extra); - - spin_lock(&glink->rx_lock); - list_add_tail(&dcmd->node, &glink->rx_queue); - spin_unlock(&glink->rx_lock); - - schedule_work(&glink->rx_work); - glink_rpm_rx_advance(glink, sizeof(dcmd->msg) + extra); - - return 0; -} - -static int glink_rpm_rx_data(struct glink_rpm *glink, size_t avail) -{ - struct glink_channel *channel; - struct { - struct glink_msg msg; - __le32 chunk_size; - __le32 left_size; - } __packed hdr; - unsigned int chunk_size; - unsigned int left_size; - unsigned int rcid; - - if (avail < sizeof(hdr)) { - dev_dbg(glink->dev, "Not enough data in fifo\n"); - return -EAGAIN; - } - - glink_rpm_rx_peak(glink, &hdr, sizeof(hdr)); - chunk_size = le32_to_cpu(hdr.chunk_size); - left_size = le32_to_cpu(hdr.left_size); - - if (avail < sizeof(hdr) + chunk_size) { - dev_dbg(glink->dev, "Payload not yet in fifo\n"); - return -EAGAIN; - } - - if (WARN(chunk_size % 4, "Incoming data must be word aligned\n")) - return -EINVAL; - - rcid = le16_to_cpu(hdr.msg.param1); - channel = idr_find(&glink->rcids, rcid); - if (!channel) { - dev_dbg(glink->dev, "Data on non-existing channel\n"); - - /* Drop the message */ - glink_rpm_rx_advance(glink, ALIGN(sizeof(hdr) + chunk_size, 8)); - return 0; - } - - /* Might have an ongoing, fragmented, message to append */ - if (!channel->buf) { - channel->buf = kmalloc(chunk_size + left_size, GFP_ATOMIC); - if (!channel->buf) - return -ENOMEM; - - channel->buf_size = chunk_size + left_size; - channel->buf_offset = 0; - } - - glink_rpm_rx_advance(glink, sizeof(hdr)); - - if (channel->buf_size - channel->buf_offset < chunk_size) { - dev_err(glink->dev, "Insufficient space in input buffer\n"); - - /* The packet header lied, drop payload */ - glink_rpm_rx_advance(glink, chunk_size); - return -ENOMEM; - } - - glink_rpm_rx_peak(glink, channel->buf + channel->buf_offset, chunk_size); - channel->buf_offset += chunk_size; - - /* Handle message when no fragments remain to be received */ - if (!left_size) { - spin_lock(&channel->recv_lock); - if (channel->ept.cb) { - channel->ept.cb(channel->ept.rpdev, - channel->buf, - channel->buf_offset, - channel->ept.priv, - RPMSG_ADDR_ANY); - } - spin_unlock(&channel->recv_lock); - - kfree(channel->buf); - channel->buf = NULL; - channel->buf_size = 0; - } - - /* Each message starts at 8 byte aligned address */ - glink_rpm_rx_advance(glink, ALIGN(chunk_size, 8)); - - return 0; -} - -static int glink_rpm_rx_open_ack(struct glink_rpm *glink, unsigned int lcid) -{ - struct glink_channel *channel; - - channel = idr_find(&glink->lcids, lcid); - if (!channel) { - dev_err(glink->dev, "Invalid open ack packet\n"); - return -EINVAL; - } - - complete(&channel->open_ack); - - return 0; -} - -static irqreturn_t glink_rpm_intr(int irq, void *data) -{ - struct glink_rpm *glink = data; - struct glink_msg msg; - unsigned int param1; - unsigned int param2; - unsigned int avail; - unsigned int cmd; - int ret; - - for (;;) { - avail = glink_rpm_rx_avail(glink); - if (avail < sizeof(msg)) - break; - - glink_rpm_rx_peak(glink, &msg, sizeof(msg)); - - cmd = le16_to_cpu(msg.cmd); - param1 = le16_to_cpu(msg.param1); - param2 = le32_to_cpu(msg.param2); - - switch (cmd) { - case RPM_CMD_VERSION: - case RPM_CMD_VERSION_ACK: - case RPM_CMD_CLOSE: - case RPM_CMD_CLOSE_ACK: - ret = glink_rpm_rx_defer(glink, 0); - break; - case RPM_CMD_OPEN_ACK: - ret = glink_rpm_rx_open_ack(glink, param1); - glink_rpm_rx_advance(glink, ALIGN(sizeof(msg), 8)); - break; - case RPM_CMD_OPEN: - ret = glink_rpm_rx_defer(glink, param2); - break; - case RPM_CMD_TX_DATA: - case RPM_CMD_TX_DATA_CONT: - ret = glink_rpm_rx_data(glink, avail); - break; - case RPM_CMD_READ_NOTIF: - glink_rpm_rx_advance(glink, ALIGN(sizeof(msg), 8)); - - mbox_send_message(glink->mbox_chan, NULL); - mbox_client_txdone(glink->mbox_chan, 0); - - ret = 0; - break; - default: - dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd); - ret = -EINVAL; - break; - } - - if (ret) - break; - } - - return IRQ_HANDLED; -} - -/* Locally initiated rpmsg_create_ept */ -static struct glink_channel *glink_rpm_create_local(struct glink_rpm *glink, - const char *name) -{ - struct glink_channel *channel; - int ret; - - channel = glink_rpm_alloc_channel(glink, name); - if (IS_ERR(channel)) - return ERR_CAST(channel); - - ret = glink_rpm_send_open_req(glink, channel); - if (ret) - goto release_channel; - - ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ); - if (!ret) - goto err_timeout; - - ret = wait_for_completion_timeout(&channel->open_req, 5 * HZ); - if (!ret) - goto err_timeout; - - glink_rpm_send_open_ack(glink, channel); - - return channel; - -err_timeout: - /* glink_rpm_send_open_req() did register the channel in lcids*/ - mutex_lock(&glink->idr_lock); - idr_remove(&glink->lcids, channel->lcid); - mutex_unlock(&glink->idr_lock); - -release_channel: - /* Release glink_rpm_send_open_req() reference */ - kref_put(&channel->refcount, glink_rpm_channel_release); - /* Release glink_rpm_alloc_channel() reference */ - kref_put(&channel->refcount, glink_rpm_channel_release); - - return ERR_PTR(-ETIMEDOUT); -} - -/* Remote initiated rpmsg_create_ept */ -static int glink_rpm_create_remote(struct glink_rpm *glink, - struct glink_channel *channel) -{ - int ret; - - glink_rpm_send_open_ack(glink, channel); - - ret = glink_rpm_send_open_req(glink, channel); - if (ret) - goto close_link; - - ret = wait_for_completion_timeout(&channel->open_ack, 5 * HZ); - if (!ret) { - ret = -ETIMEDOUT; - goto close_link; - } - - return 0; + /* Header length comes from glink native and is always 4 byte aligned */ + if (WARN(hlen % 4, "Glink Header length must be 4 bytes aligned\n")) + return; -close_link: /* - * Send a close request to "undo" our open-ack. The close-ack will - * release the last reference. + * Move the unaligned tail of the message to the padding chunk, to + * ensure word aligned accesses */ - glink_rpm_send_close_req(glink, channel); - - /* Release glink_rpm_send_open_req() reference */ - kref_put(&channel->refcount, glink_rpm_channel_release); - - return ret; -} - -static struct rpmsg_endpoint *glink_rpm_create_ept(struct rpmsg_device *rpdev, - rpmsg_rx_cb_t cb, void *priv, - struct rpmsg_channel_info chinfo) -{ - struct glink_channel *parent = to_glink_channel(rpdev->ept); - struct glink_channel *channel; - struct glink_rpm *glink = parent->glink; - struct rpmsg_endpoint *ept; - const char *name = chinfo.name; - int cid; - int ret; - - idr_for_each_entry(&glink->rcids, channel, cid) { - if (!strcmp(channel->name, name)) - break; - } - - if (!channel) { - channel = glink_rpm_create_local(glink, name); - if (IS_ERR(channel)) - return NULL; - } else { - ret = glink_rpm_create_remote(glink, channel); - if (ret) - return NULL; - } - - ept = &channel->ept; - ept->rpdev = rpdev; - ept->cb = cb; - ept->priv = priv; - ept->ops = &glink_endpoint_ops; - - return ept; -} - -static void glink_rpm_destroy_ept(struct rpmsg_endpoint *ept) -{ - struct glink_channel *channel = to_glink_channel(ept); - struct glink_rpm *glink = channel->glink; - unsigned long flags; - - spin_lock_irqsave(&channel->recv_lock, flags); - channel->ept.cb = NULL; - spin_unlock_irqrestore(&channel->recv_lock, flags); - - /* Decouple the potential rpdev from the channel */ - channel->rpdev = NULL; - - glink_rpm_send_close_req(glink, channel); -} - -static int __glink_rpm_send(struct glink_channel *channel, - void *data, int len, bool wait) -{ - struct glink_rpm *glink = channel->glink; - struct { - struct glink_msg msg; - __le32 chunk_size; - __le32 left_size; - } __packed req; - - if (WARN(len % 8, "RPM GLINK expects 8 byte aligned messages\n")) - return -EINVAL; - - req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA); - req.msg.param1 = cpu_to_le16(channel->lcid); - req.msg.param2 = cpu_to_le32(channel->rcid); - req.chunk_size = cpu_to_le32(len); - req.left_size = cpu_to_le32(0); - - return glink_rpm_tx(glink, &req, sizeof(req), data, len, wait); -} - -static int glink_rpm_send(struct rpmsg_endpoint *ept, void *data, int len) -{ - struct glink_channel *channel = to_glink_channel(ept); - - return __glink_rpm_send(channel, data, len, true); -} - -static int glink_rpm_trysend(struct rpmsg_endpoint *ept, void *data, int len) -{ - struct glink_channel *channel = to_glink_channel(ept); - - return __glink_rpm_send(channel, data, len, false); -} - -/* - * Finds the device_node for the glink child interested in this channel. - */ -static struct device_node *glink_rpm_match_channel(struct device_node *node, - const char *channel) -{ - struct device_node *child; - const char *name; - const char *key; - int ret; - - for_each_available_child_of_node(node, child) { - key = "qcom,glink-channels"; - ret = of_property_read_string(child, key, &name); - if (ret) - continue; - - if (strcmp(name, channel) == 0) - return child; - } - - return NULL; -} - -static const struct rpmsg_device_ops glink_device_ops = { - .create_ept = glink_rpm_create_ept, -}; - -static const struct rpmsg_endpoint_ops glink_endpoint_ops = { - .destroy_ept = glink_rpm_destroy_ept, - .send = glink_rpm_send, - .trysend = glink_rpm_trysend, -}; - -static void glink_rpm_rpdev_release(struct device *dev) -{ - struct rpmsg_device *rpdev = to_rpmsg_device(dev); - struct glink_channel *channel = to_glink_channel(rpdev->ept); - - channel->rpdev = NULL; - kfree(rpdev); -} - -static int glink_rpm_rx_open(struct glink_rpm *glink, unsigned int rcid, - char *name) -{ - struct glink_channel *channel; - struct rpmsg_device *rpdev; - bool create_device = false; - int lcid; - int ret; - - idr_for_each_entry(&glink->lcids, channel, lcid) { - if (!strcmp(channel->name, name)) - break; - } - - if (!channel) { - channel = glink_rpm_alloc_channel(glink, name); - if (IS_ERR(channel)) - return PTR_ERR(channel); - - /* The opening dance was initiated by the remote */ - create_device = true; - } - - mutex_lock(&glink->idr_lock); - ret = idr_alloc(&glink->rcids, channel, rcid, rcid + 1, GFP_KERNEL); - if (ret < 0) { - dev_err(glink->dev, "Unable to insert channel into rcid list\n"); - mutex_unlock(&glink->idr_lock); - goto free_channel; - } - channel->rcid = ret; - mutex_unlock(&glink->idr_lock); - - complete(&channel->open_req); - - if (create_device) { - rpdev = kzalloc(sizeof(*rpdev), GFP_KERNEL); - if (!rpdev) { - ret = -ENOMEM; - goto rcid_remove; - } - - rpdev->ept = &channel->ept; - strncpy(rpdev->id.name, name, RPMSG_NAME_SIZE); - rpdev->src = RPMSG_ADDR_ANY; - rpdev->dst = RPMSG_ADDR_ANY; - rpdev->ops = &glink_device_ops; - - rpdev->dev.of_node = glink_rpm_match_channel(glink->dev->of_node, name); - rpdev->dev.parent = glink->dev; - rpdev->dev.release = glink_rpm_rpdev_release; - - ret = rpmsg_register_device(rpdev); - if (ret) - goto free_rpdev; - - channel->rpdev = rpdev; - } - - return 0; - -free_rpdev: - kfree(rpdev); -rcid_remove: - mutex_lock(&glink->idr_lock); - idr_remove(&glink->rcids, channel->rcid); - channel->rcid = 0; - mutex_unlock(&glink->idr_lock); -free_channel: - /* Release the reference, iff we took it */ - if (create_device) - kref_put(&channel->refcount, glink_rpm_channel_release); - - return ret; -} - -static void glink_rpm_rx_close(struct glink_rpm *glink, unsigned int rcid) -{ - struct rpmsg_channel_info chinfo; - struct glink_channel *channel; - - channel = idr_find(&glink->rcids, rcid); - if (WARN(!channel, "close request on unknown channel\n")) - return; - - if (channel->rpdev) { - strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); - chinfo.src = RPMSG_ADDR_ANY; - chinfo.dst = RPMSG_ADDR_ANY; - - rpmsg_unregister_device(glink->dev, &chinfo); - } - - glink_rpm_send_close_ack(glink, channel->rcid); - - mutex_lock(&glink->idr_lock); - idr_remove(&glink->rcids, channel->rcid); - channel->rcid = 0; - mutex_unlock(&glink->idr_lock); - - kref_put(&channel->refcount, glink_rpm_channel_release); -} + aligned_dlen = ALIGN_DOWN(dlen, 4); + if (aligned_dlen != dlen) + memcpy(padding, data + aligned_dlen, dlen - aligned_dlen); -static void glink_rpm_rx_close_ack(struct glink_rpm *glink, unsigned int lcid) -{ - struct glink_channel *channel; - - channel = idr_find(&glink->lcids, lcid); - if (WARN(!channel, "close ack on unknown channel\n")) - return; - - mutex_lock(&glink->idr_lock); - idr_remove(&glink->lcids, channel->lcid); - channel->lcid = 0; - mutex_unlock(&glink->idr_lock); - - kref_put(&channel->refcount, glink_rpm_channel_release); -} - -static void glink_rpm_work(struct work_struct *work) -{ - struct glink_rpm *glink = container_of(work, struct glink_rpm, rx_work); - struct glink_defer_cmd *dcmd; - struct glink_msg *msg; - unsigned long flags; - unsigned int param1; - unsigned int param2; - unsigned int cmd; - - for (;;) { - spin_lock_irqsave(&glink->rx_lock, flags); - if (list_empty(&glink->rx_queue)) { - spin_unlock_irqrestore(&glink->rx_lock, flags); - break; - } - dcmd = list_first_entry(&glink->rx_queue, struct glink_defer_cmd, node); - list_del(&dcmd->node); - spin_unlock_irqrestore(&glink->rx_lock, flags); - - msg = &dcmd->msg; - cmd = le16_to_cpu(msg->cmd); - param1 = le16_to_cpu(msg->param1); - param2 = le32_to_cpu(msg->param2); - - switch (cmd) { - case RPM_CMD_VERSION: - glink_rpm_send_version_ack(glink); - break; - case RPM_CMD_VERSION_ACK: - break; - case RPM_CMD_OPEN: - glink_rpm_rx_open(glink, param1, msg->data); - break; - case RPM_CMD_CLOSE: - glink_rpm_rx_close(glink, param1); - break; - case RPM_CMD_CLOSE_ACK: - glink_rpm_rx_close_ack(glink, param1); - break; - default: - WARN(1, "Unknown defer object %d\n", cmd); - break; - } + head = readl(pipe->head); + head = glink_rpm_tx_write_one(pipe, head, hdr, hlen); + head = glink_rpm_tx_write_one(pipe, head, data, aligned_dlen); - kfree(dcmd); - } + pad = ALIGN(tlen, 8) - ALIGN_DOWN(tlen, 4); + if (pad) + head = glink_rpm_tx_write_one(pipe, head, padding, pad); + writel(head, pipe->head); } static int glink_rpm_parse_toc(struct device *dev, @@ -1067,14 +233,14 @@ static int glink_rpm_parse_toc(struct device *dev, switch (id) { case RPM_RX_FIFO_ID: - rx->length = size; + rx->native.length = size; rx->tail = msg_ram + offset; rx->head = msg_ram + offset + sizeof(u32); rx->fifo = msg_ram + offset + 2 * sizeof(u32); break; case RPM_TX_FIFO_ID: - tx->length = size; + tx->native.length = size; tx->tail = msg_ram + offset; tx->head = msg_ram + offset + sizeof(u32); @@ -1098,38 +264,21 @@ err_inval: static int glink_rpm_probe(struct platform_device *pdev) { - struct glink_rpm *glink; + struct qcom_glink *glink; + struct glink_rpm_pipe *rx_pipe; + struct glink_rpm_pipe *tx_pipe; struct device_node *np; void __iomem *msg_ram; size_t msg_ram_size; struct device *dev = &pdev->dev; struct resource r; - int irq; int ret; - glink = devm_kzalloc(dev, sizeof(*glink), GFP_KERNEL); - if (!glink) + rx_pipe = devm_kzalloc(&pdev->dev, sizeof(*rx_pipe), GFP_KERNEL); + tx_pipe = devm_kzalloc(&pdev->dev, sizeof(*tx_pipe), GFP_KERNEL); + if (!rx_pipe || !tx_pipe) return -ENOMEM; - glink->dev = dev; - - mutex_init(&glink->tx_lock); - spin_lock_init(&glink->rx_lock); - INIT_LIST_HEAD(&glink->rx_queue); - INIT_WORK(&glink->rx_work, glink_rpm_work); - - mutex_init(&glink->idr_lock); - idr_init(&glink->lcids); - idr_init(&glink->rcids); - - glink->mbox_client.dev = &pdev->dev; - glink->mbox_chan = mbox_request_channel(&glink->mbox_client, 0); - if (IS_ERR(glink->mbox_chan)) { - if (PTR_ERR(glink->mbox_chan) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to acquire IPC channel\n"); - return PTR_ERR(glink->mbox_chan); - } - np = of_parse_phandle(dev->of_node, "qcom,rpm-msg-ram", 0); ret = of_address_to_resource(np, 0, &r); of_node_put(np); @@ -1142,61 +291,38 @@ static int glink_rpm_probe(struct platform_device *pdev) return -ENOMEM; ret = glink_rpm_parse_toc(dev, msg_ram, msg_ram_size, - &glink->rx_pipe, &glink->tx_pipe); + rx_pipe, tx_pipe); if (ret) return ret; - writel(0, glink->tx_pipe.head); - writel(0, glink->rx_pipe.tail); + /* Pipe specific accessors */ + rx_pipe->native.avail = glink_rpm_rx_avail; + rx_pipe->native.peak = glink_rpm_rx_peak; + rx_pipe->native.advance = glink_rpm_rx_advance; + tx_pipe->native.avail = glink_rpm_tx_avail; + tx_pipe->native.write = glink_rpm_tx_write; - irq = platform_get_irq(pdev, 0); - ret = devm_request_irq(dev, irq, - glink_rpm_intr, - IRQF_NO_SUSPEND | IRQF_SHARED, - "glink-rpm", glink); - if (ret) { - dev_err(dev, "Failed to request IRQ\n"); - return ret; - } - - glink->irq = irq; + writel(0, tx_pipe->head); + writel(0, rx_pipe->tail); - ret = glink_rpm_send_version(glink); - if (ret) - return ret; + glink = qcom_glink_native_probe(&pdev->dev, + 0, + &rx_pipe->native, + &tx_pipe->native, + true); + if (IS_ERR(glink)) + return PTR_ERR(glink); platform_set_drvdata(pdev, glink); return 0; } -static int glink_rpm_remove_device(struct device *dev, void *data) -{ - device_unregister(dev); - - return 0; -} - static int glink_rpm_remove(struct platform_device *pdev) { - struct glink_rpm *glink = platform_get_drvdata(pdev); - struct glink_channel *channel; - int cid; - int ret; - - disable_irq(glink->irq); - cancel_work_sync(&glink->rx_work); - - ret = device_for_each_child(glink->dev, NULL, glink_rpm_remove_device); - if (ret) - dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret); - - /* Release any defunct local channels, waiting for close-ack */ - idr_for_each_entry(&glink->lcids, channel, cid) - kref_put(&channel->refcount, glink_rpm_channel_release); + struct qcom_glink *glink = platform_get_drvdata(pdev); - idr_destroy(&glink->lcids); - idr_destroy(&glink->rcids); + qcom_glink_native_remove(glink); return 0; } diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c new file mode 100644 index 000000000000..5cdaa5f8fb61 --- /dev/null +++ b/drivers/rpmsg/qcom_glink_smem.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2016, Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/slab.h> +#include <linux/rpmsg.h> +#include <linux/idr.h> +#include <linux/circ_buf.h> +#include <linux/soc/qcom/smem.h> +#include <linux/sizes.h> +#include <linux/delay.h> +#include <linux/regmap.h> +#include <linux/workqueue.h> +#include <linux/list.h> + +#include <linux/delay.h> +#include <linux/rpmsg.h> +#include <linux/rpmsg/qcom_glink.h> + +#include "qcom_glink_native.h" + +#define FIFO_FULL_RESERVE 8 +#define FIFO_ALIGNMENT 8 +#define TX_BLOCKED_CMD_RESERVE 8 /* size of struct read_notif_request */ + +#define SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR 478 +#define SMEM_GLINK_NATIVE_XPRT_FIFO_0 479 +#define SMEM_GLINK_NATIVE_XPRT_FIFO_1 480 + +struct glink_smem_pipe { + struct qcom_glink_pipe native; + + __le32 *tail; + __le32 *head; + + void *fifo; + + int remote_pid; +}; + +#define to_smem_pipe(p) container_of(p, struct glink_smem_pipe, native) + +static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np) +{ + struct glink_smem_pipe *pipe = to_smem_pipe(np); + size_t len; + void *fifo; + u32 head; + u32 tail; + + if (!pipe->fifo) { + fifo = qcom_smem_get(pipe->remote_pid, + SMEM_GLINK_NATIVE_XPRT_FIFO_1, &len); + if (IS_ERR(fifo)) { + pr_err("failed to acquire RX fifo handle: %ld\n", + PTR_ERR(fifo)); + return 0; + } + + pipe->fifo = fifo; + pipe->native.length = len; + } + + head = le32_to_cpu(*pipe->head); + tail = le32_to_cpu(*pipe->tail); + + if (head < tail) + return pipe->native.length - tail + head; + else + return head - tail; +} + +static void glink_smem_rx_peak(struct qcom_glink_pipe *np, + void *data, unsigned int offset, size_t count) +{ + struct glink_smem_pipe *pipe = to_smem_pipe(np); + size_t len; + u32 tail; + + tail = le32_to_cpu(*pipe->tail); + tail += offset; + if (tail >= pipe->native.length) + tail -= pipe->native.length; + + len = min_t(size_t, count, pipe->native.length - tail); + if (len) { + __ioread32_copy(data, pipe->fifo + tail, + len / sizeof(u32)); + } + + if (len != count) { + __ioread32_copy(data + len, pipe->fifo, + (count - len) / sizeof(u32)); + } +} + +static void glink_smem_rx_advance(struct qcom_glink_pipe *np, + size_t count) +{ + struct glink_smem_pipe *pipe = to_smem_pipe(np); + u32 tail; + + tail = le32_to_cpu(*pipe->tail); + + tail += count; + if (tail > pipe->native.length) + tail -= pipe->native.length; + + *pipe->tail = cpu_to_le32(tail); +} + +static size_t glink_smem_tx_avail(struct qcom_glink_pipe *np) +{ + struct glink_smem_pipe *pipe = to_smem_pipe(np); + u32 head; + u32 tail; + u32 avail; + + head = le32_to_cpu(*pipe->head); + tail = le32_to_cpu(*pipe->tail); + + if (tail <= head) + avail = pipe->native.length - head + tail; + else + avail = tail - head; + + if (avail < (FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE)) + avail = 0; + else + avail -= FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE; + + return avail; +} + +static unsigned int glink_smem_tx_write_one(struct glink_smem_pipe *pipe, + unsigned int head, + const void *data, size_t count) +{ + size_t len; + + len = min_t(size_t, count, pipe->native.length - head); + if (len) + memcpy(pipe->fifo + head, data, len); + + if (len != count) + memcpy(pipe->fifo, data + len, count - len); + + head += count; + if (head >= pipe->native.length) + head -= pipe->native.length; + + return head; +} + +static void glink_smem_tx_write(struct qcom_glink_pipe *glink_pipe, + const void *hdr, size_t hlen, + const void *data, size_t dlen) +{ + struct glink_smem_pipe *pipe = to_smem_pipe(glink_pipe); + unsigned int head; + + head = le32_to_cpu(*pipe->head); + + head = glink_smem_tx_write_one(pipe, head, hdr, hlen); + head = glink_smem_tx_write_one(pipe, head, data, dlen); + + /* Ensure head is always aligned to 8 bytes */ + head = ALIGN(head, 8); + if (head >= pipe->native.length) + head -= pipe->native.length; + + *pipe->head = cpu_to_le32(head); +} + +static void qcom_glink_smem_release(struct device *dev) +{ + kfree(dev); +} + +struct qcom_glink *qcom_glink_smem_register(struct device *parent, + struct device_node *node) +{ + struct glink_smem_pipe *rx_pipe; + struct glink_smem_pipe *tx_pipe; + struct qcom_glink *glink; + struct device *dev; + u32 remote_pid; + __le32 *descs; + size_t size; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + dev->parent = parent; + dev->of_node = node; + dev->release = qcom_glink_smem_release; + dev_set_name(dev, "%s:%s", node->parent->name, node->name); + ret = device_register(dev); + if (ret) { + pr_err("failed to register glink edge\n"); + return ERR_PTR(ret); + } + + ret = of_property_read_u32(dev->of_node, "qcom,remote-pid", + &remote_pid); + if (ret) { + dev_err(dev, "failed to parse qcom,remote-pid\n"); + goto err_put_dev; + } + + rx_pipe = devm_kzalloc(dev, sizeof(*rx_pipe), GFP_KERNEL); + tx_pipe = devm_kzalloc(dev, sizeof(*tx_pipe), GFP_KERNEL); + if (!rx_pipe || !tx_pipe) { + ret = -ENOMEM; + goto err_put_dev; + } + + ret = qcom_smem_alloc(remote_pid, + SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, 32); + if (ret && ret != -EEXIST) { + dev_err(dev, "failed to allocate glink descriptors\n"); + goto err_put_dev; + } + + descs = qcom_smem_get(remote_pid, + SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, &size); + if (IS_ERR(descs)) { + dev_err(dev, "failed to acquire xprt descriptor\n"); + ret = PTR_ERR(descs); + goto err_put_dev; + } + + if (size != 32) { + dev_err(dev, "glink descriptor of invalid size\n"); + ret = -EINVAL; + goto err_put_dev; + } + + tx_pipe->tail = &descs[0]; + tx_pipe->head = &descs[1]; + rx_pipe->tail = &descs[2]; + rx_pipe->head = &descs[3]; + + ret = qcom_smem_alloc(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0, + SZ_16K); + if (ret && ret != -EEXIST) { + dev_err(dev, "failed to allocate TX fifo\n"); + goto err_put_dev; + } + + tx_pipe->fifo = qcom_smem_get(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0, + &tx_pipe->native.length); + if (IS_ERR(tx_pipe->fifo)) { + dev_err(dev, "failed to acquire TX fifo\n"); + ret = PTR_ERR(tx_pipe->fifo); + goto err_put_dev; + } + + rx_pipe->native.avail = glink_smem_rx_avail; + rx_pipe->native.peak = glink_smem_rx_peak; + rx_pipe->native.advance = glink_smem_rx_advance; + rx_pipe->remote_pid = remote_pid; + + tx_pipe->native.avail = glink_smem_tx_avail; + tx_pipe->native.write = glink_smem_tx_write; + tx_pipe->remote_pid = remote_pid; + + *rx_pipe->tail = 0; + *tx_pipe->head = 0; + + glink = qcom_glink_native_probe(dev, + GLINK_FEATURE_INTENT_REUSE, + &rx_pipe->native, &tx_pipe->native, + false); + if (IS_ERR(glink)) { + ret = PTR_ERR(glink); + goto err_put_dev; + } + + return glink; + +err_put_dev: + put_device(dev); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(qcom_glink_smem_register); + +void qcom_glink_smem_unregister(struct qcom_glink *glink) +{ + qcom_glink_native_remove(glink); + qcom_glink_native_unregister(glink); +} +EXPORT_SYMBOL_GPL(qcom_glink_smem_unregister); + +MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>"); +MODULE_DESCRIPTION("Qualcomm GLINK SMEM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c index a0a39a8821a3..b01774e9fac0 100644 --- a/drivers/rpmsg/qcom_smd.c +++ b/drivers/rpmsg/qcom_smd.c @@ -1368,6 +1368,7 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, edge->dev.parent = parent; edge->dev.release = qcom_smd_edge_release; + edge->dev.of_node = node; edge->dev.groups = qcom_smd_edge_groups; dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name); ret = device_register(&edge->dev); diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index eee2a9f77d37..82b83002fcba 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -45,6 +45,7 @@ * @rbufs: kernel address of rx buffers * @sbufs: kernel address of tx buffers * @num_bufs: total number of buffers for rx and tx + * @buf_size: size of one rx or tx buffer * @last_sbuf: index of last tx buffer used * @bufs_dma: dma base addr of the buffers * @tx_lock: protects svq, sbufs and sleepers, to allow concurrent senders. @@ -65,6 +66,7 @@ struct virtproc_info { struct virtqueue *rvq, *svq; void *rbufs, *sbufs; unsigned int num_bufs; + unsigned int buf_size; int last_sbuf; dma_addr_t bufs_dma; struct mutex tx_lock; @@ -158,7 +160,7 @@ struct virtio_rpmsg_channel { * processor. */ #define MAX_RPMSG_NUM_BUFS (512) -#define RPMSG_BUF_SIZE (512) +#define MAX_RPMSG_BUF_SIZE (512) /* * Local addresses are dynamically allocated on-demand. @@ -193,6 +195,28 @@ static const struct rpmsg_endpoint_ops virtio_endpoint_ops = { }; /** + * rpmsg_sg_init - initialize scatterlist according to cpu address location + * @sg: scatterlist to fill + * @cpu_addr: virtual address of the buffer + * @len: buffer length + * + * An internal function filling scatterlist according to virtual address + * location (in vmalloc or in kernel). + */ +static void +rpmsg_sg_init(struct scatterlist *sg, void *cpu_addr, unsigned int len) +{ + if (is_vmalloc_addr(cpu_addr)) { + sg_init_table(sg, 1); + sg_set_page(sg, vmalloc_to_page(cpu_addr), len, + offset_in_page(cpu_addr)); + } else { + WARN_ON(!virt_addr_valid(cpu_addr)); + sg_init_one(sg, cpu_addr, len); + } +} + +/** * __ept_release() - deallocate an rpmsg endpoint * @kref: the ept's reference count * @@ -435,7 +459,7 @@ static void *get_a_tx_buf(struct virtproc_info *vrp) * (half of our buffers are used for sending messages) */ if (vrp->last_sbuf < vrp->num_bufs / 2) - ret = vrp->sbufs + RPMSG_BUF_SIZE * vrp->last_sbuf++; + ret = vrp->sbufs + vrp->buf_size * vrp->last_sbuf++; /* or recycle a used one */ else ret = virtqueue_get_buf(vrp->svq, &len); @@ -561,7 +585,7 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev, * messaging), or to improve the buffer allocator, to support * variable-length buffer sizes. */ - if (len > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) { + if (len > vrp->buf_size - sizeof(struct rpmsg_hdr)) { dev_err(dev, "message is too big (%d)\n", len); return -EMSGSIZE; } @@ -610,7 +634,7 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev, msg, sizeof(*msg) + msg->len, true); #endif - sg_init_one(&sg, msg, sizeof(*msg) + len); + rpmsg_sg_init(&sg, msg, sizeof(*msg) + len); mutex_lock(&vrp->tx_lock); @@ -632,7 +656,6 @@ out: mutex_unlock(&vrp->tx_lock); return err; } -EXPORT_SYMBOL(rpmsg_send_offchannel_raw); static int virtio_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len) { @@ -702,7 +725,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, * We currently use fixed-sized buffers, so trivially sanitize * the reported payload length. */ - if (len > RPMSG_BUF_SIZE || + if (len > vrp->buf_size || msg->len > (len - sizeof(struct rpmsg_hdr))) { dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg->len); return -EINVAL; @@ -735,7 +758,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, dev_warn(dev, "msg received with no recipient\n"); /* publish the real size of the buffer */ - sg_init_one(&sg, msg, RPMSG_BUF_SIZE); + rpmsg_sg_init(&sg, msg, vrp->buf_size); /* add the buffer back to the remote processor's virtqueue */ err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL); @@ -892,7 +915,9 @@ static int rpmsg_probe(struct virtio_device *vdev) else vrp->num_bufs = MAX_RPMSG_NUM_BUFS; - total_buf_space = vrp->num_bufs * RPMSG_BUF_SIZE; + vrp->buf_size = MAX_RPMSG_BUF_SIZE; + + total_buf_space = vrp->num_bufs * vrp->buf_size; /* allocate coherent memory for the buffers */ bufs_va = dma_alloc_coherent(vdev->dev.parent->parent, @@ -915,9 +940,9 @@ static int rpmsg_probe(struct virtio_device *vdev) /* set up the receive buffers */ for (i = 0; i < vrp->num_bufs / 2; i++) { struct scatterlist sg; - void *cpu_addr = vrp->rbufs + i * RPMSG_BUF_SIZE; + void *cpu_addr = vrp->rbufs + i * vrp->buf_size; - sg_init_one(&sg, cpu_addr, RPMSG_BUF_SIZE); + rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size); err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr, GFP_KERNEL); @@ -982,7 +1007,7 @@ static int rpmsg_remove_device(struct device *dev, void *data) static void rpmsg_remove(struct virtio_device *vdev) { struct virtproc_info *vrp = vdev->priv; - size_t total_buf_space = vrp->num_bufs * RPMSG_BUF_SIZE; + size_t total_buf_space = vrp->num_bufs * vrp->buf_size; int ret; vdev->config->reset(vdev); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 72419ac2c52a..e0e58f3b1420 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -227,14 +227,14 @@ config RTC_DRV_AS3722 will be called rtc-as3722. config RTC_DRV_DS1307 - tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025, ISL12057" + tristate "Dallas/Maxim DS1307/37/38/39/40/41, ST M41T00, EPSON RX-8025, ISL12057" help If you say yes here you get support for various compatible RTC chips (often with battery backup) connected with I2C. This driver - should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00, - EPSON RX-8025, Intersil ISL12057 and probably other chips. In some - cases the RTC must already have been initialized (by manufacturing or - a bootloader). + should handle DS1307, DS1337, DS1338, DS1339, DS1340, DS1341, + ST M41T00, EPSON RX-8025, Intersil ISL12057 and probably other chips. + In some cases the RTC must already have been initialized (by + manufacturing or a bootloader). The first seven registers on these chips hold an RTC, and other registers may add features such as NVRAM, a trickle charger for @@ -371,11 +371,11 @@ config RTC_DRV_MAX77686 will be called rtc-max77686. config RTC_DRV_RK808 - tristate "Rockchip RK808/RK818 RTC" + tristate "Rockchip RK805/RK808/RK818 RTC" depends on MFD_RK808 help If you say yes here you will get support for the - RTC of RK808 and RK818 PMIC. + RTC of RK805, RK808 and RK818 PMIC. This driver can also be built as a module. If so, the module will be called rk808-rtc. @@ -1765,6 +1765,14 @@ config RTC_DRV_CPCAP Say y here for CPCAP rtc found on some Motorola phones and tablets such as Droid 4. +config RTC_DRV_RTD119X + bool "Realtek RTD129x RTC" + depends on ARCH_REALTEK || COMPILE_TEST + default ARCH_REALTEK + help + If you say yes here, you get support for the RTD1295 SoC + Real Time Clock. + comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME @@ -1780,5 +1788,13 @@ config RTC_DRV_HID_SENSOR_TIME If this driver is compiled as a module, it will be named rtc-hid-sensor-time. +config RTC_DRV_GOLDFISH + tristate "Goldfish Real Time Clock" + depends on MIPS && (GOLDFISH || COMPILE_TEST) + help + Say yes to enable RTC driver for the Goldfish based virtual platform. + + Goldfish is a code name for the virtual platform developed by Google + for Android emulation. endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index acd366b41c85..7230014c92af 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -131,6 +131,7 @@ obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o +obj-$(CONFIG_RTC_DRV_RTD119X) += rtc-rtd119x.o obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o @@ -170,3 +171,4 @@ obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o obj-$(CONFIG_RTC_DRV_ZYNQMP) += rtc-zynqmp.o +obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 794bc4fa4937..00efe24a6063 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -24,28 +24,19 @@ static dev_t rtc_devt; static int rtc_dev_open(struct inode *inode, struct file *file) { - int err; struct rtc_device *rtc = container_of(inode->i_cdev, struct rtc_device, char_dev); - const struct rtc_class_ops *ops = rtc->ops; if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) return -EBUSY; file->private_data = rtc; - err = ops->open ? ops->open(rtc->dev.parent) : 0; - if (err == 0) { - spin_lock_irq(&rtc->irq_lock); - rtc->irq_data = 0; - spin_unlock_irq(&rtc->irq_lock); - - return 0; - } + spin_lock_irq(&rtc->irq_lock); + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); - /* something has gone wrong */ - clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); - return err; + return 0; } #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL @@ -438,9 +429,6 @@ static int rtc_dev_release(struct inode *inode, struct file *file) rtc_update_irq_enable(rtc, 0); rtc_irq_set_state(rtc, NULL, 0); - if (rtc->ops->release) - rtc->ops->release(rtc->dev.parent); - clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); return 0; } diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 4b43aa62fbc7..e7d9215c9201 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -39,6 +39,7 @@ enum ds_type { ds_1338, ds_1339, ds_1340, + ds_1341, ds_1388, ds_3231, m41t0, @@ -50,7 +51,6 @@ enum ds_type { /* rs5c372 too? different address... */ }; - /* RTC registers don't differ much, except for the century flag */ #define DS1307_REG_SECS 0x00 /* 00-59 */ # define DS1307_BIT_CH 0x80 @@ -113,11 +113,7 @@ enum ds_type { # define RX8025_BIT_VDET 0x40 # define RX8025_BIT_XST 0x20 - struct ds1307 { - u8 offset; /* register's offset */ - u8 regs[11]; - u16 nvram_offset; struct nvmem_config nvmem_cfg; enum ds_type type; unsigned long flags; @@ -126,7 +122,6 @@ struct ds1307 { struct device *dev; struct regmap *regmap; const char *name; - int irq; struct rtc_device *rtc; #ifdef CONFIG_COMMON_CLK struct clk_hw clks[2]; @@ -137,18 +132,47 @@ struct chip_desc { unsigned alarm:1; u16 nvram_offset; u16 nvram_size; + u8 offset; /* register's offset */ u8 century_reg; u8 century_enable_bit; u8 century_bit; + u8 bbsqi_bit; + irq_handler_t irq_handler; + const struct rtc_class_ops *rtc_ops; u16 trickle_charger_reg; - u8 trickle_charger_setup; - u8 (*do_trickle_setup)(struct ds1307 *, uint32_t, + u8 (*do_trickle_setup)(struct ds1307 *, u32, bool); }; -static u8 do_trickle_setup_ds1339(struct ds1307 *, uint32_t ohms, bool diode); +static int ds1307_get_time(struct device *dev, struct rtc_time *t); +static int ds1307_set_time(struct device *dev, struct rtc_time *t); +static u8 do_trickle_setup_ds1339(struct ds1307 *, u32 ohms, bool diode); +static irqreturn_t rx8130_irq(int irq, void *dev_id); +static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t); +static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t); +static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled); +static irqreturn_t mcp794xx_irq(int irq, void *dev_id); +static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t); +static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t); +static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled); -static struct chip_desc chips[last_ds_type] = { +static const struct rtc_class_ops rx8130_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = rx8130_read_alarm, + .set_alarm = rx8130_set_alarm, + .alarm_irq_enable = rx8130_alarm_irq_enable, +}; + +static const struct rtc_class_ops mcp794xx_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = mcp794xx_read_alarm, + .set_alarm = mcp794xx_set_alarm, + .alarm_irq_enable = mcp794xx_alarm_irq_enable, +}; + +static const struct chip_desc chips[last_ds_type] = { [ds_1307] = { .nvram_offset = 8, .nvram_size = 56, @@ -170,6 +194,7 @@ static struct chip_desc chips[last_ds_type] = { .alarm = 1, .century_reg = DS1307_REG_MONTH, .century_bit = DS1337_BIT_CENTURY, + .bbsqi_bit = DS1339_BIT_BBSQI, .trickle_charger_reg = 0x10, .do_trickle_setup = &do_trickle_setup_ds1339, }, @@ -179,25 +204,36 @@ static struct chip_desc chips[last_ds_type] = { .century_bit = DS1340_BIT_CENTURY, .trickle_charger_reg = 0x08, }, + [ds_1341] = { + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + }, [ds_1388] = { + .offset = 1, .trickle_charger_reg = 0x0a, }, [ds_3231] = { .alarm = 1, .century_reg = DS1307_REG_MONTH, .century_bit = DS1337_BIT_CENTURY, + .bbsqi_bit = DS3231_BIT_BBSQW, }, [rx_8130] = { .alarm = 1, /* this is battery backed SRAM */ .nvram_offset = 0x20, .nvram_size = 4, /* 32bit (4 word x 8 bit) */ + .offset = 0x10, + .irq_handler = rx8130_irq, + .rtc_ops = &rx8130_rtc_ops, }, [mcp794xx] = { .alarm = 1, /* this is battery backed SRAM */ .nvram_offset = 0x20, .nvram_size = 0x40, + .irq_handler = mcp794xx_irq, + .rtc_ops = &mcp794xx_rtc_ops, }, }; @@ -209,6 +245,7 @@ static const struct i2c_device_id ds1307_id[] = { { "ds1339", ds_1339 }, { "ds1388", ds_1388 }, { "ds1340", ds_1340 }, + { "ds1341", ds_1341 }, { "ds3231", ds_3231 }, { "m41t0", m41t0 }, { "m41t00", m41t00 }, @@ -253,6 +290,10 @@ static const struct of_device_id ds1307_of_match[] = { .data = (void *)ds_1340 }, { + .compatible = "dallas,ds1341", + .data = (void *)ds_1341 + }, + { .compatible = "maxim,ds3231", .data = (void *)ds_3231 }, @@ -298,6 +339,7 @@ static const struct acpi_device_id ds1307_acpi_ids[] = { { .id = "DS1339", .driver_data = ds_1339 }, { .id = "DS1388", .driver_data = ds_1388 }, { .id = "DS1340", .driver_data = ds_1340 }, + { .id = "DS1341", .driver_data = ds_1341 }, { .id = "DS3231", .driver_data = ds_3231 }, { .id = "M41T0", .driver_data = m41t0 }, { .id = "M41T00", .driver_data = m41t00 }, @@ -352,34 +394,36 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) struct ds1307 *ds1307 = dev_get_drvdata(dev); int tmp, ret; const struct chip_desc *chip = &chips[ds1307->type]; + u8 regs[7]; /* read the RTC date and time registers all at once */ - ret = regmap_bulk_read(ds1307->regmap, ds1307->offset, ds1307->regs, 7); + ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs, + sizeof(regs)); if (ret) { dev_err(dev, "%s error %d\n", "read", ret); return ret; } - dev_dbg(dev, "%s: %7ph\n", "read", ds1307->regs); + dev_dbg(dev, "%s: %7ph\n", "read", regs); /* if oscillator fail bit is set, no data can be trusted */ if (ds1307->type == m41t0 && - ds1307->regs[DS1307_REG_MIN] & M41T0_BIT_OF) { + regs[DS1307_REG_MIN] & M41T0_BIT_OF) { dev_warn_once(dev, "oscillator failed, set time!\n"); return -EINVAL; } - t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f); - t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f); - tmp = ds1307->regs[DS1307_REG_HOUR] & 0x3f; + t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f); + t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f); + tmp = regs[DS1307_REG_HOUR] & 0x3f; t->tm_hour = bcd2bin(tmp); - t->tm_wday = bcd2bin(ds1307->regs[DS1307_REG_WDAY] & 0x07) - 1; - t->tm_mday = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f); - tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f; + t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1; + t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f); + tmp = regs[DS1307_REG_MONTH] & 0x1f; t->tm_mon = bcd2bin(tmp) - 1; - t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100; + t->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100; - if (ds1307->regs[chip->century_reg] & chip->century_bit && + if (regs[chip->century_reg] & chip->century_bit && IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY)) t->tm_year += 100; @@ -399,7 +443,7 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) const struct chip_desc *chip = &chips[ds1307->type]; int result; int tmp; - u8 *buf = ds1307->regs; + u8 regs[7]; dev_dbg(dev, "%s secs=%d, mins=%d, " "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", @@ -418,35 +462,36 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) return -EINVAL; #endif - buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec); - buf[DS1307_REG_MIN] = bin2bcd(t->tm_min); - buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour); - buf[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1); - buf[DS1307_REG_MDAY] = bin2bcd(t->tm_mday); - buf[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1); + regs[DS1307_REG_SECS] = bin2bcd(t->tm_sec); + regs[DS1307_REG_MIN] = bin2bcd(t->tm_min); + regs[DS1307_REG_HOUR] = bin2bcd(t->tm_hour); + regs[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1); + regs[DS1307_REG_MDAY] = bin2bcd(t->tm_mday); + regs[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1); /* assume 20YY not 19YY */ tmp = t->tm_year - 100; - buf[DS1307_REG_YEAR] = bin2bcd(tmp); + regs[DS1307_REG_YEAR] = bin2bcd(tmp); if (chip->century_enable_bit) - buf[chip->century_reg] |= chip->century_enable_bit; + regs[chip->century_reg] |= chip->century_enable_bit; if (t->tm_year > 199 && chip->century_bit) - buf[chip->century_reg] |= chip->century_bit; + regs[chip->century_reg] |= chip->century_bit; if (ds1307->type == mcp794xx) { /* * these bits were cleared when preparing the date/time * values and need to be set again before writing the - * buffer out to the device. + * regsfer out to the device. */ - buf[DS1307_REG_SECS] |= MCP794XX_BIT_ST; - buf[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN; + regs[DS1307_REG_SECS] |= MCP794XX_BIT_ST; + regs[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN; } - dev_dbg(dev, "%s: %7ph\n", "write", buf); + dev_dbg(dev, "%s: %7ph\n", "write", regs); - result = regmap_bulk_write(ds1307->regmap, ds1307->offset, buf, 7); + result = regmap_bulk_write(ds1307->regmap, chip->offset, regs, + sizeof(regs)); if (result) { dev_err(dev, "%s error %d\n", "write", result); return result; @@ -458,33 +503,34 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); int ret; + u8 regs[9]; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; /* read all ALARM1, ALARM2, and status registers at once */ ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, - ds1307->regs, 9); + regs, sizeof(regs)); if (ret) { dev_err(dev, "%s error %d\n", "alarm read", ret); return ret; } dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read", - &ds1307->regs[0], &ds1307->regs[4], &ds1307->regs[7]); + ®s[0], ®s[4], ®s[7]); /* * report alarm time (ALARM1); assume 24 hour and day-of-month modes, * and that all four fields are checked matches */ - t->time.tm_sec = bcd2bin(ds1307->regs[0] & 0x7f); - t->time.tm_min = bcd2bin(ds1307->regs[1] & 0x7f); - t->time.tm_hour = bcd2bin(ds1307->regs[2] & 0x3f); - t->time.tm_mday = bcd2bin(ds1307->regs[3] & 0x3f); + t->time.tm_sec = bcd2bin(regs[0] & 0x7f); + t->time.tm_min = bcd2bin(regs[1] & 0x7f); + t->time.tm_hour = bcd2bin(regs[2] & 0x3f); + t->time.tm_mday = bcd2bin(regs[3] & 0x3f); /* ... and status */ - t->enabled = !!(ds1307->regs[7] & DS1337_BIT_A1IE); - t->pending = !!(ds1307->regs[8] & DS1337_BIT_A1I); + t->enabled = !!(regs[7] & DS1337_BIT_A1IE); + t->pending = !!(regs[8] & DS1337_BIT_A1I); dev_dbg(dev, "%s secs=%d, mins=%d, " "hours=%d, mday=%d, enabled=%d, pending=%d\n", @@ -498,7 +544,7 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned char *buf = ds1307->regs; + unsigned char regs[9]; u8 control, status; int ret; @@ -512,33 +558,35 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* read current status of both alarms and the chip */ - ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, buf, 9); + ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, + sizeof(regs)); if (ret) { dev_err(dev, "%s error %d\n", "alarm write", ret); return ret; } - control = ds1307->regs[7]; - status = ds1307->regs[8]; + control = regs[7]; + status = regs[8]; dev_dbg(dev, "%s: %4ph, %3ph, %02x %02x\n", "alarm set (old status)", - &ds1307->regs[0], &ds1307->regs[4], control, status); + ®s[0], ®s[4], control, status); /* set ALARM1, using 24 hour and day-of-month modes */ - buf[0] = bin2bcd(t->time.tm_sec); - buf[1] = bin2bcd(t->time.tm_min); - buf[2] = bin2bcd(t->time.tm_hour); - buf[3] = bin2bcd(t->time.tm_mday); + regs[0] = bin2bcd(t->time.tm_sec); + regs[1] = bin2bcd(t->time.tm_min); + regs[2] = bin2bcd(t->time.tm_hour); + regs[3] = bin2bcd(t->time.tm_mday); /* set ALARM2 to non-garbage */ - buf[4] = 0; - buf[5] = 0; - buf[6] = 0; + regs[4] = 0; + regs[5] = 0; + regs[6] = 0; /* disable alarms */ - buf[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); - buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); + regs[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); + regs[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); - ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, buf, 9); + ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, + sizeof(regs)); if (ret) { dev_err(dev, "can't set alarm time\n"); return ret; @@ -547,8 +595,8 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) /* optionally enable ALARM1 */ if (t->enabled) { dev_dbg(dev, "alarm IRQ armed\n"); - buf[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ - regmap_write(ds1307->regmap, DS1337_REG_CONTROL, buf[7]); + regs[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ + regmap_write(ds1307->regmap, DS1337_REG_CONTROL, regs[7]); } return 0; @@ -584,11 +632,11 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { #define RX8130_REG_ALARM_HOUR 0x08 #define RX8130_REG_ALARM_WEEK_OR_DAY 0x09 #define RX8130_REG_EXTENSION 0x0c -#define RX8130_REG_EXTENSION_WADA (1 << 3) +#define RX8130_REG_EXTENSION_WADA BIT(3) #define RX8130_REG_FLAG 0x0d -#define RX8130_REG_FLAG_AF (1 << 3) +#define RX8130_REG_FLAG_AF BIT(3) #define RX8130_REG_CONTROL0 0x0e -#define RX8130_REG_CONTROL0_AIE (1 << 3) +#define RX8130_REG_CONTROL0_AIE BIT(3) static irqreturn_t rx8130_irq(int irq, void *dev_id) { @@ -600,7 +648,8 @@ static irqreturn_t rx8130_irq(int irq, void *dev_id) mutex_lock(lock); /* Read control registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); if (ret < 0) goto out; if (!(ctl[1] & RX8130_REG_FLAG_AF)) @@ -608,7 +657,8 @@ static irqreturn_t rx8130_irq(int irq, void *dev_id) ctl[1] &= ~RX8130_REG_FLAG_AF; ctl[2] &= ~RX8130_REG_CONTROL0_AIE; - ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); if (ret < 0) goto out; @@ -630,12 +680,14 @@ static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t) return -EINVAL; /* Read alarm registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, 3); + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, + sizeof(ald)); if (ret < 0) return ret; /* Read control registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); if (ret < 0) return ret; @@ -676,7 +728,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* Read control registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); if (ret < 0) return ret; @@ -684,7 +737,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) ctl[1] |= RX8130_REG_FLAG_AF; ctl[2] &= ~RX8130_REG_CONTROL0_AIE; - ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); if (ret < 0) return ret; @@ -693,7 +747,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) ald[1] = bin2bcd(t->time.tm_hour); ald[2] = bin2bcd(t->time.tm_mday); - ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, 3); + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, + sizeof(ald)); if (ret < 0) return ret; @@ -702,7 +757,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) ctl[2] |= RX8130_REG_CONTROL0_AIE; - return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); } static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) @@ -725,14 +781,6 @@ static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg); } -static const struct rtc_class_ops rx8130_rtc_ops = { - .read_time = ds1307_get_time, - .set_time = ds1307_set_time, - .read_alarm = rx8130_read_alarm, - .set_alarm = rx8130_set_alarm, - .alarm_irq_enable = rx8130_alarm_irq_enable, -}; - /*----------------------------------------------------------------------*/ /* @@ -748,11 +796,11 @@ static const struct rtc_class_ops rx8130_rtc_ops = { #define MCP794XX_REG_ALARM0_CTRL 0x0d #define MCP794XX_REG_ALARM1_BASE 0x11 #define MCP794XX_REG_ALARM1_CTRL 0x14 -# define MCP794XX_BIT_ALMX_IF (1 << 3) -# define MCP794XX_BIT_ALMX_C0 (1 << 4) -# define MCP794XX_BIT_ALMX_C1 (1 << 5) -# define MCP794XX_BIT_ALMX_C2 (1 << 6) -# define MCP794XX_BIT_ALMX_POL (1 << 7) +# define MCP794XX_BIT_ALMX_IF BIT(3) +# define MCP794XX_BIT_ALMX_C0 BIT(4) +# define MCP794XX_BIT_ALMX_C1 BIT(5) +# define MCP794XX_BIT_ALMX_C2 BIT(6) +# define MCP794XX_BIT_ALMX_POL BIT(7) # define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \ MCP794XX_BIT_ALMX_C1 | \ MCP794XX_BIT_ALMX_C2) @@ -793,37 +841,38 @@ out: static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); - u8 *regs = ds1307->regs; + u8 regs[10]; int ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; /* Read control and alarm 0 registers. */ - ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10); + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); if (ret) return ret; t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN); /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ - t->time.tm_sec = bcd2bin(ds1307->regs[3] & 0x7f); - t->time.tm_min = bcd2bin(ds1307->regs[4] & 0x7f); - t->time.tm_hour = bcd2bin(ds1307->regs[5] & 0x3f); - t->time.tm_wday = bcd2bin(ds1307->regs[6] & 0x7) - 1; - t->time.tm_mday = bcd2bin(ds1307->regs[7] & 0x3f); - t->time.tm_mon = bcd2bin(ds1307->regs[8] & 0x1f) - 1; + t->time.tm_sec = bcd2bin(regs[3] & 0x7f); + t->time.tm_min = bcd2bin(regs[4] & 0x7f); + t->time.tm_hour = bcd2bin(regs[5] & 0x3f); + t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1; + t->time.tm_mday = bcd2bin(regs[7] & 0x3f); + t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1; t->time.tm_year = -1; t->time.tm_yday = -1; t->time.tm_isdst = -1; dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " - "enabled=%d polarity=%d irq=%d match=%d\n", __func__, + "enabled=%d polarity=%d irq=%d match=%lu\n", __func__, t->time.tm_sec, t->time.tm_min, t->time.tm_hour, t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled, - !!(ds1307->regs[6] & MCP794XX_BIT_ALMX_POL), - !!(ds1307->regs[6] & MCP794XX_BIT_ALMX_IF), - (ds1307->regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); + !!(regs[6] & MCP794XX_BIT_ALMX_POL), + !!(regs[6] & MCP794XX_BIT_ALMX_IF), + (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); return 0; } @@ -831,7 +880,7 @@ static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned char *regs = ds1307->regs; + unsigned char regs[10]; int ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) @@ -844,7 +893,8 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* Read control and alarm 0 registers. */ - ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10); + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); if (ret) return ret; @@ -863,7 +913,8 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) /* Disable interrupt. We will not enable until completely programmed */ regs[0] &= ~MCP794XX_BIT_ALM0_EN; - ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10); + ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); if (ret) return ret; @@ -885,22 +936,15 @@ static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) enabled ? MCP794XX_BIT_ALM0_EN : 0); } -static const struct rtc_class_ops mcp794xx_rtc_ops = { - .read_time = ds1307_get_time, - .set_time = ds1307_set_time, - .read_alarm = mcp794xx_read_alarm, - .set_alarm = mcp794xx_set_alarm, - .alarm_irq_enable = mcp794xx_alarm_irq_enable, -}; - /*----------------------------------------------------------------------*/ static int ds1307_nvram_read(void *priv, unsigned int offset, void *val, size_t bytes) { struct ds1307 *ds1307 = priv; + const struct chip_desc *chip = &chips[ds1307->type]; - return regmap_bulk_read(ds1307->regmap, ds1307->nvram_offset + offset, + return regmap_bulk_read(ds1307->regmap, chip->nvram_offset + offset, val, bytes); } @@ -908,15 +952,16 @@ static int ds1307_nvram_write(void *priv, unsigned int offset, void *val, size_t bytes) { struct ds1307 *ds1307 = priv; + const struct chip_desc *chip = &chips[ds1307->type]; - return regmap_bulk_write(ds1307->regmap, ds1307->nvram_offset + offset, + return regmap_bulk_write(ds1307->regmap, chip->nvram_offset + offset, val, bytes); } /*----------------------------------------------------------------------*/ static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, - uint32_t ohms, bool diode) + u32 ohms, bool diode) { u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE : DS1307_TRICKLE_CHARGER_NO_DIODE; @@ -939,23 +984,23 @@ static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, return setup; } -static void ds1307_trickle_init(struct ds1307 *ds1307, - struct chip_desc *chip) +static u8 ds1307_trickle_init(struct ds1307 *ds1307, + const struct chip_desc *chip) { - uint32_t ohms = 0; + u32 ohms; bool diode = true; if (!chip->do_trickle_setup) - goto out; + return 0; + if (device_property_read_u32(ds1307->dev, "trickle-resistor-ohms", &ohms)) - goto out; + return 0; + if (device_property_read_bool(ds1307->dev, "trickle-diode-disable")) diode = false; - chip->trickle_charger_setup = chip->do_trickle_setup(ds1307, - ohms, diode); -out: - return; + + return chip->do_trickle_setup(ds1307, ohms, diode); } /*----------------------------------------------------------------------*/ @@ -995,7 +1040,7 @@ static int ds3231_hwmon_read_temp(struct device *dev, s32 *mC) } static ssize_t ds3231_hwmon_show_temp(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { int ret; s32 temp; @@ -1006,8 +1051,8 @@ static ssize_t ds3231_hwmon_show_temp(struct device *dev, return sprintf(buf, "%d\n", temp); } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ds3231_hwmon_show_temp, - NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, 0444, ds3231_hwmon_show_temp, + NULL, 0); static struct attribute *ds3231_hwmon_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, @@ -1023,7 +1068,8 @@ static void ds1307_hwmon_register(struct ds1307 *ds1307) return; dev = devm_hwmon_device_register_with_groups(ds1307->dev, ds1307->name, - ds1307, ds3231_hwmon_groups); + ds1307, + ds3231_hwmon_groups); if (IS_ERR(dev)) { dev_warn(ds1307->dev, "unable to register hwmon device %ld\n", PTR_ERR(dev)); @@ -1095,7 +1141,7 @@ static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw, } static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) + unsigned long *prate) { int i; @@ -1108,7 +1154,7 @@ static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate, } static int ds3231_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) + unsigned long parent_rate) { struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); int control = 0; @@ -1168,7 +1214,7 @@ static const struct clk_ops ds3231_clk_sqw_ops = { }; static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) + unsigned long parent_rate) { return 32768; } @@ -1259,7 +1305,7 @@ static int ds3231_clks_register(struct ds1307 *ds1307) /* optional override of the clockname */ of_property_read_string_index(node, "clock-output-names", i, - &init.name); + &init.name); ds1307->clks[i].init = &init; onecell->clks[i] = devm_clk_register(ds1307->dev, @@ -1309,22 +1355,14 @@ static int ds1307_probe(struct i2c_client *client, struct ds1307 *ds1307; int err = -ENODEV; int tmp, wday; - struct chip_desc *chip; - bool want_irq = false; + const struct chip_desc *chip; + bool want_irq; bool ds1307_can_wakeup_device = false; - unsigned char *buf; + unsigned char regs[8]; struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev); struct rtc_time tm; unsigned long timestamp; - - irq_handler_t irq_handler = ds1307_irq; - - static const int bbsqi_bitpos[] = { - [ds_1337] = 0, - [ds_1339] = DS1339_BIT_BBSQI, - [ds_3231] = DS3231_BIT_BBSQW, - }; - const struct rtc_class_ops *rtc_ops = &ds13xx_rtc_ops; + u8 trickle_charger_setup = 0; ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL); if (!ds1307) @@ -1333,7 +1371,6 @@ static int ds1307_probe(struct i2c_client *client, dev_set_drvdata(&client->dev, ds1307); ds1307->dev = &client->dev; ds1307->name = client->name; - ds1307->irq = client->irq; ds1307->regmap = devm_regmap_init_i2c(client, ®map_config); if (IS_ERR(ds1307->regmap)) { @@ -1361,23 +1398,22 @@ static int ds1307_probe(struct i2c_client *client, ds1307->type = acpi_id->driver_data; } + want_irq = client->irq > 0 && chip->alarm; + if (!pdata) - ds1307_trickle_init(ds1307, chip); + trickle_charger_setup = ds1307_trickle_init(ds1307, chip); else if (pdata->trickle_charger_setup) - chip->trickle_charger_setup = pdata->trickle_charger_setup; + trickle_charger_setup = pdata->trickle_charger_setup; - if (chip->trickle_charger_setup && chip->trickle_charger_reg) { + if (trickle_charger_setup && chip->trickle_charger_reg) { + trickle_charger_setup |= DS13XX_TRICKLE_CHARGER_MAGIC; dev_dbg(ds1307->dev, "writing trickle charger info 0x%x to 0x%x\n", - DS13XX_TRICKLE_CHARGER_MAGIC | chip->trickle_charger_setup, - chip->trickle_charger_reg); + trickle_charger_setup, chip->trickle_charger_reg); regmap_write(ds1307->regmap, chip->trickle_charger_reg, - DS13XX_TRICKLE_CHARGER_MAGIC | - chip->trickle_charger_setup); + trickle_charger_setup); } - buf = ds1307->regs; - #ifdef CONFIG_OF /* * For devices with no IRQ directly connected to the SoC, the RTC chip @@ -1387,31 +1423,27 @@ static int ds1307_probe(struct i2c_client *client, * This will guarantee the 'wakealarm' sysfs entry is available on the device, * if supported by the RTC. */ - if (of_property_read_bool(client->dev.of_node, "wakeup-source")) { - ds1307_can_wakeup_device = true; - } - /* Intersil ISL12057 DT backward compatibility */ - if (of_property_read_bool(client->dev.of_node, - "isil,irq2-can-wakeup-machine")) { + if (chip->alarm && of_property_read_bool(client->dev.of_node, + "wakeup-source")) ds1307_can_wakeup_device = true; - } #endif switch (ds1307->type) { case ds_1337: case ds_1339: + case ds_1341: case ds_3231: /* get registers that the "rtc" read below won't read... */ err = regmap_bulk_read(ds1307->regmap, DS1337_REG_CONTROL, - buf, 2); + regs, 2); if (err) { dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } /* oscillator off? turn it on, so clock can tick. */ - if (ds1307->regs[0] & DS1337_BIT_nEOSC) - ds1307->regs[0] &= ~DS1337_BIT_nEOSC; + if (regs[0] & DS1337_BIT_nEOSC) + regs[0] &= ~DS1337_BIT_nEOSC; /* * Using IRQ or defined as wakeup-source? @@ -1419,114 +1451,92 @@ static int ds1307_probe(struct i2c_client *client, * For some variants, be sure alarms can trigger when we're * running on Vbackup (BBSQI/BBSQW) */ - if (chip->alarm && (ds1307->irq > 0 || - ds1307_can_wakeup_device)) { - ds1307->regs[0] |= DS1337_BIT_INTCN - | bbsqi_bitpos[ds1307->type]; - ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); - - want_irq = true; + if (want_irq || ds1307_can_wakeup_device) { + regs[0] |= DS1337_BIT_INTCN | chip->bbsqi_bit; + regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); } regmap_write(ds1307->regmap, DS1337_REG_CONTROL, - ds1307->regs[0]); + regs[0]); /* oscillator fault? clear flag, and warn */ - if (ds1307->regs[1] & DS1337_BIT_OSF) { + if (regs[1] & DS1337_BIT_OSF) { regmap_write(ds1307->regmap, DS1337_REG_STATUS, - ds1307->regs[1] & ~DS1337_BIT_OSF); + regs[1] & ~DS1337_BIT_OSF); dev_warn(ds1307->dev, "SET TIME!\n"); } break; case rx_8025: err = regmap_bulk_read(ds1307->regmap, - RX8025_REG_CTRL1 << 4 | 0x08, buf, 2); + RX8025_REG_CTRL1 << 4 | 0x08, regs, 2); if (err) { dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } /* oscillator off? turn it on, so clock can tick. */ - if (!(ds1307->regs[1] & RX8025_BIT_XST)) { - ds1307->regs[1] |= RX8025_BIT_XST; + if (!(regs[1] & RX8025_BIT_XST)) { + regs[1] |= RX8025_BIT_XST; regmap_write(ds1307->regmap, RX8025_REG_CTRL2 << 4 | 0x08, - ds1307->regs[1]); + regs[1]); dev_warn(ds1307->dev, "oscillator stop detected - SET TIME!\n"); } - if (ds1307->regs[1] & RX8025_BIT_PON) { - ds1307->regs[1] &= ~RX8025_BIT_PON; + if (regs[1] & RX8025_BIT_PON) { + regs[1] &= ~RX8025_BIT_PON; regmap_write(ds1307->regmap, RX8025_REG_CTRL2 << 4 | 0x08, - ds1307->regs[1]); + regs[1]); dev_warn(ds1307->dev, "power-on detected\n"); } - if (ds1307->regs[1] & RX8025_BIT_VDET) { - ds1307->regs[1] &= ~RX8025_BIT_VDET; + if (regs[1] & RX8025_BIT_VDET) { + regs[1] &= ~RX8025_BIT_VDET; regmap_write(ds1307->regmap, RX8025_REG_CTRL2 << 4 | 0x08, - ds1307->regs[1]); + regs[1]); dev_warn(ds1307->dev, "voltage drop detected\n"); } /* make sure we are running in 24hour mode */ - if (!(ds1307->regs[0] & RX8025_BIT_2412)) { + if (!(regs[0] & RX8025_BIT_2412)) { u8 hour; /* switch to 24 hour mode */ regmap_write(ds1307->regmap, RX8025_REG_CTRL1 << 4 | 0x08, - ds1307->regs[0] | RX8025_BIT_2412); + regs[0] | RX8025_BIT_2412); err = regmap_bulk_read(ds1307->regmap, RX8025_REG_CTRL1 << 4 | 0x08, - buf, 2); + regs, 2); if (err) { dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } /* correct hour */ - hour = bcd2bin(ds1307->regs[DS1307_REG_HOUR]); + hour = bcd2bin(regs[DS1307_REG_HOUR]); if (hour == 12) hour = 0; - if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM) hour += 12; regmap_write(ds1307->regmap, DS1307_REG_HOUR << 4 | 0x08, hour); } break; - case rx_8130: - ds1307->offset = 0x10; /* Seconds starts at 0x10 */ - rtc_ops = &rx8130_rtc_ops; - if (chip->alarm && ds1307->irq > 0) { - irq_handler = rx8130_irq; - want_irq = true; - } - break; - case ds_1388: - ds1307->offset = 1; /* Seconds starts at 1 */ - break; - case mcp794xx: - rtc_ops = &mcp794xx_rtc_ops; - if (chip->alarm && (ds1307->irq > 0 || - ds1307_can_wakeup_device)) { - irq_handler = mcp794xx_irq; - want_irq = true; - } - break; default: break; } read_rtc: /* read RTC registers */ - err = regmap_bulk_read(ds1307->regmap, ds1307->offset, buf, 8); + err = regmap_bulk_read(ds1307->regmap, chip->offset, regs, + sizeof(regs)); if (err) { dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; @@ -1537,7 +1547,7 @@ read_rtc: * specify the extra bits as must-be-zero, but there are * still a few values that are clearly out-of-range. */ - tmp = ds1307->regs[DS1307_REG_SECS]; + tmp = regs[DS1307_REG_SECS]; switch (ds1307->type) { case ds_1307: case m41t0: @@ -1556,10 +1566,10 @@ read_rtc: regmap_write(ds1307->regmap, DS1307_REG_SECS, 0); /* oscillator fault? clear flag, and warn */ - if (ds1307->regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) { + if (regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) { regmap_write(ds1307->regmap, DS1307_REG_CONTROL, - ds1307->regs[DS1307_REG_CONTROL] & - ~DS1338_BIT_OSF); + regs[DS1307_REG_CONTROL] & + ~DS1338_BIT_OSF); dev_warn(ds1307->dev, "SET TIME!\n"); goto read_rtc; } @@ -1583,9 +1593,9 @@ read_rtc: break; case mcp794xx: /* make sure that the backup battery is enabled */ - if (!(ds1307->regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) { + if (!(regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) { regmap_write(ds1307->regmap, DS1307_REG_WDAY, - ds1307->regs[DS1307_REG_WDAY] | + regs[DS1307_REG_WDAY] | MCP794XX_BIT_VBATEN); } @@ -1602,7 +1612,7 @@ read_rtc: break; } - tmp = ds1307->regs[DS1307_REG_HOUR]; + tmp = regs[DS1307_REG_HOUR]; switch (ds1307->type) { case ds_1340: case m41t0: @@ -1625,9 +1635,9 @@ read_rtc: tmp = bcd2bin(tmp & 0x1f); if (tmp == 12) tmp = 0; - if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM) tmp += 12; - regmap_write(ds1307->regmap, ds1307->offset + DS1307_REG_HOUR, + regmap_write(ds1307->regmap, chip->offset + DS1307_REG_HOUR, bin2bcd(tmp)); } @@ -1650,19 +1660,16 @@ read_rtc: MCP794XX_REG_WEEKDAY_WDAY_MASK, tm.tm_wday + 1); - if (want_irq) { + if (want_irq || ds1307_can_wakeup_device) { device_set_wakeup_capable(ds1307->dev, true); set_bit(HAS_ALARM, &ds1307->flags); } ds1307->rtc = devm_rtc_allocate_device(ds1307->dev); - if (IS_ERR(ds1307->rtc)) { + if (IS_ERR(ds1307->rtc)) return PTR_ERR(ds1307->rtc); - } - if (ds1307_can_wakeup_device && ds1307->irq <= 0) { - /* Disable request for an IRQ */ - want_irq = false; + if (ds1307_can_wakeup_device && !want_irq) { dev_info(ds1307->dev, "'wakeup-source' is set, request for an IRQ is disabled!\n"); /* We cannot support UIE mode if we do not have an IRQ line */ @@ -1670,8 +1677,8 @@ read_rtc: } if (want_irq) { - err = devm_request_threaded_irq(ds1307->dev, - ds1307->irq, NULL, irq_handler, + err = devm_request_threaded_irq(ds1307->dev, client->irq, NULL, + chip->irq_handler ?: ds1307_irq, IRQF_SHARED | IRQF_ONESHOT, ds1307->name, ds1307); if (err) { @@ -1679,8 +1686,9 @@ read_rtc: device_set_wakeup_capable(ds1307->dev, false); clear_bit(HAS_ALARM, &ds1307->flags); dev_err(ds1307->dev, "unable to request IRQ!\n"); - } else + } else { dev_dbg(ds1307->dev, "got IRQ %d\n", client->irq); + } } if (chip->nvram_size) { @@ -1691,13 +1699,12 @@ read_rtc: ds1307->nvmem_cfg.reg_read = ds1307_nvram_read; ds1307->nvmem_cfg.reg_write = ds1307_nvram_write; ds1307->nvmem_cfg.priv = ds1307; - ds1307->nvram_offset = chip->nvram_offset; ds1307->rtc->nvmem_config = &ds1307->nvmem_cfg; ds1307->rtc->nvram_old_abi = true; } - ds1307->rtc->ops = rtc_ops; + ds1307->rtc->ops = chip->rtc_ops ?: &ds13xx_rtc_ops; err = rtc_register_device(ds1307->rtc); if (err) return err; diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 7bf46bfe11a4..9caaccccaa57 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -190,7 +190,7 @@ static int ds1672_probe(struct i2c_client *client, return 0; } -static struct i2c_device_id ds1672_id[] = { +static const struct i2c_device_id ds1672_id[] = { { "ds1672", 0 }, { } }; diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c index 4f4930a2004c..b0ef8cfe742d 100644 --- a/drivers/rtc/rtc-em3027.c +++ b/drivers/rtc/rtc-em3027.c @@ -132,7 +132,7 @@ static int em3027_probe(struct i2c_client *client, return 0; } -static struct i2c_device_id em3027_id[] = { +static const struct i2c_device_id em3027_id[] = { { "em3027", 0 }, { } }; diff --git a/drivers/rtc/rtc-goldfish.c b/drivers/rtc/rtc-goldfish.c new file mode 100644 index 000000000000..d67769265185 --- /dev/null +++ b/drivers/rtc/rtc-goldfish.c @@ -0,0 +1,237 @@ +/* drivers/rtc/rtc-goldfish.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2017 Imagination Technologies Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/io.h> + +#define TIMER_TIME_LOW 0x00 /* get low bits of current time */ + /* and update TIMER_TIME_HIGH */ +#define TIMER_TIME_HIGH 0x04 /* get high bits of time at last */ + /* TIMER_TIME_LOW read */ +#define TIMER_ALARM_LOW 0x08 /* set low bits of alarm and */ + /* activate it */ +#define TIMER_ALARM_HIGH 0x0c /* set high bits of next alarm */ +#define TIMER_IRQ_ENABLED 0x10 +#define TIMER_CLEAR_ALARM 0x14 +#define TIMER_ALARM_STATUS 0x18 +#define TIMER_CLEAR_INTERRUPT 0x1c + +struct goldfish_rtc { + void __iomem *base; + int irq; + struct rtc_device *rtc; +}; + +static int goldfish_rtc_read_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + u64 rtc_alarm; + u64 rtc_alarm_low; + u64 rtc_alarm_high; + void __iomem *base; + struct goldfish_rtc *rtcdrv; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + rtc_alarm_low = readl(base + TIMER_ALARM_LOW); + rtc_alarm_high = readl(base + TIMER_ALARM_HIGH); + rtc_alarm = (rtc_alarm_high << 32) | rtc_alarm_low; + + do_div(rtc_alarm, NSEC_PER_SEC); + memset(alrm, 0, sizeof(struct rtc_wkalrm)); + + rtc_time_to_tm(rtc_alarm, &alrm->time); + + if (readl(base + TIMER_ALARM_STATUS)) + alrm->enabled = 1; + else + alrm->enabled = 0; + + return 0; +} + +static int goldfish_rtc_set_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + struct goldfish_rtc *rtcdrv; + unsigned long rtc_alarm; + u64 rtc_alarm64; + u64 rtc_status_reg; + void __iomem *base; + int ret = 0; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + if (alrm->enabled) { + ret = rtc_tm_to_time(&alrm->time, &rtc_alarm); + if (ret != 0) + return ret; + + rtc_alarm64 = rtc_alarm * NSEC_PER_SEC; + writel((rtc_alarm64 >> 32), base + TIMER_ALARM_HIGH); + writel(rtc_alarm64, base + TIMER_ALARM_LOW); + } else { + /* + * if this function was called with enabled=0 + * then it could mean that the application is + * trying to cancel an ongoing alarm + */ + rtc_status_reg = readl(base + TIMER_ALARM_STATUS); + if (rtc_status_reg) + writel(1, base + TIMER_CLEAR_ALARM); + } + + return ret; +} + +static int goldfish_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + void __iomem *base; + struct goldfish_rtc *rtcdrv; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + if (enabled) + writel(1, base + TIMER_IRQ_ENABLED); + else + writel(0, base + TIMER_IRQ_ENABLED); + + return 0; +} + +static irqreturn_t goldfish_rtc_interrupt(int irq, void *dev_id) +{ + struct goldfish_rtc *rtcdrv = dev_id; + void __iomem *base = rtcdrv->base; + + writel(1, base + TIMER_CLEAR_INTERRUPT); + + rtc_update_irq(rtcdrv->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int goldfish_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct goldfish_rtc *rtcdrv; + void __iomem *base; + u64 time_high; + u64 time_low; + u64 time; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + time_low = readl(base + TIMER_TIME_LOW); + time_high = readl(base + TIMER_TIME_HIGH); + time = (time_high << 32) | time_low; + + do_div(time, NSEC_PER_SEC); + + rtc_time_to_tm(time, tm); + + return 0; +} + +static int goldfish_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct goldfish_rtc *rtcdrv; + void __iomem *base; + unsigned long now; + u64 now64; + int ret; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + ret = rtc_tm_to_time(tm, &now); + if (ret == 0) { + now64 = now * NSEC_PER_SEC; + writel((now64 >> 32), base + TIMER_TIME_HIGH); + writel(now64, base + TIMER_TIME_LOW); + } + + return ret; +} + +static const struct rtc_class_ops goldfish_rtc_ops = { + .read_time = goldfish_rtc_read_time, + .set_time = goldfish_rtc_set_time, + .read_alarm = goldfish_rtc_read_alarm, + .set_alarm = goldfish_rtc_set_alarm, + .alarm_irq_enable = goldfish_rtc_alarm_irq_enable +}; + +static int goldfish_rtc_probe(struct platform_device *pdev) +{ + struct goldfish_rtc *rtcdrv; + struct resource *r; + int err; + + rtcdrv = devm_kzalloc(&pdev->dev, sizeof(*rtcdrv), GFP_KERNEL); + if (!rtcdrv) + return -ENOMEM; + + platform_set_drvdata(pdev, rtcdrv); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) + return -ENODEV; + + rtcdrv->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(rtcdrv->base)) + return -ENODEV; + + rtcdrv->irq = platform_get_irq(pdev, 0); + if (rtcdrv->irq < 0) + return -ENODEV; + + rtcdrv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &goldfish_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtcdrv->rtc)) + return PTR_ERR(rtcdrv->rtc); + + err = devm_request_irq(&pdev->dev, rtcdrv->irq, + goldfish_rtc_interrupt, + 0, pdev->name, rtcdrv); + if (err) + return err; + + return 0; +} + +static const struct of_device_id goldfish_rtc_of_match[] = { + { .compatible = "google,goldfish-rtc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_rtc_of_match); + +static struct platform_driver goldfish_rtc = { + .probe = goldfish_rtc_probe, + .driver = { + .name = "goldfish_rtc", + .of_match_table = goldfish_rtc_of_match, + } +}; + +module_platform_driver(goldfish_rtc); diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 8940e9e43ea0..f4c070ea8384 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -440,28 +440,6 @@ static int m41t80_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(m41t80_pm, m41t80_suspend, m41t80_resume); -static ssize_t flags_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - int val; - - val = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); - if (val < 0) - return val; - return sprintf(buf, "%#x\n", val); -} -static DEVICE_ATTR_RO(flags); - -static struct attribute *attrs[] = { - &dev_attr_flags.attr, - NULL, -}; - -static struct attribute_group attr_group = { - .attrs = attrs, -}; - #ifdef CONFIG_COMMON_CLK #define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw) @@ -912,13 +890,6 @@ static struct notifier_block wdt_notifier = { ***************************************************************************** */ -static void m41t80_remove_sysfs_group(void *_dev) -{ - struct device *dev = _dev; - - sysfs_remove_group(&dev->kobj, &attr_group); -} - static int m41t80_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -927,6 +898,7 @@ static int m41t80_probe(struct i2c_client *client, struct rtc_device *rtc = NULL; struct rtc_time tm; struct m41t80_data *m41t80_data = NULL; + bool wakeup_source = false; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -947,6 +919,10 @@ static int m41t80_probe(struct i2c_client *client, m41t80_data->features = id->driver_data; i2c_set_clientdata(client, m41t80_data); +#ifdef CONFIG_OF + wakeup_source = of_property_read_bool(client->dev.of_node, + "wakeup-source"); +#endif if (client->irq > 0) { rc = devm_request_threaded_irq(&client->dev, client->irq, NULL, m41t80_handle_irq, @@ -955,14 +931,16 @@ static int m41t80_probe(struct i2c_client *client, if (rc) { dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); client->irq = 0; - } else { - m41t80_rtc_ops.read_alarm = m41t80_read_alarm; - m41t80_rtc_ops.set_alarm = m41t80_set_alarm; - m41t80_rtc_ops.alarm_irq_enable = m41t80_alarm_irq_enable; - /* Enable the wakealarm */ - device_init_wakeup(&client->dev, true); + wakeup_source = false; } } + if (client->irq > 0 || wakeup_source) { + m41t80_rtc_ops.read_alarm = m41t80_read_alarm; + m41t80_rtc_ops.set_alarm = m41t80_set_alarm; + m41t80_rtc_ops.alarm_irq_enable = m41t80_alarm_irq_enable; + /* Enable the wakealarm */ + device_init_wakeup(&client->dev, true); + } rtc = devm_rtc_device_register(&client->dev, client->name, &m41t80_rtc_ops, THIS_MODULE); @@ -970,6 +948,10 @@ static int m41t80_probe(struct i2c_client *client, return PTR_ERR(rtc); m41t80_data->rtc = rtc; + if (client->irq <= 0) { + /* We cannot support UIE mode if we do not have an IRQ line */ + rtc->uie_unsupported = 1; + } /* Make sure HT (Halt Update) bit is cleared */ rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR); @@ -1004,21 +986,6 @@ static int m41t80_probe(struct i2c_client *client, return rc; } - /* Export sysfs entries */ - rc = sysfs_create_group(&(&client->dev)->kobj, &attr_group); - if (rc) { - dev_err(&client->dev, "Failed to create sysfs group: %d\n", rc); - return rc; - } - - rc = devm_add_action_or_reset(&client->dev, m41t80_remove_sysfs_group, - &client->dev); - if (rc) { - dev_err(&client->dev, - "Failed to add sysfs cleanup action: %d\n", rc); - return rc; - } - #ifdef CONFIG_RTC_DRV_M41T80_WDT if (m41t80_data->features & M41T80_FEATURE_HT) { save_client = client; diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index 48b6b411f8b2..cbdc86a560ba 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -226,7 +226,7 @@ max6900_probe(struct i2c_client *client, const struct i2c_device_id *id) return 0; } -static struct i2c_device_id max6900_id[] = { +static const struct i2c_device_id max6900_id[] = { { "max6900", 0 }, { } }; diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c index 16d129a0bb3b..67d6fc2d23e6 100644 --- a/drivers/rtc/rtc-max8925.c +++ b/drivers/rtc/rtc-max8925.c @@ -234,8 +234,6 @@ static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77); else ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x0); - if (ret < 0) - goto out; out: return ret; } diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 401f46d8f21b..bce427d202ee 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -238,26 +238,6 @@ static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -/* - * Clear all interrupts and release the IRQ - */ -static void mxc_rtc_release(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - void __iomem *ioaddr = pdata->ioaddr; - - spin_lock_irq(&pdata->rtc->irq_lock); - - /* Disable all rtc interrupts */ - writew(0, ioaddr + RTC_RTCIENR); - - /* Clear all interrupt status */ - writew(0xffffffff, ioaddr + RTC_RTCISR); - - spin_unlock_irq(&pdata->rtc->irq_lock); -} - static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { mxc_rtc_irq_enable(dev, RTC_ALM_BIT, enabled); @@ -343,7 +323,6 @@ static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) /* RTC layer */ static const struct rtc_class_ops mxc_rtc_ops = { - .release = mxc_rtc_release, .read_time = mxc_rtc_read_time, .set_mmss64 = mxc_rtc_set_mmss, .read_alarm = mxc_rtc_read_alarm, diff --git a/drivers/rtc/rtc-puv3.c b/drivers/rtc/rtc-puv3.c index c0a6e638c672..9e83be32ff43 100644 --- a/drivers/rtc/rtc-puv3.c +++ b/drivers/rtc/rtc-puv3.c @@ -157,49 +157,7 @@ static int puv3_rtc_proc(struct device *dev, struct seq_file *seq) return 0; } -static int puv3_rtc_open(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc_dev = platform_get_drvdata(pdev); - int ret; - - ret = request_irq(puv3_rtc_alarmno, puv3_rtc_alarmirq, - 0, "pkunity-rtc alarm", rtc_dev); - - if (ret) { - dev_err(dev, "IRQ%d error %d\n", puv3_rtc_alarmno, ret); - return ret; - } - - ret = request_irq(puv3_rtc_tickno, puv3_rtc_tickirq, - 0, "pkunity-rtc tick", rtc_dev); - - if (ret) { - dev_err(dev, "IRQ%d error %d\n", puv3_rtc_tickno, ret); - goto tick_err; - } - - return ret; - - tick_err: - free_irq(puv3_rtc_alarmno, rtc_dev); - return ret; -} - -static void puv3_rtc_release(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc_dev = platform_get_drvdata(pdev); - - /* do not clear AIE here, it may be needed for wake */ - puv3_rtc_setpie(dev, 0); - free_irq(puv3_rtc_alarmno, rtc_dev); - free_irq(puv3_rtc_tickno, rtc_dev); -} - static const struct rtc_class_ops puv3_rtcops = { - .open = puv3_rtc_open, - .release = puv3_rtc_release, .read_time = puv3_rtc_gettime, .set_time = puv3_rtc_settime, .read_alarm = puv3_rtc_getalarm, @@ -222,10 +180,6 @@ static void puv3_rtc_enable(struct device *dev, int en) static int puv3_rtc_remove(struct platform_device *dev) { - struct rtc_device *rtc = platform_get_drvdata(dev); - - rtc_device_unregister(rtc); - puv3_rtc_setpie(&dev->dev, 0); puv3_rtc_setaie(&dev->dev, 0); @@ -259,6 +213,24 @@ static int puv3_rtc_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "PKUnity_rtc: tick irq %d, alarm irq %d\n", puv3_rtc_tickno, puv3_rtc_alarmno); + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + ret = devm_request_irq(&pdev->dev, puv3_rtc_alarmno, puv3_rtc_alarmirq, + 0, "pkunity-rtc alarm", rtc); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", puv3_rtc_alarmno, ret); + return ret; + } + + ret = devm_request_irq(&pdev->dev, puv3_rtc_tickno, puv3_rtc_tickirq, + 0, "pkunity-rtc tick", rtc); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", puv3_rtc_tickno, ret); + return ret; + } + /* get the memory region */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { @@ -278,12 +250,10 @@ static int puv3_rtc_probe(struct platform_device *pdev) puv3_rtc_enable(&pdev->dev, 1); /* register RTC and exit */ - rtc = rtc_device_register("pkunity", &pdev->dev, &puv3_rtcops, - THIS_MODULE); - - if (IS_ERR(rtc)) { + rtc->ops = &puv3_rtcops; + ret = rtc_register_device(rtc); + if (ret) { dev_err(&pdev->dev, "cannot attach rtc\n"); - ret = PTR_ERR(rtc); goto err_nortc; } diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index fe4985b54608..47304f5664d8 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -348,7 +348,7 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) dev_err(dev, "No alarm IRQ resource defined\n"); return -ENXIO; } - pxa_rtc_open(dev); + pxa_rtc->base = devm_ioremap(dev, pxa_rtc->ress->start, resource_size(pxa_rtc->ress)); if (!pxa_rtc->base) { @@ -356,6 +356,8 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) return -ENOMEM; } + pxa_rtc_open(dev); + sa1100_rtc->rcnr = pxa_rtc->base + 0x0; sa1100_rtc->rtsr = pxa_rtc->base + 0x8; sa1100_rtc->rtar = pxa_rtc->base + 0x4; diff --git a/drivers/rtc/rtc-rtd119x.c b/drivers/rtc/rtc-rtd119x.c new file mode 100644 index 000000000000..b233559d950b --- /dev/null +++ b/drivers/rtc/rtc-rtd119x.c @@ -0,0 +1,242 @@ +/* + * Realtek RTD129x RTC + * + * Copyright (c) 2017 Andreas Färber + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/spinlock.h> + +#define RTD_RTCSEC 0x00 +#define RTD_RTCMIN 0x04 +#define RTD_RTCHR 0x08 +#define RTD_RTCDATE1 0x0c +#define RTD_RTCDATE2 0x10 +#define RTD_RTCACR 0x28 +#define RTD_RTCEN 0x2c +#define RTD_RTCCR 0x30 + +#define RTD_RTCSEC_RTCSEC_MASK 0x7f + +#define RTD_RTCMIN_RTCMIN_MASK 0x3f + +#define RTD_RTCHR_RTCHR_MASK 0x1f + +#define RTD_RTCDATE1_RTCDATE1_MASK 0xff + +#define RTD_RTCDATE2_RTCDATE2_MASK 0x7f + +#define RTD_RTCACR_RTCPWR BIT(7) + +#define RTD_RTCEN_RTCEN_MASK 0xff + +#define RTD_RTCCR_RTCRST BIT(6) + +struct rtd119x_rtc { + void __iomem *base; + struct clk *clk; + struct rtc_device *rtcdev; + unsigned int base_year; +}; + +static inline int rtd119x_rtc_days_in_year(int year) +{ + return 365 + (is_leap_year(year) ? 1 : 0); +} + +static void rtd119x_rtc_reset(struct device *dev) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + u32 val; + + val = readl_relaxed(data->base + RTD_RTCCR); + val |= RTD_RTCCR_RTCRST; + writel_relaxed(val, data->base + RTD_RTCCR); + + val &= ~RTD_RTCCR_RTCRST; + writel(val, data->base + RTD_RTCCR); +} + +static void rtd119x_rtc_set_enabled(struct device *dev, bool enable) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + u32 val; + + val = readl_relaxed(data->base + RTD_RTCEN); + if (enable) { + if ((val & RTD_RTCEN_RTCEN_MASK) == 0x5a) + return; + writel_relaxed(0x5a, data->base + RTD_RTCEN); + } else { + writel_relaxed(0, data->base + RTD_RTCEN); + } +} + +static int rtd119x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + s32 day; + u32 sec; + unsigned int year; + int tries = 0; + + while (true) { + tm->tm_sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1; + tm->tm_min = readl_relaxed(data->base + RTD_RTCMIN) & RTD_RTCMIN_RTCMIN_MASK; + tm->tm_hour = readl_relaxed(data->base + RTD_RTCHR) & RTD_RTCHR_RTCHR_MASK; + day = readl_relaxed(data->base + RTD_RTCDATE1) & RTD_RTCDATE1_RTCDATE1_MASK; + day |= (readl_relaxed(data->base + RTD_RTCDATE2) & RTD_RTCDATE2_RTCDATE2_MASK) << 8; + sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1; + tries++; + + if (sec == tm->tm_sec) + break; + + if (tries >= 3) + return -EINVAL; + } + if (tries > 1) + dev_dbg(dev, "%s: needed %i tries\n", __func__, tries); + + year = data->base_year; + while (day >= rtd119x_rtc_days_in_year(year)) { + day -= rtd119x_rtc_days_in_year(year); + year++; + } + tm->tm_year = year - 1900; + tm->tm_yday = day; + + tm->tm_mon = 0; + while (day >= rtc_month_days(tm->tm_mon, year)) { + day -= rtc_month_days(tm->tm_mon, year); + tm->tm_mon++; + } + tm->tm_mday = day + 1; + + return 0; +} + +static int rtd119x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + unsigned int day; + int i; + + if (1900 + tm->tm_year < data->base_year) + return -EINVAL; + + day = 0; + for (i = data->base_year; i < 1900 + tm->tm_year; i++) + day += rtd119x_rtc_days_in_year(i); + + day += tm->tm_yday; + if (day > 0x7fff) + return -EINVAL; + + rtd119x_rtc_set_enabled(dev, false); + + writel_relaxed((tm->tm_sec << 1) & RTD_RTCSEC_RTCSEC_MASK, data->base + RTD_RTCSEC); + writel_relaxed(tm->tm_min & RTD_RTCMIN_RTCMIN_MASK, data->base + RTD_RTCMIN); + writel_relaxed(tm->tm_hour & RTD_RTCHR_RTCHR_MASK, data->base + RTD_RTCHR); + writel_relaxed(day & RTD_RTCDATE1_RTCDATE1_MASK, data->base + RTD_RTCDATE1); + writel_relaxed((day >> 8) & RTD_RTCDATE2_RTCDATE2_MASK, data->base + RTD_RTCDATE2); + + rtd119x_rtc_set_enabled(dev, true); + + return 0; +} + +static const struct rtc_class_ops rtd119x_rtc_ops = { + .read_time = rtd119x_rtc_read_time, + .set_time = rtd119x_rtc_set_time, +}; + +static const struct of_device_id rtd119x_rtc_dt_ids[] = { + { .compatible = "realtek,rtd1295-rtc" }, + { } +}; + +static int rtd119x_rtc_probe(struct platform_device *pdev) +{ + struct rtd119x_rtc *data; + struct resource *res; + u32 val; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + data->base_year = 2014; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->clk = of_clk_get(pdev->dev.of_node, 0); + if (IS_ERR(data->clk)) + return PTR_ERR(data->clk); + + ret = clk_prepare_enable(data->clk); + if (ret) { + clk_put(data->clk); + return ret; + } + + val = readl_relaxed(data->base + RTD_RTCACR); + if (!(val & RTD_RTCACR_RTCPWR)) { + writel_relaxed(RTD_RTCACR_RTCPWR, data->base + RTD_RTCACR); + + rtd119x_rtc_reset(&pdev->dev); + + writel_relaxed(0, data->base + RTD_RTCMIN); + writel_relaxed(0, data->base + RTD_RTCHR); + writel_relaxed(0, data->base + RTD_RTCDATE1); + writel_relaxed(0, data->base + RTD_RTCDATE2); + } + + rtd119x_rtc_set_enabled(&pdev->dev, true); + + data->rtcdev = devm_rtc_device_register(&pdev->dev, "rtc", + &rtd119x_rtc_ops, THIS_MODULE); + if (IS_ERR(data->rtcdev)) { + dev_err(&pdev->dev, "failed to register rtc device"); + clk_disable_unprepare(data->clk); + clk_put(data->clk); + return PTR_ERR(data->rtcdev); + } + + return 0; +} + +static int rtd119x_rtc_remove(struct platform_device *pdev) +{ + struct rtd119x_rtc *data = platform_get_drvdata(pdev); + + rtd119x_rtc_set_enabled(&pdev->dev, false); + + clk_disable_unprepare(data->clk); + clk_put(data->clk); + + return 0; +} + +static struct platform_driver rtd119x_rtc_driver = { + .probe = rtd119x_rtc_probe, + .remove = rtd119x_rtc_remove, + .driver = { + .name = "rtd1295-rtc", + .of_match_table = rtd119x_rtc_dt_ids, + }, +}; +builtin_platform_driver(rtd119x_rtc_driver); diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index 85fa1da03762..aa09771de04f 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -868,7 +868,7 @@ static int rv3029_i2c_probe(struct i2c_client *client, return rv3029_probe(&client->dev, regmap, client->irq, client->name); } -static struct i2c_device_id rv3029_id[] = { +static const struct i2c_device_id rv3029_id[] = { { "rv3029", 0 }, { "rv3029c2", 0 }, { } diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index 449820eeefe8..7067bca5c20d 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -106,33 +106,12 @@ static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len) return 0; } -/* - * Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset. - * To keep the information if an irq is pending, pass the value read from - * STATUS1 to the caller. - */ -static int s35390a_reset(struct s35390a *s35390a, char *status1) +static int s35390a_init(struct s35390a *s35390a) { char buf; int ret; unsigned initcount = 0; - ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1); - if (ret < 0) - return ret; - - if (*status1 & S35390A_FLAG_POC) - /* - * Do not communicate for 0.5 seconds since the power-on - * detection circuit is in operation. - */ - msleep(500); - else if (!(*status1 & S35390A_FLAG_BLD)) - /* - * If both POC and BLD are unset everything is fine. - */ - return 0; - /* * At least one of POC and BLD are set, so reinitialise chip. Keeping * this information in the hardware to know later that the time isn't @@ -142,7 +121,6 @@ static int s35390a_reset(struct s35390a *s35390a, char *status1) * The 24H bit is kept over reset, so set it already here. */ initialize: - *status1 = S35390A_FLAG_24H; buf = S35390A_FLAG_RESET | S35390A_FLAG_24H; ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1); @@ -165,6 +143,34 @@ initialize: return 1; } +/* + * Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset. + * To keep the information if an irq is pending, pass the value read from + * STATUS1 to the caller. + */ +static int s35390a_read_status(struct s35390a *s35390a, char *status1) +{ + int ret; + + ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1); + if (ret < 0) + return ret; + + if (*status1 & S35390A_FLAG_POC) { + /* + * Do not communicate for 0.5 seconds since the power-on + * detection circuit is in operation. + */ + msleep(500); + return 1; + } else if (*status1 & S35390A_FLAG_BLD) + return 1; + /* + * If both POC and BLD are unset everything is fine. + */ + return 0; +} + static int s35390a_disable_test_mode(struct s35390a *s35390a) { char buf[1]; @@ -208,13 +214,16 @@ static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm) { struct s35390a *s35390a = i2c_get_clientdata(client); int i, err; - char buf[7]; + char buf[7], status; dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d mday=%d, " "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + if (s35390a_read_status(s35390a, &status) == 1) + s35390a_init(s35390a); + buf[S35390A_BYTE_YEAR] = bin2bcd(tm->tm_year - 100); buf[S35390A_BYTE_MONTH] = bin2bcd(tm->tm_mon + 1); buf[S35390A_BYTE_DAY] = bin2bcd(tm->tm_mday); @@ -235,9 +244,12 @@ static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm) static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm) { struct s35390a *s35390a = i2c_get_clientdata(client); - char buf[7]; + char buf[7], status; int i, err; + if (s35390a_read_status(s35390a, &status) == 1) + return -EINVAL; + err = s35390a_get_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf)); if (err < 0) return err; @@ -392,12 +404,42 @@ static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm) return s35390a_set_datetime(to_i2c_client(dev), tm); } +static int s35390a_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s35390a *s35390a = i2c_get_clientdata(client); + char sts; + int err; + + switch (cmd) { + case RTC_VL_READ: + /* s35390a_reset set lowvoltage flag and init RTC if needed */ + err = s35390a_read_status(s35390a, &sts); + if (err < 0) + return err; + if (copy_to_user((void __user *)arg, &err, sizeof(int))) + return -EFAULT; + break; + case RTC_VL_CLR: + /* update flag and clear register */ + err = s35390a_init(s35390a); + if (err < 0) + return err; + break; + default: + return -ENOIOCTLCMD; + } + + return 0; +} + static const struct rtc_class_ops s35390a_rtc_ops = { .read_time = s35390a_rtc_read_time, .set_time = s35390a_rtc_set_time, .set_alarm = s35390a_rtc_set_alarm, .read_alarm = s35390a_rtc_read_alarm, - + .ioctl = s35390a_rtc_ioctl, }; static struct i2c_driver s35390a_driver; @@ -405,7 +447,7 @@ static struct i2c_driver s35390a_driver; static int s35390a_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int err, err_reset; + int err, err_read; unsigned int i; struct s35390a *s35390a; struct rtc_time tm; @@ -438,9 +480,9 @@ static int s35390a_probe(struct i2c_client *client, } } - err_reset = s35390a_reset(s35390a, &status1); - if (err_reset < 0) { - err = err_reset; + err_read = s35390a_read_status(s35390a, &status1); + if (err_read < 0) { + err = err_read; dev_err(&client->dev, "error resetting chip\n"); goto exit_dummy; } @@ -466,7 +508,7 @@ static int s35390a_probe(struct i2c_client *client, } } - if (err_reset > 0 || s35390a_get_datetime(client, &tm) < 0) + if (err_read > 0 || s35390a_get_datetime(client, &tm) < 0) dev_warn(&client->dev, "clock needs to be set\n"); device_set_wakeup_capable(&client->dev, 1); diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index c2187bf6c7e4..ed71d1113627 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -95,46 +95,6 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int sa1100_rtc_open(struct device *dev) -{ - struct sa1100_rtc *info = dev_get_drvdata(dev); - struct rtc_device *rtc = info->rtc; - int ret; - - ret = request_irq(info->irq_1hz, sa1100_rtc_interrupt, 0, "rtc 1Hz", dev); - if (ret) { - dev_err(dev, "IRQ %d already in use.\n", info->irq_1hz); - goto fail_ui; - } - ret = request_irq(info->irq_alarm, sa1100_rtc_interrupt, 0, "rtc Alrm", dev); - if (ret) { - dev_err(dev, "IRQ %d already in use.\n", info->irq_alarm); - goto fail_ai; - } - rtc->max_user_freq = RTC_FREQ; - rtc_irq_set_freq(rtc, NULL, RTC_FREQ); - - return 0; - - fail_ai: - free_irq(info->irq_1hz, dev); - fail_ui: - clk_disable_unprepare(info->clk); - return ret; -} - -static void sa1100_rtc_release(struct device *dev) -{ - struct sa1100_rtc *info = dev_get_drvdata(dev); - - spin_lock_irq(&info->lock); - writel_relaxed(0, info->rtsr); - spin_unlock_irq(&info->lock); - - free_irq(info->irq_alarm, dev); - free_irq(info->irq_1hz, dev); -} - static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { u32 rtsr; @@ -216,8 +176,6 @@ static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) } static const struct rtc_class_ops sa1100_rtc_ops = { - .open = sa1100_rtc_open, - .release = sa1100_rtc_release, .read_time = sa1100_rtc_read_time, .set_time = sa1100_rtc_set_time, .read_alarm = sa1100_rtc_read_alarm, @@ -265,6 +223,9 @@ int sa1100_rtc_init(struct platform_device *pdev, struct sa1100_rtc *info) } info->rtc = rtc; + rtc->max_user_freq = RTC_FREQ; + rtc_irq_set_freq(rtc, NULL, RTC_FREQ); + /* Fix for a nasty initialization problem the in SA11xx RTSR register. * See also the comments in sa1100_rtc_interrupt(). * @@ -299,6 +260,7 @@ static int sa1100_rtc_probe(struct platform_device *pdev) struct resource *iores; void __iomem *base; int irq_1hz, irq_alarm; + int ret; irq_1hz = platform_get_irq_byname(pdev, "rtc 1Hz"); irq_alarm = platform_get_irq_byname(pdev, "rtc alarm"); @@ -311,6 +273,19 @@ static int sa1100_rtc_probe(struct platform_device *pdev) info->irq_1hz = irq_1hz; info->irq_alarm = irq_alarm; + ret = devm_request_irq(&pdev->dev, irq_1hz, sa1100_rtc_interrupt, 0, + "rtc 1Hz", &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already in use.\n", irq_1hz); + return ret; + } + ret = devm_request_irq(&pdev->dev, irq_alarm, sa1100_rtc_interrupt, 0, + "rtc Alrm", &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already in use.\n", irq_alarm); + return ret; + } + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, iores); if (IS_ERR(base)) @@ -339,8 +314,12 @@ static int sa1100_rtc_remove(struct platform_device *pdev) { struct sa1100_rtc *info = platform_get_drvdata(pdev); - if (info) + if (info) { + spin_lock_irq(&info->lock); + writel_relaxed(0, info->rtsr); + spin_unlock_irq(&info->lock); clk_disable_unprepare(info->clk); + } return 0; } diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 39cbc1238b92..3d2216ccd860 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -73,6 +73,9 @@ #define SUN6I_ALARM_CONFIG 0x0050 #define SUN6I_ALARM_CONFIG_WAKEUP BIT(0) +#define SUN6I_LOSC_OUT_GATING 0x0060 +#define SUN6I_LOSC_OUT_GATING_EN BIT(0) + /* * Get date values */ @@ -125,6 +128,7 @@ struct sun6i_rtc_dev { struct clk_hw hw; struct clk_hw *int_osc; struct clk *losc; + struct clk *ext_losc; spinlock_t lock; }; @@ -188,23 +192,24 @@ static void __init sun6i_rtc_clk_init(struct device_node *node) struct clk_init_data init = { .ops = &sun6i_rtc_osc_ops, }; + const char *clkout_name = "osc32k-out"; const char *parents[2]; rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); if (!rtc) return; - spin_lock_init(&rtc->lock); - clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws), + clk_data = kzalloc(sizeof(*clk_data) + (sizeof(*clk_data->hws) * 2), GFP_KERNEL); if (!clk_data) return; + spin_lock_init(&rtc->lock); rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(rtc->base)) { pr_crit("Can't map RTC registers"); - return; + goto err; } /* Switch to the external, more precise, oscillator */ @@ -216,7 +221,7 @@ static void __init sun6i_rtc_clk_init(struct device_node *node) /* Deal with old DTs */ if (!of_get_property(node, "clocks", NULL)) - return; + goto err; rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL, "rtc-int-osc", @@ -235,7 +240,8 @@ static void __init sun6i_rtc_clk_init(struct device_node *node) init.parent_names = parents; init.num_parents = of_clk_get_parent_count(node) + 1; - of_property_read_string(node, "clock-output-names", &init.name); + of_property_read_string_index(node, "clock-output-names", 0, + &init.name); rtc->losc = clk_register(NULL, &rtc->hw); if (IS_ERR(rtc->losc)) { @@ -243,9 +249,25 @@ static void __init sun6i_rtc_clk_init(struct device_node *node) return; } - clk_data->num = 1; + of_property_read_string_index(node, "clock-output-names", 1, + &clkout_name); + rtc->ext_losc = clk_register_gate(NULL, clkout_name, rtc->hw.init->name, + 0, rtc->base + SUN6I_LOSC_OUT_GATING, + SUN6I_LOSC_OUT_GATING_EN, 0, + &rtc->lock); + if (IS_ERR(rtc->ext_losc)) { + pr_crit("Couldn't register the LOSC external gate\n"); + return; + } + + clk_data->num = 2; clk_data->hws[0] = &rtc->hw; + clk_data->hws[1] = __clk_get_hw(rtc->ext_losc); of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); + return; + +err: + kfree(clk_data); } CLK_OF_DECLARE_DRIVER(sun6i_rtc_clk, "allwinner,sun6i-a31-rtc", sun6i_rtc_clk_init); diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index e1b86bb01062..7ce22967fd16 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -119,23 +119,6 @@ static inline void write_elapsed_second(unsigned long sec) spin_unlock_irq(&rtc_lock); } -static void vr41xx_rtc_release(struct device *dev) -{ - - spin_lock_irq(&rtc_lock); - - rtc1_write(ECMPLREG, 0); - rtc1_write(ECMPMREG, 0); - rtc1_write(ECMPHREG, 0); - rtc1_write(RTCL1LREG, 0); - rtc1_write(RTCL1HREG, 0); - - spin_unlock_irq(&rtc_lock); - - disable_irq(aie_irq); - disable_irq(pie_irq); -} - static int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time) { unsigned long epoch_sec, elapsed_sec; @@ -272,7 +255,6 @@ static irqreturn_t rtclong1_interrupt(int irq, void *dev_id) } static const struct rtc_class_ops vr41xx_rtc_ops = { - .release = vr41xx_rtc_release, .ioctl = vr41xx_rtc_ioctl, .read_time = vr41xx_rtc_read_time, .set_time = vr41xx_rtc_set_time, diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 9c97ad1ee121..ea19b4ff87a2 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -62,7 +62,6 @@ MODULE_LICENSE("GPL"); static int dasd_alloc_queue(struct dasd_block *); static void dasd_setup_queue(struct dasd_block *); static void dasd_free_queue(struct dasd_block *); -static void dasd_flush_request_queue(struct dasd_block *); static int dasd_flush_block_queue(struct dasd_block *); static void dasd_device_tasklet(struct dasd_device *); static void dasd_block_tasklet(struct dasd_block *); @@ -158,7 +157,6 @@ struct dasd_block *dasd_alloc_block(void) /* open_count = 0 means device online but not in use */ atomic_set(&block->open_count, -1); - spin_lock_init(&block->request_queue_lock); atomic_set(&block->tasklet_scheduled, 0); tasklet_init(&block->tasklet, (void (*)(unsigned long)) dasd_block_tasklet, @@ -391,7 +389,6 @@ static int dasd_state_ready_to_basic(struct dasd_device *device) device->state = DASD_STATE_READY; return rc; } - dasd_flush_request_queue(block); dasd_destroy_partitions(block); block->blocks = 0; block->bp_block = 0; @@ -1645,8 +1642,10 @@ void dasd_generic_handle_state_change(struct dasd_device *device) dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING); dasd_schedule_device_bh(device); - if (device->block) + if (device->block) { dasd_schedule_block_bh(device->block); + blk_mq_run_hw_queues(device->block->request_queue, true); + } } EXPORT_SYMBOL_GPL(dasd_generic_handle_state_change); @@ -2638,6 +2637,7 @@ static void dasd_block_timeout(unsigned long ptr) dasd_device_remove_stop_bits(block->base, DASD_STOPPED_PENDING); spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags); dasd_schedule_block_bh(block); + blk_mq_run_hw_queues(block->request_queue, true); } /* @@ -2677,115 +2677,11 @@ static void __dasd_process_erp(struct dasd_device *device, erp_fn(cqr); } -/* - * Fetch requests from the block device queue. - */ -static void __dasd_process_request_queue(struct dasd_block *block) -{ - struct request_queue *queue; - struct request *req; - struct dasd_ccw_req *cqr; - struct dasd_device *basedev; - unsigned long flags; - queue = block->request_queue; - basedev = block->base; - /* No queue ? Then there is nothing to do. */ - if (queue == NULL) - return; - - /* - * We requeue request from the block device queue to the ccw - * queue only in two states. In state DASD_STATE_READY the - * partition detection is done and we need to requeue requests - * for that. State DASD_STATE_ONLINE is normal block device - * operation. - */ - if (basedev->state < DASD_STATE_READY) { - while ((req = blk_fetch_request(block->request_queue))) - __blk_end_request_all(req, BLK_STS_IOERR); - return; - } - - /* - * if device is stopped do not fetch new requests - * except failfast is active which will let requests fail - * immediately in __dasd_block_start_head() - */ - if (basedev->stopped && !(basedev->features & DASD_FEATURE_FAILFAST)) - return; - - /* Now we try to fetch requests from the request queue */ - while ((req = blk_peek_request(queue))) { - if (basedev->features & DASD_FEATURE_READONLY && - rq_data_dir(req) == WRITE) { - DBF_DEV_EVENT(DBF_ERR, basedev, - "Rejecting write request %p", - req); - blk_start_request(req); - __blk_end_request_all(req, BLK_STS_IOERR); - continue; - } - if (test_bit(DASD_FLAG_ABORTALL, &basedev->flags) && - (basedev->features & DASD_FEATURE_FAILFAST || - blk_noretry_request(req))) { - DBF_DEV_EVENT(DBF_ERR, basedev, - "Rejecting failfast request %p", - req); - blk_start_request(req); - __blk_end_request_all(req, BLK_STS_TIMEOUT); - continue; - } - cqr = basedev->discipline->build_cp(basedev, block, req); - if (IS_ERR(cqr)) { - if (PTR_ERR(cqr) == -EBUSY) - break; /* normal end condition */ - if (PTR_ERR(cqr) == -ENOMEM) - break; /* terminate request queue loop */ - if (PTR_ERR(cqr) == -EAGAIN) { - /* - * The current request cannot be build right - * now, we have to try later. If this request - * is the head-of-queue we stop the device - * for 1/2 second. - */ - if (!list_empty(&block->ccw_queue)) - break; - spin_lock_irqsave( - get_ccwdev_lock(basedev->cdev), flags); - dasd_device_set_stop_bits(basedev, - DASD_STOPPED_PENDING); - spin_unlock_irqrestore( - get_ccwdev_lock(basedev->cdev), flags); - dasd_block_set_timer(block, HZ/2); - break; - } - DBF_DEV_EVENT(DBF_ERR, basedev, - "CCW creation failed (rc=%ld) " - "on request %p", - PTR_ERR(cqr), req); - blk_start_request(req); - __blk_end_request_all(req, BLK_STS_IOERR); - continue; - } - /* - * Note: callback is set to dasd_return_cqr_cb in - * __dasd_block_start_head to cover erp requests as well - */ - cqr->callback_data = (void *) req; - cqr->status = DASD_CQR_FILLED; - req->completion_data = cqr; - blk_start_request(req); - list_add_tail(&cqr->blocklist, &block->ccw_queue); - INIT_LIST_HEAD(&cqr->devlist); - dasd_profile_start(block, cqr, req); - } -} - static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr) { struct request *req; - int status; blk_status_t error = BLK_STS_OK; + int status; req = (struct request *) cqr->callback_data; dasd_profile_end(cqr->block, cqr, req); @@ -2809,7 +2705,19 @@ static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr) break; } } - __blk_end_request_all(req, error); + + /* + * We need to take care for ETIMEDOUT errors here since the + * complete callback does not get called in this case. + * Take care of all errors here and avoid additional code to + * transfer the error value to the complete callback. + */ + if (error) { + blk_mq_end_request(req, error); + blk_mq_run_hw_queues(req->q, true); + } else { + blk_mq_complete_request(req); + } } /* @@ -2938,27 +2846,30 @@ static void dasd_block_tasklet(struct dasd_block *block) struct list_head final_queue; struct list_head *l, *n; struct dasd_ccw_req *cqr; + struct dasd_queue *dq; atomic_set(&block->tasklet_scheduled, 0); INIT_LIST_HEAD(&final_queue); - spin_lock(&block->queue_lock); + spin_lock_irq(&block->queue_lock); /* Finish off requests on ccw queue */ __dasd_process_block_ccw_queue(block, &final_queue); - spin_unlock(&block->queue_lock); + spin_unlock_irq(&block->queue_lock); + /* Now call the callback function of requests with final status */ - spin_lock_irq(&block->request_queue_lock); list_for_each_safe(l, n, &final_queue) { cqr = list_entry(l, struct dasd_ccw_req, blocklist); + dq = cqr->dq; + spin_lock_irq(&dq->lock); list_del_init(&cqr->blocklist); __dasd_cleanup_cqr(cqr); + spin_unlock_irq(&dq->lock); } - spin_lock(&block->queue_lock); - /* Get new request from the block device request queue */ - __dasd_process_request_queue(block); + + spin_lock_irq(&block->queue_lock); /* Now check if the head of the ccw queue needs to be started. */ __dasd_block_start_head(block); - spin_unlock(&block->queue_lock); - spin_unlock_irq(&block->request_queue_lock); + spin_unlock_irq(&block->queue_lock); + if (waitqueue_active(&shutdown_waitq)) wake_up(&shutdown_waitq); dasd_put_device(block->base); @@ -2977,14 +2888,13 @@ static int _dasd_requeue_request(struct dasd_ccw_req *cqr) { struct dasd_block *block = cqr->block; struct request *req; - unsigned long flags; if (!block) return -EINVAL; - spin_lock_irqsave(&block->request_queue_lock, flags); + spin_lock_irq(&cqr->dq->lock); req = (struct request *) cqr->callback_data; - blk_requeue_request(block->request_queue, req); - spin_unlock_irqrestore(&block->request_queue_lock, flags); + blk_mq_requeue_request(req, false); + spin_unlock_irq(&cqr->dq->lock); return 0; } @@ -2999,6 +2909,7 @@ static int dasd_flush_block_queue(struct dasd_block *block) struct dasd_ccw_req *cqr, *n; int rc, i; struct list_head flush_queue; + unsigned long flags; INIT_LIST_HEAD(&flush_queue); spin_lock_bh(&block->queue_lock); @@ -3037,11 +2948,11 @@ restart_cb: goto restart_cb; } /* call the callback function */ - spin_lock_irq(&block->request_queue_lock); + spin_lock_irqsave(&cqr->dq->lock, flags); cqr->endclk = get_tod_clock(); list_del_init(&cqr->blocklist); __dasd_cleanup_cqr(cqr); - spin_unlock_irq(&block->request_queue_lock); + spin_unlock_irqrestore(&cqr->dq->lock, flags); } return rc; } @@ -3069,42 +2980,114 @@ EXPORT_SYMBOL(dasd_schedule_block_bh); /* * Dasd request queue function. Called from ll_rw_blk.c */ -static void do_dasd_request(struct request_queue *queue) +static blk_status_t do_dasd_request(struct blk_mq_hw_ctx *hctx, + const struct blk_mq_queue_data *qd) { - struct dasd_block *block; + struct dasd_block *block = hctx->queue->queuedata; + struct dasd_queue *dq = hctx->driver_data; + struct request *req = qd->rq; + struct dasd_device *basedev; + struct dasd_ccw_req *cqr; + blk_status_t rc = BLK_STS_OK; + + basedev = block->base; + spin_lock_irq(&dq->lock); + if (basedev->state < DASD_STATE_READY) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "device not ready for request %p", req); + rc = BLK_STS_IOERR; + goto out; + } + + /* + * if device is stopped do not fetch new requests + * except failfast is active which will let requests fail + * immediately in __dasd_block_start_head() + */ + if (basedev->stopped && !(basedev->features & DASD_FEATURE_FAILFAST)) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "device stopped request %p", req); + rc = BLK_STS_RESOURCE; + goto out; + } + + if (basedev->features & DASD_FEATURE_READONLY && + rq_data_dir(req) == WRITE) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "Rejecting write request %p", req); + rc = BLK_STS_IOERR; + goto out; + } - block = queue->queuedata; + if (test_bit(DASD_FLAG_ABORTALL, &basedev->flags) && + (basedev->features & DASD_FEATURE_FAILFAST || + blk_noretry_request(req))) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "Rejecting failfast request %p", req); + rc = BLK_STS_IOERR; + goto out; + } + + cqr = basedev->discipline->build_cp(basedev, block, req); + if (IS_ERR(cqr)) { + if (PTR_ERR(cqr) == -EBUSY || + PTR_ERR(cqr) == -ENOMEM || + PTR_ERR(cqr) == -EAGAIN) { + rc = BLK_STS_RESOURCE; + goto out; + } + DBF_DEV_EVENT(DBF_ERR, basedev, + "CCW creation failed (rc=%ld) on request %p", + PTR_ERR(cqr), req); + rc = BLK_STS_IOERR; + goto out; + } + /* + * Note: callback is set to dasd_return_cqr_cb in + * __dasd_block_start_head to cover erp requests as well + */ + cqr->callback_data = req; + cqr->status = DASD_CQR_FILLED; + cqr->dq = dq; + req->completion_data = cqr; + blk_mq_start_request(req); spin_lock(&block->queue_lock); - /* Get new request from the block device request queue */ - __dasd_process_request_queue(block); - /* Now check if the head of the ccw queue needs to be started. */ - __dasd_block_start_head(block); + list_add_tail(&cqr->blocklist, &block->ccw_queue); + INIT_LIST_HEAD(&cqr->devlist); + dasd_profile_start(block, cqr, req); + dasd_schedule_block_bh(block); spin_unlock(&block->queue_lock); + +out: + spin_unlock_irq(&dq->lock); + return rc; } /* * Block timeout callback, called from the block layer * - * request_queue lock is held on entry. - * * Return values: * BLK_EH_RESET_TIMER if the request should be left running * BLK_EH_NOT_HANDLED if the request is handled or terminated * by the driver. */ -enum blk_eh_timer_return dasd_times_out(struct request *req) +enum blk_eh_timer_return dasd_times_out(struct request *req, bool reserved) { struct dasd_ccw_req *cqr = req->completion_data; struct dasd_block *block = req->q->queuedata; struct dasd_device *device; + unsigned long flags; int rc = 0; if (!cqr) return BLK_EH_NOT_HANDLED; + spin_lock_irqsave(&cqr->dq->lock, flags); device = cqr->startdev ? cqr->startdev : block->base; - if (!device->blk_timeout) + if (!device->blk_timeout) { + spin_unlock_irqrestore(&cqr->dq->lock, flags); return BLK_EH_RESET_TIMER; + } DBF_DEV_EVENT(DBF_WARNING, device, " dasd_times_out cqr %p status %x", cqr, cqr->status); @@ -3154,19 +3137,64 @@ enum blk_eh_timer_return dasd_times_out(struct request *req) } dasd_schedule_block_bh(block); spin_unlock(&block->queue_lock); + spin_unlock_irqrestore(&cqr->dq->lock, flags); return rc ? BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED; } +static int dasd_init_hctx(struct blk_mq_hw_ctx *hctx, void *data, + unsigned int idx) +{ + struct dasd_queue *dq = kzalloc(sizeof(*dq), GFP_KERNEL); + + if (!dq) + return -ENOMEM; + + spin_lock_init(&dq->lock); + hctx->driver_data = dq; + + return 0; +} + +static void dasd_exit_hctx(struct blk_mq_hw_ctx *hctx, unsigned int idx) +{ + kfree(hctx->driver_data); + hctx->driver_data = NULL; +} + +static void dasd_request_done(struct request *req) +{ + blk_mq_end_request(req, 0); + blk_mq_run_hw_queues(req->q, true); +} + +static struct blk_mq_ops dasd_mq_ops = { + .queue_rq = do_dasd_request, + .complete = dasd_request_done, + .timeout = dasd_times_out, + .init_hctx = dasd_init_hctx, + .exit_hctx = dasd_exit_hctx, +}; + /* * Allocate and initialize request queue and default I/O scheduler. */ static int dasd_alloc_queue(struct dasd_block *block) { - block->request_queue = blk_init_queue(do_dasd_request, - &block->request_queue_lock); - if (block->request_queue == NULL) - return -ENOMEM; + int rc; + + block->tag_set.ops = &dasd_mq_ops; + block->tag_set.nr_hw_queues = DASD_NR_HW_QUEUES; + block->tag_set.queue_depth = DASD_MAX_LCU_DEV * DASD_REQ_PER_DEV; + block->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; + + rc = blk_mq_alloc_tag_set(&block->tag_set); + if (rc) + return rc; + + block->request_queue = blk_mq_init_queue(&block->tag_set); + if (IS_ERR(block->request_queue)) + return PTR_ERR(block->request_queue); block->request_queue->queuedata = block; @@ -3229,26 +3257,11 @@ static void dasd_free_queue(struct dasd_block *block) { if (block->request_queue) { blk_cleanup_queue(block->request_queue); + blk_mq_free_tag_set(&block->tag_set); block->request_queue = NULL; } } -/* - * Flush request on the request queue. - */ -static void dasd_flush_request_queue(struct dasd_block *block) -{ - struct request *req; - - if (!block->request_queue) - return; - - spin_lock_irq(&block->request_queue_lock); - while ((req = blk_fetch_request(block->request_queue))) - __blk_end_request_all(req, BLK_STS_IOERR); - spin_unlock_irq(&block->request_queue_lock); -} - static int dasd_open(struct block_device *bdev, fmode_t mode) { struct dasd_device *base; @@ -3744,8 +3757,10 @@ int dasd_generic_path_operational(struct dasd_device *device) return 1; } dasd_schedule_device_bh(device); - if (device->block) + if (device->block) { dasd_schedule_block_bh(device->block); + blk_mq_run_hw_queues(device->block->request_queue, true); + } if (!device->stopped) wake_up(&generic_waitq); @@ -4008,8 +4023,10 @@ int dasd_generic_restore_device(struct ccw_device *cdev) */ device->stopped |= DASD_UNRESUMED_PM; - if (device->block) + if (device->block) { dasd_schedule_block_bh(device->block); + blk_mq_run_hw_queues(device->block->request_queue, true); + } clear_bit(DASD_FLAG_SUSPENDED, &device->flags); dasd_put_device(device); diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index e38042ce94e6..c95a4784c191 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1326,7 +1326,7 @@ dasd_timeout_store(struct device *dev, struct device_attribute *attr, { struct dasd_device *device; struct request_queue *q; - unsigned long val, flags; + unsigned long val; device = dasd_device_from_cdev(to_ccwdev(dev)); if (IS_ERR(device) || !device->block) @@ -1342,16 +1342,10 @@ dasd_timeout_store(struct device *dev, struct device_attribute *attr, dasd_put_device(device); return -ENODEV; } - spin_lock_irqsave(&device->block->request_queue_lock, flags); - if (!val) - blk_queue_rq_timed_out(q, NULL); - else - blk_queue_rq_timed_out(q, dasd_times_out); device->blk_timeout = val; blk_queue_rq_timeout(q, device->blk_timeout * HZ); - spin_unlock_irqrestore(&device->block->request_queue_lock, flags); dasd_put_device(device); return count; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index f9e25fc03d6b..db470bd10175 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -56,6 +56,7 @@ #include <asm/dasd.h> #include <asm/idals.h> #include <linux/bitops.h> +#include <linux/blk-mq.h> /* DASD discipline magic */ #define DASD_ECKD_MAGIC 0xC5C3D2C4 @@ -185,6 +186,7 @@ struct dasd_ccw_req { char status; /* status of this request */ short retries; /* A retry counter */ unsigned long flags; /* flags of this request */ + struct dasd_queue *dq; /* ... and how */ unsigned long starttime; /* jiffies time of request start */ @@ -248,6 +250,16 @@ struct dasd_ccw_req { #define DASD_CQR_SUPPRESS_IL 6 /* Suppress 'Incorrect Length' error */ #define DASD_CQR_SUPPRESS_CR 7 /* Suppress 'Command Reject' error */ +/* + * There is no reliable way to determine the number of available CPUs on + * LPAR but there is no big performance difference between 1 and the + * maximum CPU number. + * 64 is a good trade off performance wise. + */ +#define DASD_NR_HW_QUEUES 64 +#define DASD_MAX_LCU_DEV 256 +#define DASD_REQ_PER_DEV 4 + /* Signature for error recovery functions. */ typedef struct dasd_ccw_req *(*dasd_erp_fn_t) (struct dasd_ccw_req *); @@ -539,6 +551,7 @@ struct dasd_block { struct gendisk *gdp; struct request_queue *request_queue; spinlock_t request_queue_lock; + struct blk_mq_tag_set tag_set; struct block_device *bdev; atomic_t open_count; @@ -563,6 +576,10 @@ struct dasd_attention_data { __u8 lpum; }; +struct dasd_queue { + spinlock_t lock; +}; + /* reasons why device (ccw_device_start) was stopped */ #define DASD_STOPPED_NOT_ACC 1 /* not accessible */ #define DASD_STOPPED_QUIESCE 2 /* Quiesced */ @@ -731,7 +748,7 @@ void dasd_free_device(struct dasd_device *); struct dasd_block *dasd_alloc_block(void); void dasd_free_block(struct dasd_block *); -enum blk_eh_timer_return dasd_times_out(struct request *req); +enum blk_eh_timer_return dasd_times_out(struct request *req, bool reserved); void dasd_enable_device(struct dasd_device *); void dasd_set_target_state(struct dasd_device *, int); diff --git a/drivers/s390/crypto/ap_asm.h b/drivers/s390/crypto/ap_asm.h index 287b4ad0999e..cd350345b3d2 100644 --- a/drivers/s390/crypto/ap_asm.h +++ b/drivers/s390/crypto/ap_asm.h @@ -69,16 +69,19 @@ static inline struct ap_queue_status ap_rapq(ap_qid_t qid) } /** - * ap_aqic(): Enable interruption for a specific AP. + * ap_aqic(): Control interruption for a specific AP. * @qid: The AP queue number + * @qirqctrl: struct ap_qirq_ctrl (64 bit value) * @ind: The notification indicator byte * * Returns AP queue status. */ -static inline struct ap_queue_status ap_aqic(ap_qid_t qid, void *ind) +static inline struct ap_queue_status ap_aqic(ap_qid_t qid, + struct ap_qirq_ctrl qirqctrl, + void *ind) { register unsigned long reg0 asm ("0") = qid | (3UL << 24); - register unsigned long reg1_in asm ("1") = (8UL << 44) | AP_ISC; + register struct ap_qirq_ctrl reg1_in asm ("1") = qirqctrl; register struct ap_queue_status reg1_out asm ("1"); register void *reg2 asm ("2") = ind; diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 6dee598979e7..5f0be2040272 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -166,26 +166,51 @@ static int ap_configuration_available(void) } /** + * ap_apft_available(): Test if AP facilities test (APFT) + * facility is available. + * + * Returns 1 if APFT is is available. + */ +static int ap_apft_available(void) +{ + return test_facility(15); +} + +/** * ap_test_queue(): Test adjunct processor queue. * @qid: The AP queue number + * @tbit: Test facilities bit * @info: Pointer to queue descriptor * * Returns AP queue status structure. */ -static inline struct ap_queue_status -ap_test_queue(ap_qid_t qid, unsigned long *info) +struct ap_queue_status ap_test_queue(ap_qid_t qid, + int tbit, + unsigned long *info) { - if (test_facility(15)) - qid |= 1UL << 23; /* set APFT T bit*/ + if (tbit) + qid |= 1UL << 23; /* set T bit*/ return ap_tapq(qid, info); } +EXPORT_SYMBOL(ap_test_queue); -static inline int ap_query_configuration(void) +/* + * ap_query_configuration(): Fetch cryptographic config info + * + * Returns the ap configuration info fetched via PQAP(QCI). + * On success 0 is returned, on failure a negative errno + * is returned, e.g. if the PQAP(QCI) instruction is not + * available, the return value will be -EOPNOTSUPP. + */ +int ap_query_configuration(struct ap_config_info *info) { - if (!ap_configuration) + if (!ap_configuration_available()) return -EOPNOTSUPP; - return ap_qci(ap_configuration); + if (!info) + return -EINVAL; + return ap_qci(info); } +EXPORT_SYMBOL(ap_query_configuration); /** * ap_init_configuration(): Allocate and query configuration array. @@ -198,7 +223,7 @@ static void ap_init_configuration(void) ap_configuration = kzalloc(sizeof(*ap_configuration), GFP_KERNEL); if (!ap_configuration) return; - if (ap_query_configuration() != 0) { + if (ap_query_configuration(ap_configuration) != 0) { kfree(ap_configuration); ap_configuration = NULL; return; @@ -261,7 +286,7 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type, if (!ap_test_config_card_id(AP_QID_CARD(qid))) return -ENODEV; - status = ap_test_queue(qid, &info); + status = ap_test_queue(qid, ap_apft_available(), &info); switch (status.response_code) { case AP_RESPONSE_NORMAL: *queue_depth = (int)(info & 0xff); @@ -940,7 +965,9 @@ static int ap_select_domain(void) for (j = 0; j < AP_DEVICES; j++) { if (!ap_test_config_card_id(j)) continue; - status = ap_test_queue(AP_MKQID(j, i), NULL); + status = ap_test_queue(AP_MKQID(j, i), + ap_apft_available(), + NULL); if (status.response_code != AP_RESPONSE_NORMAL) continue; count++; @@ -993,7 +1020,7 @@ static void ap_scan_bus(struct work_struct *unused) AP_DBF(DBF_DEBUG, "ap_scan_bus running\n"); - ap_query_configuration(); + ap_query_configuration(ap_configuration); if (ap_select_domain() != 0) goto out; diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 4dc7c88fb054..754cf2223cfb 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -28,6 +28,7 @@ #include <linux/device.h> #include <linux/types.h> +#include <asm/ap.h> #define AP_DEVICES 64 /* Number of AP devices. */ #define AP_DOMAINS 256 /* Number of AP domains. */ @@ -40,41 +41,6 @@ extern int ap_domain_index; extern spinlock_t ap_list_lock; extern struct list_head ap_card_list; -/** - * The ap_qid_t identifier of an ap queue. It contains a - * 6 bit card index and a 4 bit queue index (domain). - */ -typedef unsigned int ap_qid_t; - -#define AP_MKQID(_card, _queue) (((_card) & 63) << 8 | ((_queue) & 255)) -#define AP_QID_CARD(_qid) (((_qid) >> 8) & 63) -#define AP_QID_QUEUE(_qid) ((_qid) & 255) - -/** - * structy ap_queue_status - Holds the AP queue status. - * @queue_empty: Shows if queue is empty - * @replies_waiting: Waiting replies - * @queue_full: Is 1 if the queue is full - * @pad: A 4 bit pad - * @int_enabled: Shows if interrupts are enabled for the AP - * @response_code: Holds the 8 bit response code - * @pad2: A 16 bit pad - * - * The ap queue status word is returned by all three AP functions - * (PQAP, NQAP and DQAP). There's a set of flags in the first - * byte, followed by a 1 byte response code. - */ -struct ap_queue_status { - unsigned int queue_empty : 1; - unsigned int replies_waiting : 1; - unsigned int queue_full : 1; - unsigned int pad1 : 4; - unsigned int int_enabled : 1; - unsigned int response_code : 8; - unsigned int pad2 : 16; -} __packed; - - static inline int ap_test_bit(unsigned int *ptr, unsigned int nr) { return (*ptr & (0x80000000u >> nr)) != 0; @@ -238,17 +204,6 @@ struct ap_message { struct ap_message *); }; -struct ap_config_info { - unsigned int special_command:1; - unsigned int ap_extended:1; - unsigned char reserved1:6; - unsigned char reserved2[15]; - unsigned int apm[8]; /* AP ID mask */ - unsigned int aqm[8]; /* AP queue mask */ - unsigned int adm[8]; /* AP domain mask */ - unsigned char reserved4[16]; -} __packed; - /** * ap_init_message() - Initialize ap_message. * Initialize a message before using. Otherwise this might result in diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index 0f1a5d02acb0..56b96edffd5b 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -16,6 +16,25 @@ #include "ap_asm.h" /** + * ap_queue_irq_ctrl(): Control interruption on a AP queue. + * @qirqctrl: struct ap_qirq_ctrl (64 bit value) + * @ind: The notification indicator byte + * + * Returns AP queue status. + * + * Control interruption on the given AP queue. + * Just a simple wrapper function for the low level PQAP(AQIC) + * instruction available for other kernel modules. + */ +struct ap_queue_status ap_queue_irq_ctrl(ap_qid_t qid, + struct ap_qirq_ctrl qirqctrl, + void *ind) +{ + return ap_aqic(qid, qirqctrl, ind); +} +EXPORT_SYMBOL(ap_queue_irq_ctrl); + +/** * ap_queue_enable_interruption(): Enable interruption on an AP queue. * @qid: The AP queue number * @ind: the notification indicator byte @@ -27,8 +46,11 @@ static int ap_queue_enable_interruption(struct ap_queue *aq, void *ind) { struct ap_queue_status status; + struct ap_qirq_ctrl qirqctrl = { 0 }; - status = ap_aqic(aq->qid, ind); + qirqctrl.ir = 1; + qirqctrl.isc = AP_ISC; + status = ap_aqic(aq->qid, qirqctrl, ind); switch (status.response_code) { case AP_RESPONSE_NORMAL: case AP_RESPONSE_OTHERWISE_CHANGED: @@ -362,7 +384,7 @@ static enum ap_wait ap_sm_setirq_wait(struct ap_queue *aq) /* Get the status with TAPQ */ status = ap_tapq(aq->qid, NULL); - if (status.int_enabled == 1) { + if (status.irq_enabled == 1) { /* Irqs are now enabled */ aq->interrupt = AP_INTR_ENABLED; aq->state = (aq->queue_count > 0) ? diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index d145e0d90227..41366339b950 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -283,7 +283,7 @@ config SCSI_ISCSI_ATTRS config SCSI_SAS_ATTRS tristate "SAS Transport Attributes" depends on SCSI - select BLK_DEV_BSG + select BLK_DEV_BSGLIB help If you wish to export transport-specific information about each attached SAS device to sysfs, say Y. diff --git a/drivers/scsi/NCR_Q720.c b/drivers/scsi/NCR_Q720.c index 05835bf1bf9c..54e7d26908ee 100644 --- a/drivers/scsi/NCR_Q720.c +++ b/drivers/scsi/NCR_Q720.c @@ -217,8 +217,7 @@ NCR_Q720_probe(struct device *dev) } if (dma_declare_coherent_memory(dev, base_addr, base_addr, - mem_size, DMA_MEMORY_MAP) - != DMA_MEMORY_MAP) { + mem_size, 0)) { printk(KERN_ERR "NCR_Q720: DMA declare memory failed\n"); goto out_release_region; } diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 831a1c8b9f89..fe3a0da3ec97 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -315,8 +315,6 @@ static void scsi_host_dev_release(struct device *dev) { struct Scsi_Host *shost = dev_to_shost(dev); struct device *parent = dev->parent; - struct request_queue *q; - void *queuedata; scsi_proc_hostdir_rm(shost->hostt); @@ -326,12 +324,6 @@ static void scsi_host_dev_release(struct device *dev) kthread_stop(shost->ehandler); if (shost->work_q) destroy_workqueue(shost->work_q); - q = shost->uspace_req_q; - if (q) { - queuedata = q->queuedata; - blk_cleanup_queue(q); - kfree(queuedata); - } if (shost->shost_state == SHOST_CREATED) { /* diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 77a0335eb757..09ba494f8896 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -5465,7 +5465,7 @@ static int sdebug_driver_probe(struct device * dev) return error; } if (submit_queues > nr_cpu_ids) { - pr_warn("%s: trim submit_queues (was %d) to nr_cpu_ids=%d\n", + pr_warn("%s: trim submit_queues (was %d) to nr_cpu_ids=%u\n", my_name, submit_queues, nr_cpu_ids); submit_queues = nr_cpu_ids; } diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c index 15ff285a9f8f..ca360daa6a25 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.c +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c @@ -4985,13 +4985,10 @@ struct sym_lcb *sym_alloc_lcb (struct sym_hcb *np, u_char tn, u_char ln) * Compute the bus address of this table. */ if (ln && !tp->luntbl) { - int i; - tp->luntbl = sym_calloc_dma(256, "LUNTBL"); if (!tp->luntbl) goto fail; - for (i = 0 ; i < 64 ; i++) - tp->luntbl[i] = cpu_to_scr(vtobus(&np->badlun_sa)); + memset32(tp->luntbl, cpu_to_scr(vtobus(&np->badlun_sa)), 64); tp->head.luntbl_sa = cpu_to_scr(vtobus(tp->luntbl)); } @@ -5077,8 +5074,7 @@ static void sym_alloc_lcb_tags (struct sym_hcb *np, u_char tn, u_char ln) /* * Initialize the task table with invalid entries. */ - for (i = 0 ; i < SYM_CONF_MAX_TASK ; i++) - lp->itlq_tbl[i] = cpu_to_scr(np->notask_ba); + memset32(lp->itlq_tbl, cpu_to_scr(np->notask_ba), SYM_CONF_MAX_TASK); /* * Fill up the tag buffer with tag numbers. @@ -5764,8 +5760,7 @@ int sym_hcb_attach(struct Scsi_Host *shost, struct sym_fw *fw, struct sym_nvram goto attach_failed; np->badlun_sa = cpu_to_scr(SCRIPTB_BA(np, resel_bad_lun)); - for (i = 0 ; i < 64 ; i++) /* 64 luns/target, no less */ - np->badluntbl[i] = cpu_to_scr(vtobus(&np->badlun_sa)); + memset32(np->badluntbl, cpu_to_scr(vtobus(&np->badlun_sa)), 64); /* * Prepare the bus address array that contains the bus diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 07fc0ac51c52..fc9e98047421 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -1,6 +1,7 @@ menu "SOC (System On Chip) specific Drivers" source "drivers/soc/actions/Kconfig" +source "drivers/soc/amlogic/Kconfig" source "drivers/soc/atmel/Kconfig" source "drivers/soc/bcm/Kconfig" source "drivers/soc/fsl/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 9241125416ba..280a6a91a9e2 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_MACH_DOVE) += dove/ obj-y += fsl/ obj-$(CONFIG_ARCH_MXC) += imx/ obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ +obj-$(CONFIG_ARCH_MESON) += amlogic/ obj-$(CONFIG_ARCH_QCOM) += qcom/ obj-y += renesas/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig new file mode 100644 index 000000000000..22acf064531f --- /dev/null +++ b/drivers/soc/amlogic/Kconfig @@ -0,0 +1,12 @@ +menu "Amlogic SoC drivers" + +config MESON_GX_SOCINFO + bool "Amlogic Meson GX SoC Information driver" + depends on ARCH_MESON || COMPILE_TEST + default ARCH_MESON + select SOC_BUS + help + Say yes to support decoding of Amlogic Meson GX SoC family + information about the type, package and version. + +endmenu diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile new file mode 100644 index 000000000000..3e85fc462c21 --- /dev/null +++ b/drivers/soc/amlogic/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c new file mode 100644 index 000000000000..89f4cf507be6 --- /dev/null +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2017 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> +#include <linux/bitfield.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +#define AO_SEC_SD_CFG8 0xe0 +#define AO_SEC_SOCINFO_OFFSET AO_SEC_SD_CFG8 + +#define SOCINFO_MAJOR GENMASK(31, 24) +#define SOCINFO_MINOR GENMASK(23, 16) +#define SOCINFO_PACK GENMASK(15, 8) +#define SOCINFO_MISC GENMASK(7, 0) + +static const struct meson_gx_soc_id { + const char *name; + unsigned int id; +} soc_ids[] = { + { "GXBB", 0x1f }, + { "GXTVBB", 0x20 }, + { "GXL", 0x21 }, + { "GXM", 0x22 }, + { "TXL", 0x23 }, +}; + +static const struct meson_gx_package_id { + const char *name; + unsigned int major_id; + unsigned int pack_id; +} soc_packages[] = { + { "S905", 0x1f, 0 }, + { "S905M", 0x1f, 0x20 }, + { "S905D", 0x21, 0 }, + { "S905X", 0x21, 0x80 }, + { "S905L", 0x21, 0xc0 }, + { "S905M2", 0x21, 0xe0 }, + { "S912", 0x22, 0 }, +}; + +static inline unsigned int socinfo_to_major(u32 socinfo) +{ + return FIELD_GET(SOCINFO_MAJOR, socinfo); +} + +static inline unsigned int socinfo_to_minor(u32 socinfo) +{ + return FIELD_GET(SOCINFO_MINOR, socinfo); +} + +static inline unsigned int socinfo_to_pack(u32 socinfo) +{ + return FIELD_GET(SOCINFO_PACK, socinfo); +} + +static inline unsigned int socinfo_to_misc(u32 socinfo) +{ + return FIELD_GET(SOCINFO_MISC, socinfo); +} + +static const char *socinfo_to_package_id(u32 socinfo) +{ + unsigned int pack = socinfo_to_pack(socinfo) & 0xf0; + unsigned int major = socinfo_to_major(socinfo); + int i; + + for (i = 0 ; i < ARRAY_SIZE(soc_packages) ; ++i) { + if (soc_packages[i].major_id == major && + soc_packages[i].pack_id == pack) + return soc_packages[i].name; + } + + return "Unknown"; +} + +static const char *socinfo_to_soc_id(u32 socinfo) +{ + unsigned int id = socinfo_to_major(socinfo); + int i; + + for (i = 0 ; i < ARRAY_SIZE(soc_ids) ; ++i) { + if (soc_ids[i].id == id) + return soc_ids[i].name; + } + + return "Unknown"; +} + +int __init meson_gx_socinfo_init(void) +{ + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + struct device_node *np; + struct regmap *regmap; + unsigned int socinfo; + struct device *dev; + int ret; + + /* look up for chipid node */ + np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gx-ao-secure"); + if (!np) + return -ENODEV; + + /* check if interface is enabled */ + if (!of_device_is_available(np)) + return -ENODEV; + + /* check if chip-id is available */ + if (!of_property_read_bool(np, "amlogic,has-chip-id")) + return -ENODEV; + + /* node should be a syscon */ + regmap = syscon_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(regmap)) { + pr_err("%s: failed to get regmap\n", __func__); + return -ENODEV; + } + + ret = regmap_read(regmap, AO_SEC_SOCINFO_OFFSET, &socinfo); + if (ret < 0) + return ret; + + if (!socinfo) { + pr_err("%s: invalid chipid value\n", __func__); + return -EINVAL; + } + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENODEV; + + soc_dev_attr->family = "Amlogic Meson"; + + np = of_find_node_by_path("/"); + of_property_read_string(np, "model", &soc_dev_attr->machine); + of_node_put(np); + + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%x:%x - %x:%x", + socinfo_to_major(socinfo), + socinfo_to_minor(socinfo), + socinfo_to_pack(socinfo), + socinfo_to_misc(socinfo)); + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%s (%s)", + socinfo_to_soc_id(socinfo), + socinfo_to_package_id(socinfo)); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr->revision); + kfree_const(soc_dev_attr->soc_id); + kfree(soc_dev_attr); + return PTR_ERR(soc_dev); + } + dev = soc_device_to_device(soc_dev); + + dev_info(dev, "Amlogic Meson %s Revision %x:%x (%x:%x) Detected\n", + soc_dev_attr->soc_id, + socinfo_to_major(socinfo), + socinfo_to_minor(socinfo), + socinfo_to_pack(socinfo), + socinfo_to_misc(socinfo)); + + return 0; +} +device_initcall(meson_gx_socinfo_init); diff --git a/drivers/soc/fsl/qbman/bman_ccsr.c b/drivers/soc/fsl/qbman/bman_ccsr.c index a8e8389a6894..eaa9585c7347 100644 --- a/drivers/soc/fsl/qbman/bman_ccsr.c +++ b/drivers/soc/fsl/qbman/bman_ccsr.c @@ -177,8 +177,8 @@ static int fsl_bman_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - dev_err(dev, "Can't get %s property 'IORESOURCE_MEM'\n", - node->full_name); + dev_err(dev, "Can't get %pOF property 'IORESOURCE_MEM'\n", + node); return -ENXIO; } bm_ccsr_start = devm_ioremap(dev, res->start, resource_size(res)); @@ -205,14 +205,14 @@ static int fsl_bman_probe(struct platform_device *pdev) err_irq = platform_get_irq(pdev, 0); if (err_irq <= 0) { - dev_info(dev, "Can't get %s IRQ\n", node->full_name); + dev_info(dev, "Can't get %pOF IRQ\n", node); return -ENODEV; } ret = devm_request_irq(dev, err_irq, bman_isr, IRQF_SHARED, "bman-err", dev); if (ret) { - dev_err(dev, "devm_request_irq() failed %d for '%s'\n", - ret, node->full_name); + dev_err(dev, "devm_request_irq() failed %d for '%pOF'\n", + ret, node); return ret; } /* Disable Buffer Pool State Change */ diff --git a/drivers/soc/fsl/qbman/bman_portal.c b/drivers/soc/fsl/qbman/bman_portal.c index 8354d4dabdad..39b39c8f1399 100644 --- a/drivers/soc/fsl/qbman/bman_portal.c +++ b/drivers/soc/fsl/qbman/bman_portal.c @@ -103,16 +103,14 @@ static int bman_portal_probe(struct platform_device *pdev) addr_phys[0] = platform_get_resource(pdev, IORESOURCE_MEM, DPAA_PORTAL_CE); if (!addr_phys[0]) { - dev_err(dev, "Can't get %s property 'reg::CE'\n", - node->full_name); + dev_err(dev, "Can't get %pOF property 'reg::CE'\n", node); return -ENXIO; } addr_phys[1] = platform_get_resource(pdev, IORESOURCE_MEM, DPAA_PORTAL_CI); if (!addr_phys[1]) { - dev_err(dev, "Can't get %s property 'reg::CI'\n", - node->full_name); + dev_err(dev, "Can't get %pOF property 'reg::CI'\n", node); return -ENXIO; } @@ -120,7 +118,7 @@ static int bman_portal_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq <= 0) { - dev_err(dev, "Can't get %s IRQ'\n", node->full_name); + dev_err(dev, "Can't get %pOF IRQ'\n", node); return -ENXIO; } pcfg->irq = irq; diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c index 90bc40c48675..835ce947ffca 100644 --- a/drivers/soc/fsl/qbman/qman_ccsr.c +++ b/drivers/soc/fsl/qbman/qman_ccsr.c @@ -695,8 +695,8 @@ static int fsl_qman_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - dev_err(dev, "Can't get %s property 'IORESOURCE_MEM'\n", - node->full_name); + dev_err(dev, "Can't get %pOF property 'IORESOURCE_MEM'\n", + node); return -ENXIO; } qm_ccsr_start = devm_ioremap(dev, res->start, resource_size(res)); @@ -740,15 +740,15 @@ static int fsl_qman_probe(struct platform_device *pdev) err_irq = platform_get_irq(pdev, 0); if (err_irq <= 0) { - dev_info(dev, "Can't get %s property 'interrupts'\n", - node->full_name); + dev_info(dev, "Can't get %pOF property 'interrupts'\n", + node); return -ENODEV; } ret = devm_request_irq(dev, err_irq, qman_isr, IRQF_SHARED, "qman-err", dev); if (ret) { - dev_err(dev, "devm_request_irq() failed %d for '%s'\n", - ret, node->full_name); + dev_err(dev, "devm_request_irq() failed %d for '%pOF'\n", + ret, node); return ret; } diff --git a/drivers/soc/fsl/qbman/qman_portal.c b/drivers/soc/fsl/qbman/qman_portal.c index adbaa30d3c5a..cbacdf4f98ed 100644 --- a/drivers/soc/fsl/qbman/qman_portal.c +++ b/drivers/soc/fsl/qbman/qman_portal.c @@ -237,30 +237,27 @@ static int qman_portal_probe(struct platform_device *pdev) addr_phys[0] = platform_get_resource(pdev, IORESOURCE_MEM, DPAA_PORTAL_CE); if (!addr_phys[0]) { - dev_err(dev, "Can't get %s property 'reg::CE'\n", - node->full_name); + dev_err(dev, "Can't get %pOF property 'reg::CE'\n", node); return -ENXIO; } addr_phys[1] = platform_get_resource(pdev, IORESOURCE_MEM, DPAA_PORTAL_CI); if (!addr_phys[1]) { - dev_err(dev, "Can't get %s property 'reg::CI'\n", - node->full_name); + dev_err(dev, "Can't get %pOF property 'reg::CI'\n", node); return -ENXIO; } err = of_property_read_u32(node, "cell-index", &val); if (err) { - dev_err(dev, "Can't get %s property 'cell-index'\n", - node->full_name); + dev_err(dev, "Can't get %pOF property 'cell-index'\n", node); return err; } pcfg->channel = val; pcfg->cpu = -1; irq = platform_get_irq(pdev, 0); if (irq <= 0) { - dev_err(dev, "Can't get %s IRQ\n", node->full_name); + dev_err(dev, "Can't get %pOF IRQ\n", node); return -ENXIO; } pcfg->irq = irq; diff --git a/drivers/soc/fsl/qe/gpio.c b/drivers/soc/fsl/qe/gpio.c index 0aaf429f31d5..3b27075c21a7 100644 --- a/drivers/soc/fsl/qe/gpio.c +++ b/drivers/soc/fsl/qe/gpio.c @@ -304,8 +304,8 @@ static int __init qe_add_gpiochips(void) goto err; continue; err: - pr_err("%s: registration failed with status %d\n", - np->full_name, ret); + pr_err("%pOF: registration failed with status %d\n", + np, ret); kfree(qe_gc); /* try others anyway */ } diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index c80a04e1b2b1..c2048382830f 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -1067,7 +1067,7 @@ static const struct pmic_wrapper_type pwrap_mt2701 = { .init_soc_specific = pwrap_mt2701_init_soc_specific, }; -static struct pmic_wrapper_type pwrap_mt8135 = { +static const struct pmic_wrapper_type pwrap_mt8135 = { .regs = mt8135_regs, .type = PWRAP_MT8135, .arb_en_all = 0x1ff, @@ -1079,7 +1079,7 @@ static struct pmic_wrapper_type pwrap_mt8135 = { .init_soc_specific = pwrap_mt8135_init_soc_specific, }; -static struct pmic_wrapper_type pwrap_mt8173 = { +static const struct pmic_wrapper_type pwrap_mt8173 = { .regs = mt8173_regs, .type = PWRAP_MT8173, .arb_en_all = 0x3f, @@ -1091,7 +1091,7 @@ static struct pmic_wrapper_type pwrap_mt8173 = { .init_soc_specific = pwrap_mt8173_init_soc_specific, }; -static struct of_device_id of_pwrap_match_tbl[] = { +static const struct of_device_id of_pwrap_match_tbl[] = { { .compatible = "mediatek,mt2701-pwrap", .data = &pwrap_mt2701, @@ -1233,8 +1233,8 @@ static int pwrap_probe(struct platform_device *pdev) ret = of_platform_populate(np, NULL, NULL, wrp->dev); if (ret) { - dev_dbg(wrp->dev, "failed to create child devices at %s\n", - np->full_name); + dev_dbg(wrp->dev, "failed to create child devices at %pOF\n", + np); goto err_out2; } diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index ceb2cc495cd0..e1ce8b1b5090 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -22,6 +22,7 @@ #include <dt-bindings/power/mt2701-power.h> #include <dt-bindings/power/mt6797-power.h> +#include <dt-bindings/power/mt7622-power.h> #include <dt-bindings/power/mt8173-power.h> #define SPM_VDE_PWR_CON 0x0210 @@ -39,6 +40,11 @@ #define SPM_MFG_2D_PWR_CON 0x02c0 #define SPM_MFG_ASYNC_PWR_CON 0x02c4 #define SPM_USB_PWR_CON 0x02cc +#define SPM_ETHSYS_PWR_CON 0x02e0 /* MT7622 */ +#define SPM_HIF0_PWR_CON 0x02e4 /* MT7622 */ +#define SPM_HIF1_PWR_CON 0x02e8 /* MT7622 */ +#define SPM_WB_PWR_CON 0x02ec /* MT7622 */ + #define SPM_PWR_STATUS 0x060c #define SPM_PWR_STATUS_2ND 0x0610 @@ -64,6 +70,10 @@ #define PWR_STATUS_MFG_ASYNC BIT(23) #define PWR_STATUS_AUDIO BIT(24) #define PWR_STATUS_USB BIT(25) +#define PWR_STATUS_ETHSYS BIT(24) /* MT7622 */ +#define PWR_STATUS_HIF0 BIT(25) /* MT7622 */ +#define PWR_STATUS_HIF1 BIT(26) /* MT7622 */ +#define PWR_STATUS_WB BIT(27) /* MT7622 */ enum clk_id { CLK_NONE, @@ -73,6 +83,7 @@ enum clk_id { CLK_VENC_LT, CLK_ETHIF, CLK_VDEC, + CLK_HIFSEL, CLK_MAX, }; @@ -84,6 +95,7 @@ static const char * const clk_names[] = { "venc_lt", "ethif", "vdec", + "hif_sel", NULL, }; @@ -124,6 +136,19 @@ struct scp { struct scp_ctrl_reg ctrl_reg; }; +struct scp_subdomain { + int origin; + int subdomain; +}; + +struct scp_soc_data { + const struct scp_domain_data *domains; + int num_domains; + const struct scp_subdomain *subdomains; + int num_subdomains; + const struct scp_ctrl_reg regs; +}; + static int scpsys_domain_is_on(struct scp_domain *scpd) { struct scp *scp = scpd->scp; @@ -357,7 +382,7 @@ static void init_clks(struct platform_device *pdev, struct clk **clk) static struct scp *init_scp(struct platform_device *pdev, const struct scp_domain_data *scp_domain_data, int num, - struct scp_ctrl_reg *scp_ctrl_reg) + const struct scp_ctrl_reg *scp_ctrl_reg) { struct genpd_onecell_data *pd_data; struct resource *res; @@ -565,26 +590,6 @@ static const struct scp_domain_data scp_domain_data_mt2701[] = { }, }; -#define NUM_DOMAINS_MT2701 ARRAY_SIZE(scp_domain_data_mt2701) - -static int __init scpsys_probe_mt2701(struct platform_device *pdev) -{ - struct scp *scp; - struct scp_ctrl_reg scp_reg; - - scp_reg.pwr_sta_offs = SPM_PWR_STATUS; - scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND; - - scp = init_scp(pdev, scp_domain_data_mt2701, NUM_DOMAINS_MT2701, - &scp_reg); - if (IS_ERR(scp)) - return PTR_ERR(scp); - - mtk_register_power_domains(pdev, scp, NUM_DOMAINS_MT2701); - - return 0; -} - /* * MT6797 power domain support */ @@ -649,51 +654,62 @@ static const struct scp_domain_data scp_domain_data_mt6797[] = { }, }; -#define NUM_DOMAINS_MT6797 ARRAY_SIZE(scp_domain_data_mt6797) #define SPM_PWR_STATUS_MT6797 0x0180 #define SPM_PWR_STATUS_2ND_MT6797 0x0184 -static int __init scpsys_probe_mt6797(struct platform_device *pdev) -{ - struct scp *scp; - struct genpd_onecell_data *pd_data; - int ret; - struct scp_ctrl_reg scp_reg; - - scp_reg.pwr_sta_offs = SPM_PWR_STATUS_MT6797; - scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND_MT6797; - - scp = init_scp(pdev, scp_domain_data_mt6797, NUM_DOMAINS_MT6797, - &scp_reg); - if (IS_ERR(scp)) - return PTR_ERR(scp); - - mtk_register_power_domains(pdev, scp, NUM_DOMAINS_MT6797); - - pd_data = &scp->pd_data; - - ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM], - pd_data->domains[MT6797_POWER_DOMAIN_VDEC]); - if (ret && IS_ENABLED(CONFIG_PM)) - dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); - - ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM], - pd_data->domains[MT6797_POWER_DOMAIN_ISP]); - if (ret && IS_ENABLED(CONFIG_PM)) - dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); - - ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM], - pd_data->domains[MT6797_POWER_DOMAIN_VENC]); - if (ret && IS_ENABLED(CONFIG_PM)) - dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); +static const struct scp_subdomain scp_subdomain_mt6797[] = { + {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_VDEC}, + {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_ISP}, + {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_VENC}, + {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_MJC}, +}; - ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM], - pd_data->domains[MT6797_POWER_DOMAIN_MJC]); - if (ret && IS_ENABLED(CONFIG_PM)) - dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); +/* + * MT7622 power domain support + */ - return 0; -} +static const struct scp_domain_data scp_domain_data_mt7622[] = { + [MT7622_POWER_DOMAIN_ETHSYS] = { + .name = "ethsys", + .sta_mask = PWR_STATUS_ETHSYS, + .ctl_offs = SPM_ETHSYS_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_NONE}, + .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_ETHSYS, + .active_wakeup = true, + }, + [MT7622_POWER_DOMAIN_HIF0] = { + .name = "hif0", + .sta_mask = PWR_STATUS_HIF0, + .ctl_offs = SPM_HIF0_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_HIFSEL}, + .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_HIF0, + .active_wakeup = true, + }, + [MT7622_POWER_DOMAIN_HIF1] = { + .name = "hif1", + .sta_mask = PWR_STATUS_HIF1, + .ctl_offs = SPM_HIF1_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_HIFSEL}, + .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_HIF1, + .active_wakeup = true, + }, + [MT7622_POWER_DOMAIN_WB] = { + .name = "wb", + .sta_mask = PWR_STATUS_WB, + .ctl_offs = SPM_WB_PWR_CON, + .sram_pdn_bits = 0, + .sram_pdn_ack_bits = 0, + .clk_id = {CLK_NONE}, + .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_WB, + .active_wakeup = true, + }, +}; /* * MT8173 power domain support @@ -789,39 +805,50 @@ static const struct scp_domain_data scp_domain_data_mt8173[] = { }, }; -#define NUM_DOMAINS_MT8173 ARRAY_SIZE(scp_domain_data_mt8173) - -static int __init scpsys_probe_mt8173(struct platform_device *pdev) -{ - struct scp *scp; - struct genpd_onecell_data *pd_data; - int ret; - struct scp_ctrl_reg scp_reg; - - scp_reg.pwr_sta_offs = SPM_PWR_STATUS; - scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND; - - scp = init_scp(pdev, scp_domain_data_mt8173, NUM_DOMAINS_MT8173, - &scp_reg); - if (IS_ERR(scp)) - return PTR_ERR(scp); - - mtk_register_power_domains(pdev, scp, NUM_DOMAINS_MT8173); +static const struct scp_subdomain scp_subdomain_mt8173[] = { + {MT8173_POWER_DOMAIN_MFG_ASYNC, MT8173_POWER_DOMAIN_MFG_2D}, + {MT8173_POWER_DOMAIN_MFG_2D, MT8173_POWER_DOMAIN_MFG}, +}; - pd_data = &scp->pd_data; +static const struct scp_soc_data mt2701_data = { + .domains = scp_domain_data_mt2701, + .num_domains = ARRAY_SIZE(scp_domain_data_mt2701), + .regs = { + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND + } +}; - ret = pm_genpd_add_subdomain(pd_data->domains[MT8173_POWER_DOMAIN_MFG_ASYNC], - pd_data->domains[MT8173_POWER_DOMAIN_MFG_2D]); - if (ret && IS_ENABLED(CONFIG_PM)) - dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); +static const struct scp_soc_data mt6797_data = { + .domains = scp_domain_data_mt6797, + .num_domains = ARRAY_SIZE(scp_domain_data_mt6797), + .subdomains = scp_subdomain_mt6797, + .num_subdomains = ARRAY_SIZE(scp_subdomain_mt6797), + .regs = { + .pwr_sta_offs = SPM_PWR_STATUS_MT6797, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND_MT6797 + } +}; - ret = pm_genpd_add_subdomain(pd_data->domains[MT8173_POWER_DOMAIN_MFG_2D], - pd_data->domains[MT8173_POWER_DOMAIN_MFG]); - if (ret && IS_ENABLED(CONFIG_PM)) - dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); +static const struct scp_soc_data mt7622_data = { + .domains = scp_domain_data_mt7622, + .num_domains = ARRAY_SIZE(scp_domain_data_mt7622), + .regs = { + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND + } +}; - return 0; -} +static const struct scp_soc_data mt8173_data = { + .domains = scp_domain_data_mt8173, + .num_domains = ARRAY_SIZE(scp_domain_data_mt8173), + .subdomains = scp_subdomain_mt8173, + .num_subdomains = ARRAY_SIZE(scp_subdomain_mt8173), + .regs = { + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND + } +}; /* * scpsys driver init @@ -830,13 +857,16 @@ static int __init scpsys_probe_mt8173(struct platform_device *pdev) static const struct of_device_id of_scpsys_match_tbl[] = { { .compatible = "mediatek,mt2701-scpsys", - .data = scpsys_probe_mt2701, + .data = &mt2701_data, }, { .compatible = "mediatek,mt6797-scpsys", - .data = scpsys_probe_mt6797, + .data = &mt6797_data, + }, { + .compatible = "mediatek,mt7622-scpsys", + .data = &mt7622_data, }, { .compatible = "mediatek,mt8173-scpsys", - .data = scpsys_probe_mt8173, + .data = &mt8173_data, }, { /* sentinel */ } @@ -844,16 +874,33 @@ static const struct of_device_id of_scpsys_match_tbl[] = { static int scpsys_probe(struct platform_device *pdev) { - int (*probe)(struct platform_device *); - const struct of_device_id *of_id; + const struct of_device_id *match; + const struct scp_subdomain *sd; + const struct scp_soc_data *soc; + struct scp *scp; + struct genpd_onecell_data *pd_data; + int i, ret; - of_id = of_match_node(of_scpsys_match_tbl, pdev->dev.of_node); - if (!of_id || !of_id->data) - return -EINVAL; + match = of_match_device(of_scpsys_match_tbl, &pdev->dev); + soc = (const struct scp_soc_data *)match->data; + + scp = init_scp(pdev, soc->domains, soc->num_domains, &soc->regs); + if (IS_ERR(scp)) + return PTR_ERR(scp); - probe = of_id->data; + mtk_register_power_domains(pdev, scp, soc->num_domains); - return probe(pdev); + pd_data = &scp->pd_data; + + for (i = 0, sd = soc->subdomains ; i < soc->num_subdomains ; i++) { + ret = pm_genpd_add_subdomain(pd_data->domains[sd->origin], + pd_data->domains[sd->subdomain]); + if (ret && IS_ENABLED(CONFIG_PM)) + dev_err(&pdev->dev, "Failed to add subdomain: %d\n", + ret); + } + + return 0; } static struct platform_driver scpsys_drv = { diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 9fca977ef18d..b00bccddcd3b 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -1,6 +1,17 @@ # # QCOM Soc drivers # +menu "Qualcomm SoC drivers" + +config QCOM_GLINK_SSR + tristate "Qualcomm Glink SSR driver" + depends on RPMSG + depends on QCOM_RPROC_COMMON + help + Say y here to enable GLINK SSR support. The GLINK SSR driver + implements the SSR protocol for notifying the remote processor about + neighboring subsystems going up or down. + config QCOM_GSBI tristate "QCOM General Serial Bus Interface" depends on ARCH_QCOM @@ -74,3 +85,5 @@ config QCOM_WCNSS_CTRL help Client driver for the WCNSS_CTRL SMD channel, used to download nv firmware to a newly booted WCNSS chip. + +endmenu diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 414f0de274fa..f151de41eb93 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_PM) += spm.o diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c new file mode 100644 index 000000000000..19c7399eddb5 --- /dev/null +++ b/drivers/soc/qcom/glink_ssr.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017, Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/completion.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/rpmsg.h> +#include <linux/remoteproc/qcom_rproc.h> + +/** + * struct do_cleanup_msg - The data structure for an SSR do_cleanup message + * version: The G-Link SSR protocol version + * command: The G-Link SSR command - do_cleanup + * seq_num: Sequence number + * name_len: Length of the name of the subsystem being restarted + * name: G-Link edge name of the subsystem being restarted + */ +struct do_cleanup_msg { + __le32 version; + __le32 command; + __le32 seq_num; + __le32 name_len; + char name[32]; +}; + +/** + * struct cleanup_done_msg - The data structure for an SSR cleanup_done message + * version: The G-Link SSR protocol version + * response: The G-Link SSR response to a do_cleanup command, cleanup_done + * seq_num: Sequence number + */ +struct cleanup_done_msg { + __le32 version; + __le32 response; + __le32 seq_num; +}; + +/** + * G-Link SSR protocol commands + */ +#define GLINK_SSR_DO_CLEANUP 0 +#define GLINK_SSR_CLEANUP_DONE 1 + +struct glink_ssr { + struct device *dev; + struct rpmsg_endpoint *ept; + + struct notifier_block nb; + + u32 seq_num; + struct completion completion; +}; + +static int qcom_glink_ssr_callback(struct rpmsg_device *rpdev, + void *data, int len, void *priv, u32 addr) +{ + struct cleanup_done_msg *msg = data; + struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); + + if (len < sizeof(*msg)) { + dev_err(ssr->dev, "message too short\n"); + return -EINVAL; + } + + if (le32_to_cpu(msg->version) != 0) + return -EINVAL; + + if (le32_to_cpu(msg->response) != GLINK_SSR_CLEANUP_DONE) + return 0; + + if (le32_to_cpu(msg->seq_num) != ssr->seq_num) { + dev_err(ssr->dev, "invalid sequence number of response\n"); + return -EINVAL; + } + + complete(&ssr->completion); + + return 0; +} + +static int qcom_glink_ssr_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct glink_ssr *ssr = container_of(nb, struct glink_ssr, nb); + struct do_cleanup_msg msg; + char *ssr_name = data; + int ret; + + ssr->seq_num++; + reinit_completion(&ssr->completion); + + memset(&msg, 0, sizeof(msg)); + msg.command = cpu_to_le32(GLINK_SSR_DO_CLEANUP); + msg.seq_num = cpu_to_le32(ssr->seq_num); + msg.name_len = cpu_to_le32(strlen(ssr_name)); + strlcpy(msg.name, ssr_name, sizeof(msg.name)); + + ret = rpmsg_send(ssr->ept, &msg, sizeof(msg)); + if (ret < 0) + dev_err(ssr->dev, "failed to send cleanup message\n"); + + ret = wait_for_completion_timeout(&ssr->completion, HZ); + if (!ret) + dev_err(ssr->dev, "timeout waiting for cleanup done message\n"); + + return NOTIFY_DONE; +} + +static int qcom_glink_ssr_probe(struct rpmsg_device *rpdev) +{ + struct glink_ssr *ssr; + + ssr = devm_kzalloc(&rpdev->dev, sizeof(*ssr), GFP_KERNEL); + if (!ssr) + return -ENOMEM; + + init_completion(&ssr->completion); + + ssr->dev = &rpdev->dev; + ssr->ept = rpdev->ept; + ssr->nb.notifier_call = qcom_glink_ssr_notify; + + dev_set_drvdata(&rpdev->dev, ssr); + + return qcom_register_ssr_notifier(&ssr->nb); +} + +static void qcom_glink_ssr_remove(struct rpmsg_device *rpdev) +{ + struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); + + qcom_unregister_ssr_notifier(&ssr->nb); +} + +static const struct rpmsg_device_id qcom_glink_ssr_match[] = { + { "glink_ssr" }, + {} +}; + +static struct rpmsg_driver qcom_glink_ssr_driver = { + .probe = qcom_glink_ssr_probe, + .remove = qcom_glink_ssr_remove, + .callback = qcom_glink_ssr_callback, + .id_table = qcom_glink_ssr_match, + .drv = { + .name = "qcom_glink_ssr", + }, +}; +module_rpmsg_driver(qcom_glink_ssr_driver); + +MODULE_ALIAS("rpmsg:glink_ssr"); +MODULE_DESCRIPTION("Qualcomm GLINK SSR notifier"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index bd63df0d14e0..08bd8549242a 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -178,14 +178,13 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, if (phdr->p_filesz) { sprintf(fw_name + fw_name_len - 3, "b%02d", i); - ret = request_firmware(&seg_fw, fw_name, dev); + ret = request_firmware_into_buf(&seg_fw, fw_name, dev, + ptr, phdr->p_filesz); if (ret) { dev_err(dev, "failed to load %s\n", fw_name); break; } - memcpy(ptr, seg_fw->data, seg_fw->size); - release_firmware(seg_fw); } diff --git a/drivers/soc/qcom/smsm.c b/drivers/soc/qcom/smsm.c index dc540ea92e9d..403bea9d546b 100644 --- a/drivers/soc/qcom/smsm.c +++ b/drivers/soc/qcom/smsm.c @@ -496,7 +496,8 @@ static int qcom_smsm_probe(struct platform_device *pdev) if (!smsm->hosts) return -ENOMEM; - local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,smem-state-cells"); + local_node = of_find_node_with_property(of_node_get(pdev->dev.of_node), + "#qcom,smem-state-cells"); if (!local_node) { dev_err(&pdev->dev, "no state entry\n"); return -EINVAL; diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c index b9069184df19..d008e5b82db4 100644 --- a/drivers/soc/qcom/wcnss_ctrl.c +++ b/drivers/soc/qcom/wcnss_ctrl.c @@ -347,6 +347,7 @@ static const struct of_device_id wcnss_ctrl_of_match[] = { { .compatible = "qcom,wcnss", }, {} }; +MODULE_DEVICE_TABLE(of, wcnss_ctrl_of_match); static struct rpmsg_driver wcnss_ctrl_driver = { .probe = wcnss_ctrl_probe, diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 87a4be46bd98..567414cb42ba 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -3,7 +3,7 @@ config SOC_RENESAS default y if ARCH_RENESAS select SOC_BUS select RST_RCAR if ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || \ - ARCH_R8A7795 || ARCH_R8A7796 + ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77995 select SYSC_R8A7743 if ARCH_R8A7743 select SYSC_R8A7745 if ARCH_R8A7745 select SYSC_R8A7779 if ARCH_R8A7779 @@ -13,6 +13,7 @@ config SOC_RENESAS select SYSC_R8A7794 if ARCH_R8A7794 select SYSC_R8A7795 if ARCH_R8A7795 select SYSC_R8A7796 if ARCH_R8A7796 + select SYSC_R8A77995 if ARCH_R8A77995 if SOC_RENESAS @@ -53,6 +54,10 @@ config SYSC_R8A7796 bool "R-Car M3-W System Controller support" if COMPILE_TEST select SYSC_RCAR +config SYSC_R8A77995 + bool "R-Car D3 System Controller support" if COMPILE_TEST + select SYSC_RCAR + # Family config RST_RCAR bool "R-Car Reset Controller support" if COMPILE_TEST diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 1a1a297b26a7..6b6e7f16104c 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o +obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o # Family obj-$(CONFIG_RST_RCAR) += rcar-rst.o diff --git a/drivers/soc/renesas/r8a77995-sysc.c b/drivers/soc/renesas/r8a77995-sysc.c new file mode 100644 index 000000000000..f718429cab02 --- /dev/null +++ b/drivers/soc/renesas/r8a77995-sysc.c @@ -0,0 +1,31 @@ +/* + * Renesas R-Car D3 System Controller + * + * Copyright (C) 2017 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/bug.h> +#include <linux/kernel.h> +#include <linux/sys_soc.h> + +#include <dt-bindings/power/r8a77995-sysc.h> + +#include "rcar-sysc.h" + +static struct rcar_sysc_area r8a77995_areas[] __initdata = { + { "always-on", 0, 0, R8A77995_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca53-scu", 0x140, 0, R8A77995_PD_CA53_SCU, R8A77995_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A77995_PD_CA53_CPU0, R8A77995_PD_CA53_SCU, + PD_CPU_NOCR }, +}; + + +const struct rcar_sysc_info r8a77995_sysc_info __initconst = { + .areas = r8a77995_areas, + .num_areas = ARRAY_SIZE(r8a77995_areas), +}; diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index a6d1c26d3167..baa47014e96b 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -41,6 +41,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { /* R-Car Gen3 is handled like R-Car Gen2 */ { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen2 }, { /* sentinel */ } }; @@ -61,7 +62,7 @@ static int __init rcar_rst_init(void) base = of_iomap(np, 0); if (!base) { - pr_warn("%s: Cannot map regs\n", np->full_name); + pr_warn("%pOF: Cannot map regs\n", np); error = -ENOMEM; goto out_put; } @@ -70,7 +71,7 @@ static int __init rcar_rst_init(void) cfg = match->data; saved_mode = ioread32(base + cfg->modemr); - pr_debug("%s: MODE = 0x%08x\n", np->full_name, saved_mode); + pr_debug("%pOF: MODE = 0x%08x\n", np, saved_mode); out_put: of_node_put(np); diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 7c8da3c90011..c8406e81640f 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -284,6 +284,9 @@ static const struct of_device_id rcar_sysc_matches[] = { #ifdef CONFIG_SYSC_R8A7796 { .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info }, #endif +#ifdef CONFIG_SYSC_R8A77995 + { .compatible = "renesas,r8a77995-sysc", .data = &r8a77995_sysc_info }, +#endif { /* sentinel */ } }; @@ -323,7 +326,7 @@ static int __init rcar_sysc_pd_init(void) base = of_iomap(np, 0); if (!base) { - pr_warn("%s: Cannot map regs\n", np->full_name); + pr_warn("%pOF: Cannot map regs\n", np); error = -ENOMEM; goto out_put; } @@ -348,13 +351,13 @@ static int __init rcar_sysc_pd_init(void) */ syscimr = ioread32(base + SYSCIMR); syscimr |= syscier; - pr_debug("%s: syscimr = 0x%08x\n", np->full_name, syscimr); + pr_debug("%pOF: syscimr = 0x%08x\n", np, syscimr); iowrite32(syscimr, base + SYSCIMR); /* * SYSC needs all interrupt sources enabled to control power. */ - pr_debug("%s: syscier = 0x%08x\n", np->full_name, syscier); + pr_debug("%pOF: syscier = 0x%08x\n", np, syscier); iowrite32(syscier, base + SYSCIER); for (i = 0; i < info->num_areas; i++) { diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 1a5bebaf54ba..2f524922c4d2 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -58,6 +58,7 @@ extern const struct rcar_sysc_info r8a7792_sysc_info; extern const struct rcar_sysc_info r8a7794_sysc_info; extern const struct rcar_sysc_info r8a7795_sysc_info; extern const struct rcar_sysc_info r8a7796_sysc_info; +extern const struct rcar_sysc_info r8a77995_sysc_info; /* diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index ca26f13d399c..90d6b7a4340a 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -144,6 +144,11 @@ static const struct renesas_soc soc_rcar_m3_w __initconst __maybe_unused = { .id = 0x52, }; +static const struct renesas_soc soc_rcar_d3 __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x58, +}; + static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = { .family = &fam_shmobile, .id = 0x37, @@ -199,6 +204,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A7796 { .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w }, #endif +#ifdef CONFIG_ARCH_R8A77995 + { .compatible = "renesas,r8a77995", .data = &soc_rcar_d3 }, +#endif #ifdef CONFIG_ARCH_SH73A0 { .compatible = "renesas,sh73a0", .data = &soc_shmobile_ag5 }, #endif diff --git a/drivers/soc/rockchip/grf.c b/drivers/soc/rockchip/grf.c index d61db34ad6dd..15e71fd6c513 100644 --- a/drivers/soc/rockchip/grf.c +++ b/drivers/soc/rockchip/grf.c @@ -54,6 +54,17 @@ static const struct rockchip_grf_info rk3288_grf __initconst = { .num_values = ARRAY_SIZE(rk3288_defaults), }; +#define RK3328_GRF_SOC_CON4 0x410 + +static const struct rockchip_grf_value rk3328_defaults[] __initconst = { + { "jtag switching", RK3328_GRF_SOC_CON4, HIWORD_UPDATE(0, 1, 12) }, +}; + +static const struct rockchip_grf_info rk3328_grf __initconst = { + .values = rk3328_defaults, + .num_values = ARRAY_SIZE(rk3328_defaults), +}; + #define RK3368_GRF_SOC_CON15 0x43c static const struct rockchip_grf_value rk3368_defaults[] __initconst = { @@ -84,6 +95,9 @@ static const struct of_device_id rockchip_grf_dt_match[] __initconst = { .compatible = "rockchip,rk3288-grf", .data = (void *)&rk3288_grf, }, { + .compatible = "rockchip,rk3328-grf", + .data = (void *)&rk3328_grf, + }, { .compatible = "rockchip,rk3368-grf", .data = (void *)&rk3368_grf, }, { diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c index 796c46a6cbe7..40b75748835f 100644 --- a/drivers/soc/rockchip/pm_domains.c +++ b/drivers/soc/rockchip/pm_domains.c @@ -20,6 +20,7 @@ #include <linux/mfd/syscon.h> #include <dt-bindings/power/rk3288-power.h> #include <dt-bindings/power/rk3328-power.h> +#include <dt-bindings/power/rk3366-power.h> #include <dt-bindings/power/rk3368-power.h> #include <dt-bindings/power/rk3399-power.h> @@ -730,6 +731,16 @@ static const struct rockchip_domain_info rk3328_pm_domains[] = { [RK3328_PD_VPU] = DOMAIN_RK3328(-1, 9, 9, false), }; +static const struct rockchip_domain_info rk3366_pm_domains[] = { + [RK3366_PD_PERI] = DOMAIN_RK3368(10, 10, 6, true), + [RK3366_PD_VIO] = DOMAIN_RK3368(14, 14, 8, false), + [RK3366_PD_VIDEO] = DOMAIN_RK3368(13, 13, 7, false), + [RK3366_PD_RKVDEC] = DOMAIN_RK3368(11, 11, 7, false), + [RK3366_PD_WIFIBT] = DOMAIN_RK3368(8, 8, 9, false), + [RK3366_PD_VPU] = DOMAIN_RK3368(12, 12, 7, false), + [RK3366_PD_GPU] = DOMAIN_RK3368(15, 15, 2, false), +}; + static const struct rockchip_domain_info rk3368_pm_domains[] = { [RK3368_PD_PERI] = DOMAIN_RK3368(13, 12, 6, true), [RK3368_PD_VIO] = DOMAIN_RK3368(15, 14, 8, false), @@ -794,6 +805,23 @@ static const struct rockchip_pmu_info rk3328_pmu = { .domain_info = rk3328_pm_domains, }; +static const struct rockchip_pmu_info rk3366_pmu = { + .pwr_offset = 0x0c, + .status_offset = 0x10, + .req_offset = 0x3c, + .idle_offset = 0x40, + .ack_offset = 0x40, + + .core_pwrcnt_offset = 0x48, + .gpu_pwrcnt_offset = 0x50, + + .core_power_transition_time = 24, + .gpu_power_transition_time = 24, + + .num_domains = ARRAY_SIZE(rk3366_pm_domains), + .domain_info = rk3366_pm_domains, +}; + static const struct rockchip_pmu_info rk3368_pmu = { .pwr_offset = 0x0c, .status_offset = 0x10, @@ -834,6 +862,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = { .data = (void *)&rk3328_pmu, }, { + .compatible = "rockchip,rk3366-power-controller", + .data = (void *)&rk3366_pmu, + }, + { .compatible = "rockchip,rk3368-power-controller", .data = (void *)&rk3368_pmu, }, diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c index a6a5d807cc2b..7c4fec1f93b5 100644 --- a/drivers/soc/samsung/pm_domains.c +++ b/drivers/soc/samsung/pm_domains.c @@ -147,7 +147,7 @@ static __init const char *exynos_get_domain_name(struct device_node *node) const char *name; if (of_property_read_string(node, "label", &name) < 0) - name = strrchr(node->full_name, '/') + 1; + name = kbasename(node->full_name); return kstrdup_const(name, GFP_KERNEL); } @@ -237,11 +237,11 @@ no_clk: continue; if (of_genpd_add_subdomain(&parent, &child)) - pr_warn("%s failed to add subdomain: %s\n", - parent.np->full_name, child.np->full_name); + pr_warn("%pOF failed to add subdomain: %pOF\n", + parent.np, child.np); else - pr_info("%s has as child subdomain: %s.\n", - parent.np->full_name, child.np->full_name); + pr_info("%pOF has as child subdomain: %pOF.\n", + parent.np, child.np); } return 0; diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index 99e354c8f53f..882be5ed7e84 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -23,6 +23,7 @@ struct sunxi_sram_func { char *func; u8 val; + u32 reg_val; }; struct sunxi_sram_data { @@ -39,10 +40,11 @@ struct sunxi_sram_desc { bool claimed; }; -#define SUNXI_SRAM_MAP(_val, _func) \ +#define SUNXI_SRAM_MAP(_reg_val, _val, _func) \ { \ .func = _func, \ .val = _val, \ + .reg_val = _reg_val, \ } #define SUNXI_SRAM_DATA(_name, _reg, _off, _width, ...) \ @@ -57,14 +59,20 @@ struct sunxi_sram_desc { static struct sunxi_sram_desc sun4i_a10_sram_a3_a4 = { .data = SUNXI_SRAM_DATA("A3-A4", 0x4, 0x4, 2, - SUNXI_SRAM_MAP(0, "cpu"), - SUNXI_SRAM_MAP(1, "emac")), + SUNXI_SRAM_MAP(0, 0, "cpu"), + SUNXI_SRAM_MAP(1, 1, "emac")), }; static struct sunxi_sram_desc sun4i_a10_sram_d = { .data = SUNXI_SRAM_DATA("D", 0x4, 0x0, 1, - SUNXI_SRAM_MAP(0, "cpu"), - SUNXI_SRAM_MAP(1, "usb-otg")), + SUNXI_SRAM_MAP(0, 0, "cpu"), + SUNXI_SRAM_MAP(1, 1, "usb-otg")), +}; + +static struct sunxi_sram_desc sun50i_a64_sram_c = { + .data = SUNXI_SRAM_DATA("C", 0x4, 24, 1, + SUNXI_SRAM_MAP(0, 1, "cpu"), + SUNXI_SRAM_MAP(1, 0, "de2")), }; static const struct of_device_id sunxi_sram_dt_ids[] = { @@ -76,6 +84,10 @@ static const struct of_device_id sunxi_sram_dt_ids[] = { .compatible = "allwinner,sun4i-a10-sram-d", .data = &sun4i_a10_sram_d.data, }, + { + .compatible = "allwinner,sun50i-a64-sram-c", + .data = &sun50i_a64_sram_c.data, + }, {} }; @@ -121,7 +133,8 @@ static int sunxi_sram_show(struct seq_file *s, void *data) for (func = sram_data->func; func->func; func++) { seq_printf(s, "\t\t%s%c\n", func->func, - func->val == val ? '*' : ' '); + func->reg_val == val ? + '*' : ' '); } } @@ -149,10 +162,13 @@ static inline struct sunxi_sram_desc *to_sram_desc(const struct sunxi_sram_data } static const struct sunxi_sram_data *sunxi_sram_of_parse(struct device_node *node, - unsigned int *value) + unsigned int *reg_value) { const struct of_device_id *match; + const struct sunxi_sram_data *data; + struct sunxi_sram_func *func; struct of_phandle_args args; + u8 val; int ret; ret = of_parse_phandle_with_fixed_args(node, "allwinner,sram", 1, 0, @@ -165,8 +181,7 @@ static const struct sunxi_sram_data *sunxi_sram_of_parse(struct device_node *nod goto err; } - if (value) - *value = args.args[0]; + val = args.args[0]; match = of_match_node(sunxi_sram_dt_ids, args.np); if (!match) { @@ -174,6 +189,26 @@ static const struct sunxi_sram_data *sunxi_sram_of_parse(struct device_node *nod goto err; } + data = match->data; + if (!data) { + ret = -EINVAL; + goto err; + }; + + for (func = data->func; func->func; func++) { + if (val == func->val) { + if (reg_value) + *reg_value = func->reg_val; + + break; + } + } + + if (!func->func) { + ret = -EINVAL; + goto err; + } + of_node_put(args.np); return match->data; @@ -190,6 +225,9 @@ int sunxi_sram_claim(struct device *dev) u32 val, mask; if (IS_ERR(base)) + return PTR_ERR(base); + + if (!base) return -EPROBE_DEFER; if (!dev || !dev->of_node) @@ -267,6 +305,7 @@ static int sunxi_sram_probe(struct platform_device *pdev) static const struct of_device_id sunxi_sram_dt_match[] = { { .compatible = "allwinner,sun4i-a10-sram-controller" }, + { .compatible = "allwinner,sun50i-a64-sram-controller" }, { }, }; MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match); diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index 1beb7c347344..e9e277178c94 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -107,6 +107,11 @@ config ARCH_TEGRA_186_SOC endif endif +config SOC_TEGRA_FUSE + def_bool y + depends on ARCH_TEGRA + select SOC_BUS + config SOC_TEGRA_FLOWCTRL bool diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 7413f60fa855..b7c552e3133c 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -19,10 +19,12 @@ #include <linux/device.h> #include <linux/kobject.h> #include <linux/init.h> -#include <linux/platform_device.h> +#include <linux/io.h> #include <linux/of.h> #include <linux/of_address.h> -#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> #include <soc/tegra/common.h> #include <soc/tegra/fuse.h> @@ -210,6 +212,31 @@ static void tegra_enable_fuse_clk(void __iomem *base) writel(reg, base + 0x14); } +struct device * __init tegra_soc_device_register(void) +{ + struct soc_device_attribute *attr; + struct soc_device *dev; + + attr = kzalloc(sizeof(*attr), GFP_KERNEL); + if (!attr) + return NULL; + + attr->family = kasprintf(GFP_KERNEL, "Tegra"); + attr->revision = kasprintf(GFP_KERNEL, "%d", tegra_sku_info.revision); + attr->soc_id = kasprintf(GFP_KERNEL, "%u", tegra_get_chip_id()); + + dev = soc_device_register(attr); + if (IS_ERR(dev)) { + kfree(attr->soc_id); + kfree(attr->revision); + kfree(attr->family); + kfree(attr); + return ERR_CAST(dev); + } + + return soc_device_to_device(dev); +} + static int __init tegra_init_fuse(void) { const struct of_device_id *match; @@ -311,6 +338,31 @@ static int __init tegra_init_fuse(void) pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n", tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); + return 0; } early_initcall(tegra_init_fuse); + +#ifdef CONFIG_ARM64 +static int __init tegra_init_soc(void) +{ + struct device_node *np; + struct device *soc; + + /* make sure we're running on Tegra */ + np = of_find_matching_node(NULL, tegra_fuse_match); + if (!np) + return 0; + + of_node_put(np); + + soc = tegra_soc_device_register(); + if (IS_ERR(soc)) { + pr_err("failed to register SoC device: %ld\n", PTR_ERR(soc)); + return PTR_ERR(soc); + } + + return 0; +} +device_initcall(tegra_init_soc); +#endif diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index e233dd5dcab3..0453ff6839a7 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -918,10 +918,8 @@ static void tegra_powergate_init(struct tegra_pmc *pmc, if (!np) return; - for_each_child_of_node(np, child) { + for_each_child_of_node(np, child) tegra_powergate_add(pmc, child); - of_node_put(child); - } of_node_put(np); } diff --git a/drivers/soc/versatile/soc-realview.c b/drivers/soc/versatile/soc-realview.c index 282e371378ce..caf698e5f0b0 100644 --- a/drivers/soc/versatile/soc-realview.c +++ b/drivers/soc/versatile/soc-realview.c @@ -85,7 +85,7 @@ static struct device_attribute realview_build_attr = static int realview_soc_probe(struct platform_device *pdev) { - static struct regmap *syscon_regmap; + struct regmap *syscon_regmap; struct soc_device *soc_dev; struct soc_device_attribute *soc_dev_attr; struct device_node *np = pdev->dev.of_node; diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c index a4e3ae8f0c85..13eaf16ecd16 100644 --- a/drivers/staging/mt29f_spinand/mt29f_spinand.c +++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c @@ -18,7 +18,7 @@ #include <linux/delay.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> -#include <linux/mtd/nand.h> +#include <linux/mtd/rawnand.h> #include <linux/spi/spi.h> #include "mt29f_spinand.h" diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 58169e519422..7952357df9c8 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -224,13 +224,14 @@ static void optee_release(struct tee_context *ctx) if (!IS_ERR(shm)) { arg = tee_shm_get_va(shm, 0); /* - * If va2pa fails for some reason, we can't call - * optee_close_session(), only free the memory. Secure OS - * will leak sessions and finally refuse more sessions, but - * we will at least let normal world reclaim its memory. + * If va2pa fails for some reason, we can't call into + * secure world, only free the memory. Secure OS will leak + * sessions and finally refuse more sessions, but we will + * at least let normal world reclaim its memory. */ if (!IS_ERR(arg)) - tee_shm_va2pa(shm, arg, &parg); + if (tee_shm_va2pa(shm, arg, &parg)) + arg = NULL; /* prevent usage of parg below */ } list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list, @@ -258,7 +259,7 @@ static void optee_release(struct tee_context *ctx) } } -static struct tee_driver_ops optee_ops = { +static const struct tee_driver_ops optee_ops = { .get_version = optee_get_version, .open = optee_open, .release = optee_release, @@ -268,13 +269,13 @@ static struct tee_driver_ops optee_ops = { .cancel_req = optee_cancel_req, }; -static struct tee_desc optee_desc = { +static const struct tee_desc optee_desc = { .name = DRIVER_NAME "-clnt", .ops = &optee_ops, .owner = THIS_MODULE, }; -static struct tee_driver_ops optee_supp_ops = { +static const struct tee_driver_ops optee_supp_ops = { .get_version = optee_get_version, .open = optee_open, .release = optee_release, @@ -282,7 +283,7 @@ static struct tee_driver_ops optee_supp_ops = { .supp_send = optee_supp_send, }; -static struct tee_desc optee_supp_desc = { +static const struct tee_desc optee_supp_desc = { .name = DRIVER_NAME "-supp", .ops = &optee_supp_ops, .owner = THIS_MODULE, diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 13b7c98cdf25..069c8e1429de 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -298,7 +298,7 @@ struct optee_smc_disable_shm_cache_result { OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE) /* - * Resume from RPC (for example after processing an IRQ) + * Resume from RPC (for example after processing a foreign interrupt) * * Call register usage: * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC @@ -383,19 +383,19 @@ struct optee_smc_disable_shm_cache_result { OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE) /* - * Deliver an IRQ in normal world. + * Deliver foreign interrupt to normal world. * * "Call" register usage: - * a0 OPTEE_SMC_RETURN_RPC_IRQ + * a0 OPTEE_SMC_RETURN_RPC_FOREIGN_INTR * a1-7 Resume information, must be preserved * * "Return" register usage: * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. * a1-7 Preserved */ -#define OPTEE_SMC_RPC_FUNC_IRQ 4 -#define OPTEE_SMC_RETURN_RPC_IRQ \ - OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_IRQ) +#define OPTEE_SMC_RPC_FUNC_FOREIGN_INTR 4 +#define OPTEE_SMC_RETURN_RPC_FOREIGN_INTR \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FOREIGN_INTR) /* * Do an RPC request. The supplied struct optee_msg_arg tells which diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 8814eca06021..cef417f4f4d2 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -140,11 +140,8 @@ static void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg) msec_to_wait = arg->params[0].u.value.a; - /* set task's state to interruptible sleep */ - set_current_state(TASK_INTERRUPTIBLE); - - /* take a nap */ - msleep(msec_to_wait); + /* Go to interruptible sleep */ + msleep_interruptible(msec_to_wait); arg->ret = TEEC_SUCCESS; return; @@ -374,11 +371,11 @@ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param) shm = reg_pair_to_ptr(param->a1, param->a2); tee_shm_free(shm); break; - case OPTEE_SMC_RPC_FUNC_IRQ: + case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: /* - * An IRQ was raised while secure world was executing, - * since all IRQs are handled in Linux a dummy RPC is - * performed to let Linux take the IRQ through the normal + * A foreign interrupt was raised while secure world was + * executing, since they are handled in Linux a dummy RPC is + * performed to let Linux take the interrupt through the normal * vector. */ break; diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 5c60bf4423e6..58a5009eacc3 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -90,8 +90,13 @@ static int tee_ioctl_version(struct tee_context *ctx, struct tee_ioctl_version_data vers; ctx->teedev->desc->ops->get_version(ctx->teedev, &vers); + + if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED) + vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED; + if (copy_to_user(uvers, &vers, sizeof(vers))) return -EFAULT; + return 0; } diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index d356d7f025eb..4bc7956cefc4 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -80,7 +80,7 @@ static int tee_shm_op_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) size, vma->vm_page_prot); } -static struct dma_buf_ops tee_shm_dma_buf_ops = { +static const struct dma_buf_ops tee_shm_dma_buf_ops = { .map_dma_buf = tee_shm_op_map_dma_buf, .unmap_dma_buf = tee_shm_op_unmap_dma_buf, .release = tee_shm_op_release, diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index b5b5facb8747..07002df4f83a 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -342,7 +342,7 @@ config X86_PKG_TEMP_THERMAL config INTEL_SOC_DTS_IOSF_CORE tristate - depends on X86 + depends on X86 && PCI select IOSF_MBI help This is becoming a common feature for Intel SoCs to expose the additional @@ -352,7 +352,7 @@ config INTEL_SOC_DTS_IOSF_CORE config INTEL_SOC_DTS_THERMAL tristate "Intel SoCs DTS thermal driver" - depends on X86 + depends on X86 && PCI select INTEL_SOC_DTS_IOSF_CORE select THERMAL_WRITABLE_TRIPS help @@ -473,4 +473,12 @@ config ZX2967_THERMAL the primitive temperature sensor embedded in zx2967 SoCs. This sensor generates the real time die temperature. +config UNIPHIER_THERMAL + tristate "Socionext UniPhier thermal driver" + depends on ARCH_UNIPHIER || COMPILE_TEST + depends on THERMAL_OF && MFD_SYSCON + help + Enable this to plug in UniPhier on-chip PVT thermal driver into the + thermal framework. The driver supports CPU thermal zone temperature + reporting and a couple of trip points. endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 094d7039981c..8b79bca23536 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -59,3 +59,4 @@ obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o +obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o diff --git a/drivers/thermal/broadcom/bcm2835_thermal.c b/drivers/thermal/broadcom/bcm2835_thermal.c index e6863c841662..a4d6a0e2e993 100644 --- a/drivers/thermal/broadcom/bcm2835_thermal.c +++ b/drivers/thermal/broadcom/bcm2835_thermal.c @@ -145,7 +145,7 @@ static void bcm2835_thermal_debugfs(struct platform_device *pdev) debugfs_create_regset32("regset", 0444, data->debugfsdir, regset); } -static struct thermal_zone_of_device_ops bcm2835_thermal_ops = { +static const struct thermal_zone_of_device_ops bcm2835_thermal_ops = { .get_temp = bcm2835_thermal_get_temp, }; diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index 9c3ce341eb97..bd3572c41585 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -206,7 +206,7 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp) return 0; } -static struct thermal_zone_of_device_ops hisi_of_thermal_ops = { +static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = { .get_temp = hisi_thermal_get_temp, }; diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c index 51ceb80212a7..c719167e9f28 100644 --- a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c @@ -228,7 +228,7 @@ static void get_single_name(acpi_handle handle, char *name) struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER}; if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer))) - pr_warn("Failed get name from handle\n"); + pr_warn("Failed to get device name from acpi handle\n"); else { memcpy(name, buffer.pointer, ACPI_NAME_SIZE); kfree(buffer.pointer); diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.h b/drivers/thermal/int340x_thermal/acpi_thermal_rel.h index f00700bc9d79..65075b174329 100644 --- a/drivers/thermal/int340x_thermal/acpi_thermal_rel.h +++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.h @@ -34,10 +34,10 @@ struct trt { acpi_handle target; u64 influence; u64 sample_period; - u64 reverved1; - u64 reverved2; - u64 reverved3; - u64 reverved4; + u64 reserved1; + u64 reserved2; + u64 reserved3; + u64 reserved4; } __packed; #define ACPI_NR_ART_ELEMENTS 13 diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c index a9ec94ed7a42..8ee38f55c7f3 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -16,6 +16,8 @@ #include <linux/thermal.h> #include "acpi_thermal_rel.h" +#define INT3400_THERMAL_TABLE_CHANGED 0x83 + enum int3400_thermal_uuid { INT3400_THERMAL_PASSIVE_1, INT3400_THERMAL_ACTIVE, @@ -104,7 +106,7 @@ static struct attribute *uuid_attrs[] = { NULL }; -static struct attribute_group uuid_attribute_group = { +static const struct attribute_group uuid_attribute_group = { .attrs = uuid_attrs, .name = "uuids" }; @@ -185,6 +187,35 @@ static int int3400_thermal_run_osc(acpi_handle handle, return result; } +static void int3400_notify(acpi_handle handle, + u32 event, + void *data) +{ + struct int3400_thermal_priv *priv = data; + char *thermal_prop[5]; + + if (!priv) + return; + + switch (event) { + case INT3400_THERMAL_TABLE_CHANGED: + thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", + priv->thermal->type); + thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", + priv->thermal->temperature); + thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP="); + thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", + THERMAL_TABLE_CHANGED); + thermal_prop[4] = NULL; + kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE, + thermal_prop); + break; + default: + dev_err(&priv->adev->dev, "Unsupported event [0x%x]\n", event); + break; + } +} + static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, int *temp) { @@ -290,6 +321,12 @@ static int int3400_thermal_probe(struct platform_device *pdev) if (result) goto free_zone; + result = acpi_install_notify_handler( + priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify, + (void *)priv); + if (result) + goto free_zone; + return 0; free_zone: @@ -306,6 +343,10 @@ static int int3400_thermal_remove(struct platform_device *pdev) { struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); + acpi_remove_notify_handler( + priv->adev->handle, ACPI_DEVICE_NOTIFY, + int3400_notify); + if (!priv->rel_misc_dev_res) acpi_thermal_rel_misc_device_remove(priv->adev->handle); diff --git a/drivers/thermal/int340x_thermal/int3406_thermal.c b/drivers/thermal/int340x_thermal/int3406_thermal.c index 1891f34ab7fc..f69ab026ba24 100644 --- a/drivers/thermal/int340x_thermal/int3406_thermal.c +++ b/drivers/thermal/int340x_thermal/int3406_thermal.c @@ -21,39 +21,33 @@ struct int3406_thermal_data { int upper_limit; - int upper_limit_index; int lower_limit; - int lower_limit_index; acpi_handle handle; struct acpi_video_device_brightness *br; struct backlight_device *raw_bd; struct thermal_cooling_device *cooling_dev; }; -static int int3406_thermal_to_raw(int level, struct int3406_thermal_data *d) -{ - int max_level = d->br->levels[d->br->count - 1]; - int raw_max = d->raw_bd->props.max_brightness; - - return level * raw_max / max_level; -} - -static int int3406_thermal_to_acpi(int level, struct int3406_thermal_data *d) -{ - int raw_max = d->raw_bd->props.max_brightness; - int max_level = d->br->levels[d->br->count - 1]; - - return level * max_level / raw_max; -} +/* + * According to the ACPI spec, + * "Each brightness level is represented by a number between 0 and 100, + * and can be thought of as a percentage. For example, 50 can be 50% + * power consumption or 50% brightness, as defined by the OEM." + * + * As int3406 device uses this value to communicate with the native + * graphics driver, we make the assumption that it represents + * the percentage of brightness only + */ +#define ACPI_TO_RAW(v, d) (d->raw_bd->props.max_brightness * v / 100) +#define RAW_TO_ACPI(v, d) (v * 100 / d->raw_bd->props.max_brightness) static int int3406_thermal_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned long *state) { struct int3406_thermal_data *d = cooling_dev->devdata; - int index = d->lower_limit_index ? d->lower_limit_index : 2; - *state = d->br->count - 1 - index; + *state = d->upper_limit - d->lower_limit; return 0; } @@ -62,19 +56,15 @@ int3406_thermal_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state) { struct int3406_thermal_data *d = cooling_dev->devdata; - int level, raw_level; + int acpi_level, raw_level; - if (state > d->br->count - 3) + if (state > d->upper_limit - d->lower_limit) return -EINVAL; - state = d->br->count - 1 - state; - level = d->br->levels[state]; + acpi_level = d->br->levels[d->upper_limit - state]; - if ((d->upper_limit && level > d->upper_limit) || - (d->lower_limit && level < d->lower_limit)) - return -EINVAL; + raw_level = ACPI_TO_RAW(acpi_level, d); - raw_level = int3406_thermal_to_raw(level, d); return backlight_device_set_brightness(d->raw_bd, raw_level); } @@ -83,27 +73,22 @@ int3406_thermal_get_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long *state) { struct int3406_thermal_data *d = cooling_dev->devdata; - int raw_level, level, i; - int *levels = d->br->levels; + int acpi_level; + int index; - raw_level = d->raw_bd->props.brightness; - level = int3406_thermal_to_acpi(raw_level, d); + acpi_level = RAW_TO_ACPI(d->raw_bd->props.brightness, d); /* - * There is no 1:1 mapping between the firmware interface level with the - * raw interface level, we will have to find one that is close enough. + * There is no 1:1 mapping between the firmware interface level + * with the raw interface level, we will have to find one that is + * right above it. */ - for (i = 2; i < d->br->count; i++) { - if (level < levels[i]) { - if (i == 2) - break; - if ((level - levels[i - 1]) < (levels[i] - level)) - i--; + for (index = d->lower_limit; index < d->upper_limit; index++) { + if (acpi_level <= d->br->levels[index]) break; - } } - *state = d->br->count - 1 - i; + *state = d->upper_limit - index; return 0; } @@ -117,7 +102,7 @@ static int int3406_thermal_get_index(int *array, int nr, int value) { int i; - for (i = 0; i < nr; i++) { + for (i = 2; i < nr; i++) { if (array[i] == value) break; } @@ -128,27 +113,20 @@ static void int3406_thermal_get_limit(struct int3406_thermal_data *d) { acpi_status status; unsigned long long lower_limit, upper_limit; - int index; status = acpi_evaluate_integer(d->handle, "DDDL", NULL, &lower_limit); - if (ACPI_SUCCESS(status)) { - index = int3406_thermal_get_index(d->br->levels, d->br->count, - lower_limit); - if (index > 0) { - d->lower_limit = (int)lower_limit; - d->lower_limit_index = index; - } - } + if (ACPI_SUCCESS(status)) + d->lower_limit = int3406_thermal_get_index(d->br->levels, + d->br->count, lower_limit); status = acpi_evaluate_integer(d->handle, "DDPC", NULL, &upper_limit); - if (ACPI_SUCCESS(status)) { - index = int3406_thermal_get_index(d->br->levels, d->br->count, - upper_limit); - if (index > 0) { - d->upper_limit = (int)upper_limit; - d->upper_limit_index = index; - } - } + if (ACPI_SUCCESS(status)) + d->upper_limit = int3406_thermal_get_index(d->br->levels, + d->br->count, upper_limit); + + /* lower_limit and upper_limit should be always set */ + d->lower_limit = d->lower_limit > 0 ? d->lower_limit : 2; + d->upper_limit = d->upper_limit > 0 ? d->upper_limit : d->br->count - 1; } static void int3406_notify(acpi_handle handle, u32 event, void *data) diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c index ff3b36f339e3..f02341f7134d 100644 --- a/drivers/thermal/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c @@ -127,7 +127,7 @@ static struct attribute *power_limit_attrs[] = { NULL }; -static struct attribute_group power_limit_attribute_group = { +static const struct attribute_group power_limit_attribute_group = { .attrs = power_limit_attrs, .name = "power_limits" }; diff --git a/drivers/thermal/intel_pch_thermal.c b/drivers/thermal/intel_pch_thermal.c index 2b49e8d0fe9e..c60b1cfcc64e 100644 --- a/drivers/thermal/intel_pch_thermal.c +++ b/drivers/thermal/intel_pch_thermal.c @@ -49,7 +49,7 @@ #define WPT_TSGPEN 0x84 /* General Purpose Event Enables */ /* Wildcat Point-LP PCH Thermal Register bit definitions */ -#define WPT_TEMP_TSR 0x00ff /* Temp TS Reading */ +#define WPT_TEMP_TSR 0x01ff /* Temp TS Reading */ #define WPT_TSC_CPDE 0x01 /* Catastrophic Power-Down Enable */ #define WPT_TSS_TSDSS 0x10 /* Thermal Sensor Dynamic Shutdown Status */ #define WPT_TSS_GPES 0x08 /* GPE status */ @@ -125,7 +125,7 @@ static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips) *nr_trips = 0; /* Check if BIOS has already enabled thermal sensor */ - if (WPT_TSS_TSDSS & readb(ptd->hw_base + WPT_TSS)) { + if (WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL)) { ptd->bios_enabled = true; goto read_trips; } @@ -141,7 +141,7 @@ static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips) } writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL); - if (!(WPT_TSS_TSDSS & readb(ptd->hw_base + WPT_TSS))) { + if (!(WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL))) { dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n"); return -ENODEV; } @@ -174,9 +174,9 @@ read_trips: static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp) { - u8 wpt_temp; + u16 wpt_temp; - wpt_temp = WPT_TEMP_TSR & readl(ptd->hw_base + WPT_TEMP); + wpt_temp = WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP); /* Resolution of 1/2 degree C and an offset of -50C */ *temp = (wpt_temp * 1000 / 2 - 50000); @@ -387,7 +387,7 @@ static int intel_pch_thermal_resume(struct device *device) return ptd->ops->resume(ptd); } -static struct pci_device_id intel_pch_thermal_id[] = { +static const struct pci_device_id intel_pch_thermal_id[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1), .driver_data = board_hsw, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2), diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 7737f14846f9..1e61c09153c9 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -3,6 +3,7 @@ * Author: Hanyi Wu <hanyi.wu@mediatek.com> * Sascha Hauer <s.hauer@pengutronix.de> * Dawei Chien <dawei.chien@mediatek.com> + * Louis Yu <louis.yu@mediatek.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -111,9 +112,10 @@ /* * Layout of the fuses providing the calibration data - * These macros could be used for both MT8173 and MT2701. - * MT8173 has five sensors and need five VTS calibration data, - * and MT2701 has three sensors and need three VTS calibration data. + * These macros could be used for MT8173, MT2701, and MT2712. + * MT8173 has 5 sensors and needs 5 VTS calibration data. + * MT2701 has 3 sensors and needs 3 VTS calibration data. + * MT2712 has 4 sensors and needs 4 VTS calibration data. */ #define MT8173_CALIB_BUF0_VALID BIT(0) #define MT8173_CALIB_BUF1_ADC_GE(x) (((x) >> 22) & 0x3ff) @@ -124,6 +126,8 @@ #define MT8173_CALIB_BUF2_VTS_TSABB(x) (((x) >> 14) & 0x1ff) #define MT8173_CALIB_BUF0_DEGC_CALI(x) (((x) >> 1) & 0x3f) #define MT8173_CALIB_BUF0_O_SLOPE(x) (((x) >> 26) & 0x3f) +#define MT8173_CALIB_BUF0_O_SLOPE_SIGN(x) (((x) >> 7) & 0x1) +#define MT8173_CALIB_BUF1_ID(x) (((x) >> 9) & 0x1) /* MT2701 thermal sensors */ #define MT2701_TS1 0 @@ -136,11 +140,26 @@ /* The total number of temperature sensors in the MT2701 */ #define MT2701_NUM_SENSORS 3 -#define THERMAL_NAME "mtk-thermal" - /* The number of sensing points per bank */ #define MT2701_NUM_SENSORS_PER_ZONE 3 +/* MT2712 thermal sensors */ +#define MT2712_TS1 0 +#define MT2712_TS2 1 +#define MT2712_TS3 2 +#define MT2712_TS4 3 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT2712_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT2712 */ +#define MT2712_NUM_SENSORS 4 + +/* The number of sensing points per bank */ +#define MT2712_NUM_SENSORS_PER_ZONE 4 + +#define THERMAL_NAME "mtk-thermal" + struct mtk_thermal; struct thermal_bank_cfg { @@ -215,6 +234,21 @@ static const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = { static const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 }; +/* MT2712 thermal sensor data */ +static const int mt2712_bank_data[MT2712_NUM_SENSORS] = { + MT2712_TS1, MT2712_TS2, MT2712_TS3, MT2712_TS4 +}; + +static const int mt2712_msr[MT2712_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR3 +}; + +static const int mt2712_adcpnp[MT2712_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3 +}; + +static const int mt2712_mux_values[MT2712_NUM_SENSORS] = { 0, 1, 2, 3 }; + /** * The MT8173 thermal controller has four banks. Each bank can read up to * four temperature sensors simultaneously. The MT8173 has a total of 5 @@ -278,6 +312,31 @@ static const struct mtk_thermal_data mt2701_thermal_data = { }; /** + * The MT2712 thermal controller has one bank, which can read up to + * four temperature sensors simultaneously. The MT2712 has a total of 4 + * temperature sensors. + * + * The thermal core only gets the maximum temperature of this one bank, + * so the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data. + */ +static const struct mtk_thermal_data mt2712_thermal_data = { + .auxadc_channel = MT2712_TEMP_AUXADC_CHANNEL, + .num_banks = 1, + .num_sensors = MT2712_NUM_SENSORS, + .bank_data = { + { + .num_sensors = 4, + .sensors = mt2712_bank_data, + }, + }, + .msr = mt2712_msr, + .adcpnp = mt2712_adcpnp, + .sensor_mux_values = mt2712_mux_values, +}; + +/** * raw_to_mcelsius - convert a raw ADC value to mcelsius * @mt: The thermal controller * @raw: raw ADC value @@ -552,7 +611,11 @@ static int mtk_thermal_get_calibration_data(struct device *dev, mt->vts[MT8173_TS4] = MT8173_CALIB_BUF2_VTS_TS4(buf[2]); mt->vts[MT8173_TSABB] = MT8173_CALIB_BUF2_VTS_TSABB(buf[2]); mt->degc_cali = MT8173_CALIB_BUF0_DEGC_CALI(buf[0]); - mt->o_slope = MT8173_CALIB_BUF0_O_SLOPE(buf[0]); + if (MT8173_CALIB_BUF1_ID(buf[1]) & + MT8173_CALIB_BUF0_O_SLOPE_SIGN(buf[0])) + mt->o_slope = -MT8173_CALIB_BUF0_O_SLOPE(buf[0]); + else + mt->o_slope = MT8173_CALIB_BUF0_O_SLOPE(buf[0]); } else { dev_info(dev, "Device not calibrated, using default calibration values\n"); } @@ -571,6 +634,10 @@ static const struct of_device_id mtk_thermal_of_match[] = { { .compatible = "mediatek,mt2701-thermal", .data = (void *)&mt2701_thermal_data, + }, + { + .compatible = "mediatek,mt2712-thermal", + .data = (void *)&mt2712_thermal_data, }, { }, }; @@ -645,16 +712,16 @@ static int mtk_thermal_probe(struct platform_device *pdev) return -EINVAL; } + ret = device_reset(&pdev->dev); + if (ret) + return ret; + ret = clk_prepare_enable(mt->clk_auxadc); if (ret) { dev_err(&pdev->dev, "Can't enable auxadc clk: %d\n", ret); return ret; } - ret = device_reset(&pdev->dev); - if (ret) - goto err_disable_clk_auxadc; - ret = clk_prepare_enable(mt->clk_peri_therm); if (ret) { dev_err(&pdev->dev, "Can't enable peri clk: %d\n", ret); @@ -705,6 +772,7 @@ static struct platform_driver mtk_thermal_driver = { module_platform_driver(mtk_thermal_driver); +MODULE_AUTHOR("Louis Yu <louis.yu@mediatek.com>"); MODULE_AUTHOR("Dawei Chien <dawei.chien@mediatek.com>"); MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); MODULE_AUTHOR("Hanyi Wu <hanyi.wu@mediatek.com>"); diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 4362a69ac88d..c866cc165960 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -188,7 +188,7 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) tmu_write(data, TMR_DISABLE, &data->regs->tmr); } -static struct thermal_zone_of_device_ops tmu_tz_ops = { +static const struct thermal_zone_of_device_ops tmu_tz_ops = { .get_temp = tmu_get_temp, }; diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 37fcefd06d9f..203aca44a2bb 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -225,7 +225,7 @@ static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high) return 0; } -static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { +static const struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { .get_temp = rcar_gen3_thermal_get_temp, .set_trips = rcar_gen3_thermal_set_trips, }; diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 4c7796512453..206035139110 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -320,6 +320,44 @@ static const struct tsadc_table rk3288_code_table[] = { {0, 125000}, }; +static const struct tsadc_table rk3328_code_table[] = { + {0, -40000}, + {296, -40000}, + {304, -35000}, + {313, -30000}, + {331, -20000}, + {340, -15000}, + {349, -10000}, + {359, -5000}, + {368, 0}, + {378, 5000}, + {388, 10000}, + {398, 15000}, + {408, 20000}, + {418, 25000}, + {429, 30000}, + {440, 35000}, + {451, 40000}, + {462, 45000}, + {473, 50000}, + {485, 55000}, + {496, 60000}, + {508, 65000}, + {521, 70000}, + {533, 75000}, + {546, 80000}, + {559, 85000}, + {572, 90000}, + {586, 95000}, + {600, 100000}, + {614, 105000}, + {629, 110000}, + {644, 115000}, + {659, 120000}, + {675, 125000}, + {TSADCV2_DATA_MASK, 125000}, +}; + static const struct tsadc_table rk3368_code_table[] = { {0, -40000}, {106, -40000}, @@ -790,6 +828,29 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = { }, }; +static const struct rockchip_tsadc_chip rk3328_tsadc_data = { + .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ + .chn_num = 1, /* one channels for tsadc */ + + .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv2_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = { + .id = rk3328_code_table, + .length = ARRAY_SIZE(rk3328_code_table), + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + static const struct rockchip_tsadc_chip rk3366_tsadc_data = { .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ .chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */ @@ -875,6 +936,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = { .data = (void *)&rk3288_tsadc_data, }, { + .compatible = "rockchip,rk3328-tsadc", + .data = (void *)&rk3328_tsadc_data, + }, + { .compatible = "rockchip,rk3366-tsadc", .data = (void *)&rk3366_tsadc_data, }, diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 7b8ef09d2b3c..ed805c7c5ace 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -1286,7 +1286,7 @@ static int exynos_map_dt_data(struct platform_device *pdev) return 0; } -static struct thermal_zone_of_device_ops exynos_sensor_ops = { +static const struct thermal_zone_of_device_ops exynos_sensor_ops = { .get_temp = exynos_get_temp, .set_emul_temp = exynos_tmu_set_emulation, }; diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 5a51c740e372..2b1b0ba393a4 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -390,7 +390,7 @@ static void handle_critical_trips(struct thermal_zone_device *tz, if (trip_type == THERMAL_TRIP_CRITICAL) { dev_emerg(&tz->device, - "critical temperature reached(%d C),shutting down\n", + "critical temperature reached (%d C), shutting down\n", tz->temperature / 1000); mutex_lock(&poweroff_lock); if (!power_off_triggered) { @@ -836,11 +836,7 @@ static void thermal_release(struct device *dev) if (!strncmp(dev_name(dev), "thermal_zone", sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - kfree(tz->trip_hyst_attrs); - kfree(tz->trips_attribute_group.attrs); - kfree(tz->device.groups); + thermal_zone_destroy_device_groups(tz); kfree(tz); } else if (!strncmp(dev_name(dev), "cooling_device", sizeof("cooling_device") - 1)) { @@ -1213,10 +1209,8 @@ thermal_zone_device_register(const char *type, int trips, int mask, ida_init(&tz->ida); mutex_init(&tz->lock); result = ida_simple_get(&thermal_tz_ida, 0, 0, GFP_KERNEL); - if (result < 0) { - kfree(tz); - return ERR_PTR(result); - } + if (result < 0) + goto free_tz; tz->id = result; strlcpy(tz->type, type, sizeof(tz->type)); @@ -1232,18 +1226,15 @@ thermal_zone_device_register(const char *type, int trips, int mask, /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz, mask); if (result) - goto unregister; + goto remove_id; /* A new thermal zone needs to be updated anyway. */ atomic_set(&tz->need_update, 1); dev_set_name(&tz->device, "thermal_zone%d", tz->id); result = device_register(&tz->device); - if (result) { - ida_simple_remove(&thermal_tz_ida, tz->id); - kfree(tz); - return ERR_PTR(result); - } + if (result) + goto remove_device_groups; for (count = 0; count < trips; count++) { if (tz->ops->get_trip_type(tz, count, &trip_type)) @@ -1297,6 +1288,14 @@ unregister: ida_simple_remove(&thermal_tz_ida, tz->id); device_unregister(&tz->device); return ERR_PTR(result); + +remove_device_groups: + thermal_zone_destroy_device_groups(tz); +remove_id: + ida_simple_remove(&thermal_tz_ida, tz->id); +free_tz: + kfree(tz); + return ERR_PTR(result); } EXPORT_SYMBOL_GPL(thermal_zone_device_register); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 2412b3759e16..27e3b1df7360 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -71,6 +71,7 @@ int thermal_build_list_of_policies(char *buf); /* sysfs I/F */ int thermal_zone_create_device_groups(struct thermal_zone_device *, int); +void thermal_zone_destroy_device_groups(struct thermal_zone_device *); void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *); /* used only at binding time */ ssize_t diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index a694de907a26..fb80c96d8f73 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -605,6 +605,24 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) return 0; } +/** + * destroy_trip_attrs() - destroy attributes for trip points + * @tz: the thermal zone device + * + * helper function to free resources allocated by create_trip_attrs() + */ +static void destroy_trip_attrs(struct thermal_zone_device *tz) +{ + if (!tz) + return; + + kfree(tz->trip_type_attrs); + kfree(tz->trip_temp_attrs); + if (tz->ops->get_trip_hyst) + kfree(tz->trip_hyst_attrs); + kfree(tz->trips_attribute_group.attrs); +} + int thermal_zone_create_device_groups(struct thermal_zone_device *tz, int mask) { @@ -637,6 +655,17 @@ int thermal_zone_create_device_groups(struct thermal_zone_device *tz, return 0; } +void thermal_zone_destroy_device_groups(struct thermal_zone_device *tz) +{ + if (!tz) + return; + + if (tz->trips) + destroy_trip_attrs(tz); + + kfree(tz->device.groups); +} + /* sys I/F for cooling device */ static ssize_t thermal_cooling_device_type_show(struct device *dev, diff --git a/drivers/thermal/uniphier_thermal.c b/drivers/thermal/uniphier_thermal.c new file mode 100644 index 000000000000..95704732f760 --- /dev/null +++ b/drivers/thermal/uniphier_thermal.c @@ -0,0 +1,384 @@ +/** + * uniphier_thermal.c - Socionext UniPhier thermal driver + * + * Copyright 2014 Panasonic Corporation + * Copyright 2016-2017 Socionext Inc. + * All rights reserved. + * + * Author: + * Kunihiko Hayashi <hayashi.kunihiko@socionext.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/bitops.h> +#include <linux/interrupt.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/thermal.h> + +#include "thermal_core.h" + +/* + * block registers + * addresses are the offset from .block_base + */ +#define PVTCTLEN 0x0000 +#define PVTCTLEN_EN BIT(0) + +#define PVTCTLMODE 0x0004 +#define PVTCTLMODE_MASK 0xf +#define PVTCTLMODE_TEMPMON 0x5 + +#define EMONREPEAT 0x0040 +#define EMONREPEAT_ENDLESS BIT(24) +#define EMONREPEAT_PERIOD GENMASK(3, 0) +#define EMONREPEAT_PERIOD_1000000 0x9 + +/* + * common registers + * addresses are the offset from .map_base + */ +#define PVTCTLSEL 0x0900 +#define PVTCTLSEL_MASK GENMASK(2, 0) +#define PVTCTLSEL_MONITOR 0 + +#define SETALERT0 0x0910 +#define SETALERT1 0x0914 +#define SETALERT2 0x0918 +#define SETALERT_TEMP_OVF (GENMASK(7, 0) << 16) +#define SETALERT_TEMP_OVF_VALUE(val) (((val) & GENMASK(7, 0)) << 16) +#define SETALERT_EN BIT(0) + +#define PMALERTINTCTL 0x0920 +#define PMALERTINTCTL_CLR(ch) BIT(4 * (ch) + 2) +#define PMALERTINTCTL_SET(ch) BIT(4 * (ch) + 1) +#define PMALERTINTCTL_EN(ch) BIT(4 * (ch) + 0) +#define PMALERTINTCTL_MASK (GENMASK(10, 8) | GENMASK(6, 4) | \ + GENMASK(2, 0)) + +#define TMOD 0x0928 +#define TMOD_WIDTH 9 + +#define TMODCOEF 0x0e5c + +#define TMODSETUP0_EN BIT(30) +#define TMODSETUP0_VAL(val) (((val) & GENMASK(13, 0)) << 16) +#define TMODSETUP1_EN BIT(15) +#define TMODSETUP1_VAL(val) ((val) & GENMASK(14, 0)) + +/* SoC critical temperature */ +#define CRITICAL_TEMP_LIMIT (120 * 1000) + +/* Max # of alert channels */ +#define ALERT_CH_NUM 3 + +/* SoC specific thermal sensor data */ +struct uniphier_tm_soc_data { + u32 map_base; + u32 block_base; + u32 tmod_setup_addr; +}; + +struct uniphier_tm_dev { + struct regmap *regmap; + struct device *dev; + bool alert_en[ALERT_CH_NUM]; + struct thermal_zone_device *tz_dev; + const struct uniphier_tm_soc_data *data; +}; + +static int uniphier_tm_initialize_sensor(struct uniphier_tm_dev *tdev) +{ + struct regmap *map = tdev->regmap; + u32 val; + u32 tmod_calib[2]; + int ret; + + /* stop PVT */ + regmap_write_bits(map, tdev->data->block_base + PVTCTLEN, + PVTCTLEN_EN, 0); + + /* + * Since SoC has a calibrated value that was set in advance, + * TMODCOEF shows non-zero and PVT refers the value internally. + * + * If TMODCOEF shows zero, the boards don't have the calibrated + * value, and the driver has to set default value from DT. + */ + ret = regmap_read(map, tdev->data->map_base + TMODCOEF, &val); + if (ret) + return ret; + if (!val) { + /* look for the default values in DT */ + ret = of_property_read_u32_array(tdev->dev->of_node, + "socionext,tmod-calibration", + tmod_calib, + ARRAY_SIZE(tmod_calib)); + if (ret) + return ret; + + regmap_write(map, tdev->data->tmod_setup_addr, + TMODSETUP0_EN | TMODSETUP0_VAL(tmod_calib[0]) | + TMODSETUP1_EN | TMODSETUP1_VAL(tmod_calib[1])); + } + + /* select temperature mode */ + regmap_write_bits(map, tdev->data->block_base + PVTCTLMODE, + PVTCTLMODE_MASK, PVTCTLMODE_TEMPMON); + + /* set monitoring period */ + regmap_write_bits(map, tdev->data->block_base + EMONREPEAT, + EMONREPEAT_ENDLESS | EMONREPEAT_PERIOD, + EMONREPEAT_ENDLESS | EMONREPEAT_PERIOD_1000000); + + /* set monitor mode */ + regmap_write_bits(map, tdev->data->map_base + PVTCTLSEL, + PVTCTLSEL_MASK, PVTCTLSEL_MONITOR); + + return 0; +} + +static void uniphier_tm_set_alert(struct uniphier_tm_dev *tdev, u32 ch, + u32 temp) +{ + struct regmap *map = tdev->regmap; + + /* set alert temperature */ + regmap_write_bits(map, tdev->data->map_base + SETALERT0 + (ch << 2), + SETALERT_EN | SETALERT_TEMP_OVF, + SETALERT_EN | + SETALERT_TEMP_OVF_VALUE(temp / 1000)); +} + +static void uniphier_tm_enable_sensor(struct uniphier_tm_dev *tdev) +{ + struct regmap *map = tdev->regmap; + int i; + u32 bits = 0; + + for (i = 0; i < ALERT_CH_NUM; i++) + if (tdev->alert_en[i]) + bits |= PMALERTINTCTL_EN(i); + + /* enable alert interrupt */ + regmap_write_bits(map, tdev->data->map_base + PMALERTINTCTL, + PMALERTINTCTL_MASK, bits); + + /* start PVT */ + regmap_write_bits(map, tdev->data->block_base + PVTCTLEN, + PVTCTLEN_EN, PVTCTLEN_EN); + + usleep_range(700, 1500); /* The spec note says at least 700us */ +} + +static void uniphier_tm_disable_sensor(struct uniphier_tm_dev *tdev) +{ + struct regmap *map = tdev->regmap; + + /* disable alert interrupt */ + regmap_write_bits(map, tdev->data->map_base + PMALERTINTCTL, + PMALERTINTCTL_MASK, 0); + + /* stop PVT */ + regmap_write_bits(map, tdev->data->block_base + PVTCTLEN, + PVTCTLEN_EN, 0); + + usleep_range(1000, 2000); /* The spec note says at least 1ms */ +} + +static int uniphier_tm_get_temp(void *data, int *out_temp) +{ + struct uniphier_tm_dev *tdev = data; + struct regmap *map = tdev->regmap; + int ret; + u32 temp; + + ret = regmap_read(map, tdev->data->map_base + TMOD, &temp); + if (ret) + return ret; + + /* MSB of the TMOD field is a sign bit */ + *out_temp = sign_extend32(temp, TMOD_WIDTH - 1) * 1000; + + return 0; +} + +static const struct thermal_zone_of_device_ops uniphier_of_thermal_ops = { + .get_temp = uniphier_tm_get_temp, +}; + +static void uniphier_tm_irq_clear(struct uniphier_tm_dev *tdev) +{ + u32 mask = 0, bits = 0; + int i; + + for (i = 0; i < ALERT_CH_NUM; i++) { + mask |= (PMALERTINTCTL_CLR(i) | PMALERTINTCTL_SET(i)); + bits |= PMALERTINTCTL_CLR(i); + } + + /* clear alert interrupt */ + regmap_write_bits(tdev->regmap, + tdev->data->map_base + PMALERTINTCTL, mask, bits); +} + +static irqreturn_t uniphier_tm_alarm_irq(int irq, void *_tdev) +{ + struct uniphier_tm_dev *tdev = _tdev; + + disable_irq_nosync(irq); + uniphier_tm_irq_clear(tdev); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t uniphier_tm_alarm_irq_thread(int irq, void *_tdev) +{ + struct uniphier_tm_dev *tdev = _tdev; + + thermal_zone_device_update(tdev->tz_dev, THERMAL_EVENT_UNSPECIFIED); + + return IRQ_HANDLED; +} + +static int uniphier_tm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *regmap; + struct device_node *parent; + struct uniphier_tm_dev *tdev; + const struct thermal_trip *trips; + int i, ret, irq, ntrips, crit_temp = INT_MAX; + + tdev = devm_kzalloc(dev, sizeof(*tdev), GFP_KERNEL); + if (!tdev) + return -ENOMEM; + tdev->dev = dev; + + tdev->data = of_device_get_match_data(dev); + if (WARN_ON(!tdev->data)) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + /* get regmap from syscon node */ + parent = of_get_parent(dev->of_node); /* parent should be syscon node */ + regmap = syscon_node_to_regmap(parent); + of_node_put(parent); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to get regmap (error %ld)\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + tdev->regmap = regmap; + + ret = uniphier_tm_initialize_sensor(tdev); + if (ret) { + dev_err(dev, "failed to initialize sensor\n"); + return ret; + } + + ret = devm_request_threaded_irq(dev, irq, uniphier_tm_alarm_irq, + uniphier_tm_alarm_irq_thread, + 0, "thermal", tdev); + if (ret) + return ret; + + platform_set_drvdata(pdev, tdev); + + tdev->tz_dev = devm_thermal_zone_of_sensor_register(dev, 0, tdev, + &uniphier_of_thermal_ops); + if (IS_ERR(tdev->tz_dev)) { + dev_err(dev, "failed to register sensor device\n"); + return PTR_ERR(tdev->tz_dev); + } + + /* get trip points */ + trips = of_thermal_get_trip_points(tdev->tz_dev); + ntrips = of_thermal_get_ntrips(tdev->tz_dev); + if (ntrips > ALERT_CH_NUM) { + dev_err(dev, "thermal zone has too many trips\n"); + return -E2BIG; + } + + /* set alert temperatures */ + for (i = 0; i < ntrips; i++) { + if (trips[i].type == THERMAL_TRIP_CRITICAL && + trips[i].temperature < crit_temp) + crit_temp = trips[i].temperature; + uniphier_tm_set_alert(tdev, i, trips[i].temperature); + tdev->alert_en[i] = true; + } + if (crit_temp > CRITICAL_TEMP_LIMIT) { + dev_err(dev, "critical trip is over limit(>%d), or not set\n", + CRITICAL_TEMP_LIMIT); + return -EINVAL; + } + + uniphier_tm_enable_sensor(tdev); + + return 0; +} + +static int uniphier_tm_remove(struct platform_device *pdev) +{ + struct uniphier_tm_dev *tdev = platform_get_drvdata(pdev); + + /* disable sensor */ + uniphier_tm_disable_sensor(tdev); + + return 0; +} + +static const struct uniphier_tm_soc_data uniphier_pxs2_tm_data = { + .map_base = 0xe000, + .block_base = 0xe000, + .tmod_setup_addr = 0xe904, +}; + +static const struct uniphier_tm_soc_data uniphier_ld20_tm_data = { + .map_base = 0xe000, + .block_base = 0xe800, + .tmod_setup_addr = 0xe938, +}; + +static const struct of_device_id uniphier_tm_dt_ids[] = { + { + .compatible = "socionext,uniphier-pxs2-thermal", + .data = &uniphier_pxs2_tm_data, + }, + { + .compatible = "socionext,uniphier-ld20-thermal", + .data = &uniphier_ld20_tm_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, uniphier_tm_dt_ids); + +static struct platform_driver uniphier_tm_driver = { + .probe = uniphier_tm_probe, + .remove = uniphier_tm_remove, + .driver = { + .name = "uniphier-thermal", + .of_match_table = uniphier_tm_dt_ids, + }, +}; +module_platform_driver(uniphier_tm_driver); + +MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); +MODULE_DESCRIPTION("UniPhier thermal driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/zx2967_thermal.c b/drivers/thermal/zx2967_thermal.c index a5670ad2cfc8..6acce0bce7c0 100644 --- a/drivers/thermal/zx2967_thermal.c +++ b/drivers/thermal/zx2967_thermal.c @@ -111,7 +111,7 @@ unlock: return ret; } -static struct thermal_zone_of_device_ops zx2967_of_thermal_ops = { +static const struct thermal_zone_of_device_ops zx2967_of_thermal_ops = { .get_temp = zx2967_thermal_get_temp, }; diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 873e0ba89737..cc2b4d9433ed 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -458,4 +458,9 @@ config MIPS_EJTAG_FDC_KGDB_CHAN help FDC channel number to use for KGDB. +config VCC + tristate "Sun Virtual Console Concentrator" + depends on SUN_LDOMS + help + Support for Sun logical domain consoles. endif # TTY diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 8689279afdf1..16330a819685 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -33,5 +33,6 @@ obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_DA_TTY) += metag_da.o obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o +obj-$(CONFIG_VCC) += vcc.o obj-y += ipwireless/ diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index ae8cfc81ffc5..d9123f995705 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -371,7 +371,7 @@ static const struct file_operations port_regs_ops = { }; #endif /* CONFIG_DEBUG_FS */ -static struct dmi_system_id pch_uart_dmi_table[] = { +static const struct dmi_system_id pch_uart_dmi_table[] = { { .ident = "CM-iTC", { diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c new file mode 100644 index 000000000000..ef01d24858cd --- /dev/null +++ b/drivers/tty/vcc.c @@ -0,0 +1,1155 @@ +/* vcc.c: sun4v virtual channel concentrator + * + * Copyright (C) 2017 Oracle. All rights reserved. + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <asm/vio.h> +#include <asm/ldc.h> + +#define DRV_MODULE_NAME "vcc" +#define DRV_MODULE_VERSION "1.1" +#define DRV_MODULE_RELDATE "July 1, 2017" + +static char version[] = + DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")"; + +MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_MODULE_VERSION); + +struct vcc_port { + struct vio_driver_state vio; + + spinlock_t lock; + char *domain; + struct tty_struct *tty; /* only populated while dev is open */ + unsigned long index; /* index into the vcc_table */ + + u64 refcnt; + bool excl_locked; + + bool removed; + + /* This buffer is required to support the tty write_room interface + * and guarantee that any characters that the driver accepts will + * be eventually sent, either immediately or later. + */ + int chars_in_buffer; + struct vio_vcc buffer; + + struct timer_list rx_timer; + struct timer_list tx_timer; +}; + +/* Microseconds that thread will delay waiting for a vcc port ref */ +#define VCC_REF_DELAY 100 + +#define VCC_MAX_PORTS 1024 +#define VCC_MINOR_START 0 /* must be zero */ +#define VCC_BUFF_LEN VIO_VCC_MTU_SIZE + +#define VCC_CTL_BREAK -1 +#define VCC_CTL_HUP -2 + +static const char vcc_driver_name[] = "vcc"; +static const char vcc_device_node[] = "vcc"; +static struct tty_driver *vcc_tty_driver; + +static struct vcc_port *vcc_table[VCC_MAX_PORTS]; +static DEFINE_SPINLOCK(vcc_table_lock); + +int vcc_dbg; +int vcc_dbg_ldc; +int vcc_dbg_vio; + +module_param(vcc_dbg, uint, 0664); +module_param(vcc_dbg_ldc, uint, 0664); +module_param(vcc_dbg_vio, uint, 0664); + +#define VCC_DBG_DRV 0x1 +#define VCC_DBG_LDC 0x2 +#define VCC_DBG_PKT 0x4 + +#define vccdbg(f, a...) \ + do { \ + if (vcc_dbg & VCC_DBG_DRV) \ + pr_info(f, ## a); \ + } while (0) \ + +#define vccdbgl(l) \ + do { \ + if (vcc_dbg & VCC_DBG_LDC) \ + ldc_print(l); \ + } while (0) \ + +#define vccdbgp(pkt) \ + do { \ + if (vcc_dbg & VCC_DBG_PKT) { \ + int i; \ + for (i = 0; i < pkt.tag.stype; i++) \ + pr_info("[%c]", pkt.data[i]); \ + } \ + } while (0) \ + +/* Note: Be careful when adding flags to this line discipline. Don't + * add anything that will cause echoing or we'll go into recursive + * loop echoing chars back and forth with the console drivers. + */ +static const struct ktermios vcc_tty_termios = { + .c_iflag = IGNBRK | IGNPAR, + .c_oflag = OPOST, + .c_cflag = B38400 | CS8 | CREAD | HUPCL, + .c_cc = INIT_C_CC, + .c_ispeed = 38400, + .c_ospeed = 38400 +}; + +/** + * vcc_table_add() - Add VCC port to the VCC table + * @port: pointer to the VCC port + * + * Return: index of the port in the VCC table on success, + * -1 on failure + */ +static int vcc_table_add(struct vcc_port *port) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&vcc_table_lock, flags); + for (i = VCC_MINOR_START; i < VCC_MAX_PORTS; i++) { + if (!vcc_table[i]) { + vcc_table[i] = port; + break; + } + } + spin_unlock_irqrestore(&vcc_table_lock, flags); + + if (i < VCC_MAX_PORTS) + return i; + else + return -1; +} + +/** + * vcc_table_remove() - Removes a VCC port from the VCC table + * @index: Index into the VCC table + */ +static void vcc_table_remove(unsigned long index) +{ + unsigned long flags; + + if (WARN_ON(index >= VCC_MAX_PORTS)) + return; + + spin_lock_irqsave(&vcc_table_lock, flags); + vcc_table[index] = NULL; + spin_unlock_irqrestore(&vcc_table_lock, flags); +} + +/** + * vcc_get() - Gets a reference to VCC port + * @index: Index into the VCC table + * @excl: Indicates if an exclusive access is requested + * + * Return: reference to the VCC port, if found + * NULL, if port not found + */ +static struct vcc_port *vcc_get(unsigned long index, bool excl) +{ + struct vcc_port *port; + unsigned long flags; + +try_again: + spin_lock_irqsave(&vcc_table_lock, flags); + + port = vcc_table[index]; + if (!port) { + spin_unlock_irqrestore(&vcc_table_lock, flags); + return NULL; + } + + if (!excl) { + if (port->excl_locked) { + spin_unlock_irqrestore(&vcc_table_lock, flags); + udelay(VCC_REF_DELAY); + goto try_again; + } + port->refcnt++; + spin_unlock_irqrestore(&vcc_table_lock, flags); + return port; + } + + if (port->refcnt) { + spin_unlock_irqrestore(&vcc_table_lock, flags); + /* Threads wanting exclusive access will wait half the time, + * probably giving them higher priority in the case of + * multiple waiters. + */ + udelay(VCC_REF_DELAY/2); + goto try_again; + } + + port->refcnt++; + port->excl_locked = true; + spin_unlock_irqrestore(&vcc_table_lock, flags); + + return port; +} + +/** + * vcc_put() - Returns a reference to VCC port + * @port: pointer to VCC port + * @excl: Indicates if the returned reference is an exclusive reference + * + * Note: It's the caller's responsibility to ensure the correct value + * for the excl flag + */ +static void vcc_put(struct vcc_port *port, bool excl) +{ + unsigned long flags; + + if (!port) + return; + + spin_lock_irqsave(&vcc_table_lock, flags); + + /* check if caller attempted to put with the wrong flags */ + if (WARN_ON((excl && !port->excl_locked) || + (!excl && port->excl_locked))) + goto done; + + port->refcnt--; + + if (excl) + port->excl_locked = false; + +done: + spin_unlock_irqrestore(&vcc_table_lock, flags); +} + +/** + * vcc_get_ne() - Get a non-exclusive reference to VCC port + * @index: Index into the VCC table + * + * Gets a non-exclusive reference to VCC port, if it's not removed + * + * Return: pointer to the VCC port, if found + * NULL, if port not found + */ +static struct vcc_port *vcc_get_ne(unsigned long index) +{ + struct vcc_port *port; + + port = vcc_get(index, false); + + if (port && port->removed) { + vcc_put(port, false); + return NULL; + } + + return port; +} + +static void vcc_kick_rx(struct vcc_port *port) +{ + struct vio_driver_state *vio = &port->vio; + + assert_spin_locked(&port->lock); + + if (!timer_pending(&port->rx_timer) && !port->removed) { + disable_irq_nosync(vio->vdev->rx_irq); + port->rx_timer.expires = (jiffies + 1); + add_timer(&port->rx_timer); + } +} + +static void vcc_kick_tx(struct vcc_port *port) +{ + assert_spin_locked(&port->lock); + + if (!timer_pending(&port->tx_timer) && !port->removed) { + port->tx_timer.expires = (jiffies + 1); + add_timer(&port->tx_timer); + } +} + +static int vcc_rx_check(struct tty_struct *tty, int size) +{ + if (WARN_ON(!tty || !tty->port)) + return 1; + + /* tty_buffer_request_room won't sleep because it uses + * GFP_ATOMIC flag to allocate buffer + */ + if (test_bit(TTY_THROTTLED, &tty->flags) || + (tty_buffer_request_room(tty->port, VCC_BUFF_LEN) < VCC_BUFF_LEN)) + return 0; + + return 1; +} + +static int vcc_rx(struct tty_struct *tty, char *buf, int size) +{ + int len = 0; + + if (WARN_ON(!tty || !tty->port)) + return len; + + len = tty_insert_flip_string(tty->port, buf, size); + if (len) + tty_flip_buffer_push(tty->port); + + return len; +} + +static int vcc_ldc_read(struct vcc_port *port) +{ + struct vio_driver_state *vio = &port->vio; + struct tty_struct *tty; + struct vio_vcc pkt; + int rv = 0; + + tty = port->tty; + if (!tty) { + rv = ldc_rx_reset(vio->lp); + vccdbg("VCC: reset rx q: rv=%d\n", rv); + goto done; + } + + /* Read as long as LDC has incoming data. */ + while (1) { + if (!vcc_rx_check(tty, VIO_VCC_MTU_SIZE)) { + vcc_kick_rx(port); + break; + } + + vccdbgl(vio->lp); + + rv = ldc_read(vio->lp, &pkt, sizeof(pkt)); + if (rv <= 0) + break; + + vccdbg("VCC: ldc_read()=%d\n", rv); + vccdbg("TAG [%02x:%02x:%04x:%08x]\n", + pkt.tag.type, pkt.tag.stype, + pkt.tag.stype_env, pkt.tag.sid); + + if (pkt.tag.type == VIO_TYPE_DATA) { + vccdbgp(pkt); + /* vcc_rx_check ensures memory availability */ + vcc_rx(tty, pkt.data, pkt.tag.stype); + } else { + pr_err("VCC: unknown msg [%02x:%02x:%04x:%08x]\n", + pkt.tag.type, pkt.tag.stype, + pkt.tag.stype_env, pkt.tag.sid); + rv = -ECONNRESET; + break; + } + + WARN_ON(rv != LDC_PACKET_SIZE); + } + +done: + return rv; +} + +static void vcc_rx_timer(unsigned long index) +{ + struct vio_driver_state *vio; + struct vcc_port *port; + unsigned long flags; + int rv; + + port = vcc_get_ne(index); + if (!port) + return; + + spin_lock_irqsave(&port->lock, flags); + port->rx_timer.expires = 0; + + vio = &port->vio; + + enable_irq(vio->vdev->rx_irq); + + if (!port->tty || port->removed) + goto done; + + rv = vcc_ldc_read(port); + if (rv == -ECONNRESET) + vio_conn_reset(vio); + +done: + spin_unlock_irqrestore(&port->lock, flags); + vcc_put(port, false); +} + +static void vcc_tx_timer(unsigned long index) +{ + struct vcc_port *port; + struct vio_vcc *pkt; + unsigned long flags; + int tosend = 0; + int rv; + + port = vcc_get_ne(index); + if (!port) + return; + + spin_lock_irqsave(&port->lock, flags); + port->tx_timer.expires = 0; + + if (!port->tty || port->removed) + goto done; + + tosend = min(VCC_BUFF_LEN, port->chars_in_buffer); + if (!tosend) + goto done; + + pkt = &port->buffer; + pkt->tag.type = VIO_TYPE_DATA; + pkt->tag.stype = tosend; + vccdbgl(port->vio.lp); + + rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); + WARN_ON(!rv); + + if (rv < 0) { + vccdbg("VCC: ldc_write()=%d\n", rv); + vcc_kick_tx(port); + } else { + struct tty_struct *tty = port->tty; + + port->chars_in_buffer = 0; + if (tty) + tty_wakeup(tty); + } + +done: + spin_unlock_irqrestore(&port->lock, flags); + vcc_put(port, false); +} + +/** + * vcc_event() - LDC event processing engine + * @arg: VCC private data + * @event: LDC event + * + * Handles LDC events for VCC + */ +static void vcc_event(void *arg, int event) +{ + struct vio_driver_state *vio; + struct vcc_port *port; + unsigned long flags; + int rv; + + port = arg; + vio = &port->vio; + + spin_lock_irqsave(&port->lock, flags); + + switch (event) { + case LDC_EVENT_RESET: + case LDC_EVENT_UP: + vio_link_state_change(vio, event); + break; + + case LDC_EVENT_DATA_READY: + rv = vcc_ldc_read(port); + if (rv == -ECONNRESET) + vio_conn_reset(vio); + break; + + default: + pr_err("VCC: unexpected LDC event(%d)\n", event); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +static struct ldc_channel_config vcc_ldc_cfg = { + .event = vcc_event, + .mtu = VIO_VCC_MTU_SIZE, + .mode = LDC_MODE_RAW, + .debug = 0, +}; + +/* Ordered from largest major to lowest */ +static struct vio_version vcc_versions[] = { + { .major = 1, .minor = 0 }, +}; + +static struct tty_port_operations vcc_port_ops = { 0 }; + +static ssize_t vcc_sysfs_domain_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct vcc_port *port; + int rv; + + port = dev_get_drvdata(dev); + if (!port) + return -ENODEV; + + rv = scnprintf(buf, PAGE_SIZE, "%s\n", port->domain); + + return rv; +} + +static int vcc_send_ctl(struct vcc_port *port, int ctl) +{ + struct vio_vcc pkt; + int rv; + + pkt.tag.type = VIO_TYPE_CTRL; + pkt.tag.sid = ctl; + pkt.tag.stype = 0; + + rv = ldc_write(port->vio.lp, &pkt, sizeof(pkt.tag)); + WARN_ON(!rv); + vccdbg("VCC: ldc_write(%ld)=%d\n", sizeof(pkt.tag), rv); + + return rv; +} + +static ssize_t vcc_sysfs_break_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vcc_port *port; + unsigned long flags; + int rv = count; + int brk; + + port = dev_get_drvdata(dev); + if (!port) + return -ENODEV; + + spin_lock_irqsave(&port->lock, flags); + + if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) + rv = -EINVAL; + else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) + vcc_kick_tx(port); + + spin_unlock_irqrestore(&port->lock, flags); + + return rv; +} + +static DEVICE_ATTR(domain, 0400, vcc_sysfs_domain_show, NULL); +static DEVICE_ATTR(break, 0200, NULL, vcc_sysfs_break_store); + +static struct attribute *vcc_sysfs_entries[] = { + &dev_attr_domain.attr, + &dev_attr_break.attr, + NULL +}; + +static struct attribute_group vcc_attribute_group = { + .name = NULL, + .attrs = vcc_sysfs_entries, +}; + +/** + * vcc_probe() - Initialize VCC port + * @vdev: Pointer to VIO device of the new VCC port + * @id: VIO device ID + * + * Initializes a VCC port to receive serial console data from + * the guest domain. Sets up a TTY end point on the control + * domain. Sets up VIO/LDC link between the guest & control + * domain endpoints. + * + * Return: status of the probe + */ +static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) +{ + struct mdesc_handle *hp; + struct vcc_port *port; + struct device *dev; + const char *domain; + char *name; + u64 node; + int rv; + + vccdbg("VCC: name=%s\n", dev_name(&vdev->dev)); + + if (!vcc_tty_driver) { + pr_err("VCC: TTY driver not registered\n"); + return -ENODEV; + } + + port = kzalloc(sizeof(struct vcc_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + name = kstrdup(dev_name(&vdev->dev), GFP_KERNEL); + + rv = vio_driver_init(&port->vio, vdev, VDEV_CONSOLE_CON, vcc_versions, + ARRAY_SIZE(vcc_versions), NULL, name); + if (rv) + goto free_port; + + port->vio.debug = vcc_dbg_vio; + vcc_ldc_cfg.debug = vcc_dbg_ldc; + + rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port); + if (rv) + goto free_port; + + spin_lock_init(&port->lock); + + port->index = vcc_table_add(port); + if (port->index == -1) { + pr_err("VCC: no more TTY indices left for allocation\n"); + goto free_ldc; + } + + /* Register the device using VCC table index as TTY index */ + dev = tty_register_device(vcc_tty_driver, port->index, &vdev->dev); + if (IS_ERR(dev)) { + rv = PTR_ERR(dev); + goto free_table; + } + + hp = mdesc_grab(); + + node = vio_vdev_node(hp, vdev); + if (node == MDESC_NODE_NULL) { + rv = -ENXIO; + mdesc_release(hp); + goto unreg_tty; + } + + domain = mdesc_get_property(hp, node, "vcc-domain-name", NULL); + if (!domain) { + rv = -ENXIO; + mdesc_release(hp); + goto unreg_tty; + } + port->domain = kstrdup(domain, GFP_KERNEL); + + mdesc_release(hp); + + rv = sysfs_create_group(&vdev->dev.kobj, &vcc_attribute_group); + if (rv) + goto free_domain; + + init_timer(&port->rx_timer); + port->rx_timer.function = vcc_rx_timer; + port->rx_timer.data = port->index; + + init_timer(&port->tx_timer); + port->tx_timer.function = vcc_tx_timer; + port->tx_timer.data = port->index; + + dev_set_drvdata(&vdev->dev, port); + + /* It's possible to receive IRQs in the middle of vio_port_up. Disable + * IRQs until the port is up. + */ + disable_irq_nosync(vdev->rx_irq); + vio_port_up(&port->vio); + enable_irq(vdev->rx_irq); + + return 0; + +free_domain: + kfree(port->domain); +unreg_tty: + tty_unregister_device(vcc_tty_driver, port->index); +free_table: + vcc_table_remove(port->index); +free_ldc: + vio_ldc_free(&port->vio); +free_port: + kfree(name); + kfree(port); + + return rv; +} + +/** + * vcc_remove() - Terminate a VCC port + * @vdev: Pointer to VIO device of the VCC port + * + * Terminates a VCC port. Sets up the teardown of TTY and + * VIO/LDC link between guest and primary domains. + * + * Return: status of removal + */ +static int vcc_remove(struct vio_dev *vdev) +{ + struct vcc_port *port = dev_get_drvdata(&vdev->dev); + + if (!port) + return -ENODEV; + + del_timer_sync(&port->rx_timer); + del_timer_sync(&port->tx_timer); + + /* If there's a process with the device open, do a synchronous + * hangup of the TTY. This *may* cause the process to call close + * asynchronously, but it's not guaranteed. + */ + if (port->tty) + tty_vhangup(port->tty); + + /* Get exclusive reference to VCC, ensures that there are no other + * clients to this port + */ + port = vcc_get(port->index, true); + + if (WARN_ON(!port)) + return -ENODEV; + + tty_unregister_device(vcc_tty_driver, port->index); + + del_timer_sync(&port->vio.timer); + vio_ldc_free(&port->vio); + sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); + dev_set_drvdata(&vdev->dev, NULL); + if (port->tty) { + port->removed = true; + vcc_put(port, true); + } else { + vcc_table_remove(port->index); + + kfree(port->vio.name); + kfree(port->domain); + kfree(port); + } + + return 0; +} + +static const struct vio_device_id vcc_match[] = { + { + .type = "vcc-port", + }, + {}, +}; +MODULE_DEVICE_TABLE(vio, vcc_match); + +static struct vio_driver vcc_driver = { + .id_table = vcc_match, + .probe = vcc_probe, + .remove = vcc_remove, + .name = "vcc", +}; + +static int vcc_open(struct tty_struct *tty, struct file *vcc_file) +{ + struct vcc_port *port; + + if (unlikely(!tty)) { + pr_err("VCC: open: Invalid TTY handle\n"); + return -ENXIO; + } + + if (tty->count > 1) + return -EBUSY; + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: open: Failed to find VCC port\n"); + return -ENODEV; + } + + if (unlikely(!port->vio.lp)) { + pr_err("VCC: open: LDC channel not configured\n"); + vcc_put(port, false); + return -EPIPE; + } + vccdbgl(port->vio.lp); + + vcc_put(port, false); + + if (unlikely(!tty->port)) { + pr_err("VCC: open: TTY port not found\n"); + return -ENXIO; + } + + if (unlikely(!tty->port->ops)) { + pr_err("VCC: open: TTY ops not defined\n"); + return -ENXIO; + } + + return tty_port_open(tty->port, tty, vcc_file); +} + +static void vcc_close(struct tty_struct *tty, struct file *vcc_file) +{ + if (unlikely(!tty)) { + pr_err("VCC: close: Invalid TTY handle\n"); + return; + } + + if (unlikely(tty->count > 1)) + return; + + if (unlikely(!tty->port)) { + pr_err("VCC: close: TTY port not found\n"); + return; + } + + tty_port_close(tty->port, tty, vcc_file); +} + +static void vcc_ldc_hup(struct vcc_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + if (vcc_send_ctl(port, VCC_CTL_HUP) < 0) + vcc_kick_tx(port); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void vcc_hangup(struct tty_struct *tty) +{ + struct vcc_port *port; + + if (unlikely(!tty)) { + pr_err("VCC: hangup: Invalid TTY handle\n"); + return; + } + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: hangup: Failed to find VCC port\n"); + return; + } + + if (unlikely(!tty->port)) { + pr_err("VCC: hangup: TTY port not found\n"); + vcc_put(port, false); + return; + } + + vcc_ldc_hup(port); + + vcc_put(port, false); + + tty_port_hangup(tty->port); +} + +static int vcc_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + struct vcc_port *port; + struct vio_vcc *pkt; + unsigned long flags; + int total_sent = 0; + int tosend = 0; + int rv = -EINVAL; + + if (unlikely(!tty)) { + pr_err("VCC: write: Invalid TTY handle\n"); + return -ENXIO; + } + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: write: Failed to find VCC port"); + return -ENODEV; + } + + spin_lock_irqsave(&port->lock, flags); + + pkt = &port->buffer; + pkt->tag.type = VIO_TYPE_DATA; + + while (count > 0) { + /* Minimum of data to write and space available */ + tosend = min(count, (VCC_BUFF_LEN - port->chars_in_buffer)); + + if (!tosend) + break; + + memcpy(&pkt->data[port->chars_in_buffer], &buf[total_sent], + tosend); + port->chars_in_buffer += tosend; + pkt->tag.stype = tosend; + + vccdbg("TAG [%02x:%02x:%04x:%08x]\n", pkt->tag.type, + pkt->tag.stype, pkt->tag.stype_env, pkt->tag.sid); + vccdbg("DATA [%s]\n", pkt->data); + vccdbgl(port->vio.lp); + + /* Since we know we have enough room in VCC buffer for tosend + * we record that it was sent regardless of whether the + * hypervisor actually took it because we have it buffered. + */ + rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); + vccdbg("VCC: write: ldc_write(%d)=%d\n", + (VIO_TAG_SIZE + tosend), rv); + + total_sent += tosend; + count -= tosend; + if (rv < 0) { + vcc_kick_tx(port); + break; + } + + port->chars_in_buffer = 0; + } + + spin_unlock_irqrestore(&port->lock, flags); + + vcc_put(port, false); + + vccdbg("VCC: write: total=%d rv=%d", total_sent, rv); + + return total_sent ? total_sent : rv; +} + +static int vcc_write_room(struct tty_struct *tty) +{ + struct vcc_port *port; + u64 num; + + if (unlikely(!tty)) { + pr_err("VCC: write_room: Invalid TTY handle\n"); + return -ENXIO; + } + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: write_room: Failed to find VCC port\n"); + return -ENODEV; + } + + num = VCC_BUFF_LEN - port->chars_in_buffer; + + vcc_put(port, false); + + return num; +} + +static int vcc_chars_in_buffer(struct tty_struct *tty) +{ + struct vcc_port *port; + u64 num; + + if (unlikely(!tty)) { + pr_err("VCC: chars_in_buffer: Invalid TTY handle\n"); + return -ENXIO; + } + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: chars_in_buffer: Failed to find VCC port\n"); + return -ENODEV; + } + + num = port->chars_in_buffer; + + vcc_put(port, false); + + return num; +} + +static int vcc_break_ctl(struct tty_struct *tty, int state) +{ + struct vcc_port *port; + unsigned long flags; + + if (unlikely(!tty)) { + pr_err("VCC: break_ctl: Invalid TTY handle\n"); + return -ENXIO; + } + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: break_ctl: Failed to find VCC port\n"); + return -ENODEV; + } + + /* Turn off break */ + if (state == 0) { + vcc_put(port, false); + return 0; + } + + spin_lock_irqsave(&port->lock, flags); + + if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) + vcc_kick_tx(port); + + spin_unlock_irqrestore(&port->lock, flags); + + vcc_put(port, false); + + return 0; +} + +static int vcc_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct vcc_port *port_vcc; + struct tty_port *port_tty; + int ret; + + if (unlikely(!tty)) { + pr_err("VCC: install: Invalid TTY handle\n"); + return -ENXIO; + } + + if (tty->index >= VCC_MAX_PORTS) + return -EINVAL; + + ret = tty_standard_install(driver, tty); + if (ret) + return ret; + + port_tty = kzalloc(sizeof(struct tty_port), GFP_KERNEL); + if (!port_tty) + return -ENOMEM; + + port_vcc = vcc_get(tty->index, true); + if (!port_vcc) { + pr_err("VCC: install: Failed to find VCC port\n"); + tty->port = NULL; + kfree(port_tty); + return -ENODEV; + } + + tty_port_init(port_tty); + port_tty->ops = &vcc_port_ops; + tty->port = port_tty; + + port_vcc->tty = tty; + + vcc_put(port_vcc, true); + + return 0; +} + +static void vcc_cleanup(struct tty_struct *tty) +{ + struct vcc_port *port; + + if (unlikely(!tty)) { + pr_err("VCC: cleanup: Invalid TTY handle\n"); + return; + } + + port = vcc_get(tty->index, true); + if (port) { + port->tty = NULL; + + if (port->removed) { + vcc_table_remove(tty->index); + kfree(port->vio.name); + kfree(port->domain); + kfree(port); + } else { + vcc_put(port, true); + } + } + + tty_port_destroy(tty->port); + kfree(tty->port); + tty->port = NULL; +} + +static const struct tty_operations vcc_ops = { + .open = vcc_open, + .close = vcc_close, + .hangup = vcc_hangup, + .write = vcc_write, + .write_room = vcc_write_room, + .chars_in_buffer = vcc_chars_in_buffer, + .break_ctl = vcc_break_ctl, + .install = vcc_install, + .cleanup = vcc_cleanup, +}; + +#define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) + +static int vcc_tty_init(void) +{ + int rv; + + pr_info("VCC: %s\n", version); + + vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS); + if (IS_ERR(vcc_tty_driver)) { + pr_err("VCC: TTY driver alloc failed\n"); + return PTR_ERR(vcc_tty_driver); + } + + vcc_tty_driver->driver_name = vcc_driver_name; + vcc_tty_driver->name = vcc_device_node; + + vcc_tty_driver->minor_start = VCC_MINOR_START; + vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; + vcc_tty_driver->init_termios = vcc_tty_termios; + + tty_set_operations(vcc_tty_driver, &vcc_ops); + + rv = tty_register_driver(vcc_tty_driver); + if (rv) { + pr_err("VCC: TTY driver registration failed\n"); + put_tty_driver(vcc_tty_driver); + vcc_tty_driver = NULL; + return rv; + } + + vccdbg("VCC: TTY driver registered\n"); + + return 0; +} + +static void vcc_tty_exit(void) +{ + tty_unregister_driver(vcc_tty_driver); + put_tty_driver(vcc_tty_driver); + vccdbg("VCC: TTY driver unregistered\n"); + + vcc_tty_driver = NULL; +} + +static int __init vcc_init(void) +{ + int rv; + + rv = vcc_tty_init(); + if (rv) { + pr_err("VCC: TTY init failed\n"); + return rv; + } + + rv = vio_register_driver(&vcc_driver); + if (rv) { + pr_err("VCC: VIO driver registration failed\n"); + vcc_tty_exit(); + } else { + vccdbg("VCC: VIO driver registered successfully\n"); + } + + return rv; +} + +static void __exit vcc_exit(void) +{ + vio_unregister_driver(&vcc_driver); + vccdbg("VCC: VIO driver unregistered\n"); + vcc_tty_exit(); + vccdbg("VCC: TTY driver unregistered\n"); +} + +module_init(vcc_init); +module_exit(vcc_exit); diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index a8b8d8b8d9f3..d4e0f7cd96fa 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -123,13 +123,12 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) * regular memory. The HCD_LOCAL_MEM flag does just that. */ - if (!dma_declare_coherent_memory(dev, mem->start, + retval = dma_declare_coherent_memory(dev, mem->start, mem->start - mem->parent->start, resource_size(mem), - DMA_MEMORY_MAP | - DMA_MEMORY_EXCLUSIVE)) { + DMA_MEMORY_EXCLUSIVE); + if (retval) { dev_err(dev, "cannot declare coherent memory\n"); - retval = -ENXIO; goto err1; } diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index cfcfadfc94fc..16d081a093bb 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -227,13 +227,10 @@ static int ohci_hcd_tmio_drv_probe(struct platform_device *dev) goto err_ioremap_regs; } - if (!dma_declare_coherent_memory(&dev->dev, sram->start, - sram->start, - resource_size(sram), - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE)) { - ret = -EBUSY; + ret = dma_declare_coherent_memory(&dev->dev, sram->start, sram->start, + resource_size(sram), DMA_MEMORY_EXCLUSIVE); + if (ret) goto err_dma_declare; - } if (cell->enable) { ret = cell->enable(dev); diff --git a/drivers/vfio/platform/vfio_amba.c b/drivers/vfio/platform/vfio_amba.c index 31372fbf6c5b..62dfbfeaabfc 100644 --- a/drivers/vfio/platform/vfio_amba.c +++ b/drivers/vfio/platform/vfio_amba.c @@ -93,7 +93,7 @@ static int vfio_amba_remove(struct amba_device *adev) return -EINVAL; } -static struct amba_id pl330_ids[] = { +static const struct amba_id pl330_ids[] = { { 0, 0 }, }; diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 330d50582f40..f5a86f651f38 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -85,6 +85,7 @@ struct vfio_group { struct list_head unbound_list; struct mutex unbound_lock; atomic_t opened; + wait_queue_head_t container_q; bool noiommu; struct kvm *kvm; struct blocking_notifier_head notifier; @@ -138,9 +139,10 @@ struct iommu_group *vfio_iommu_group_get(struct device *dev) iommu_group_set_name(group, "vfio-noiommu"); iommu_group_set_iommudata(group, &noiommu, NULL); ret = iommu_group_add_device(group, dev); - iommu_group_put(group); - if (ret) + if (ret) { + iommu_group_put(group); return NULL; + } /* * Where to taint? At this point we've added an IOMMU group for a @@ -337,6 +339,7 @@ static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group) mutex_init(&group->unbound_lock); atomic_set(&group->container_users, 0); atomic_set(&group->opened, 0); + init_waitqueue_head(&group->container_q); group->iommu_group = iommu_group; #ifdef CONFIG_VFIO_NOIOMMU group->noiommu = (iommu_group_get_iommudata(iommu_group) == &noiommu); @@ -993,6 +996,23 @@ void *vfio_del_group_dev(struct device *dev) } } while (ret <= 0); + /* + * In order to support multiple devices per group, devices can be + * plucked from the group while other devices in the group are still + * in use. The container persists with this group and those remaining + * devices still attached. If the user creates an isolation violation + * by binding this device to another driver while the group is still in + * use, that's their fault. However, in the case of removing the last, + * or potentially the only, device in the group there can be no other + * in-use devices in the group. The user has done their due diligence + * and we should lay no claims to those devices. In order to do that, + * we need to make sure the group is detached from the container. + * Without this stall, we're potentially racing with a user process + * that may attempt to immediately bind this device to another driver. + */ + if (list_empty(&group->device_list)) + wait_event(group->container_q, !group->container); + vfio_group_put(group); return device_data; @@ -1298,6 +1318,7 @@ static void __vfio_group_unset_container(struct vfio_group *group) group->iommu_group); group->container = NULL; + wake_up(&group->container_q); list_del(&group->container_next); /* Detaching the last group deprivileges a container, remove iommu */ diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 8549cb111627..92155cce926d 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -1169,13 +1169,21 @@ static bool vfio_iommu_has_sw_msi(struct iommu_group *group, phys_addr_t *base) INIT_LIST_HEAD(&group_resv_regions); iommu_get_group_resv_regions(group, &group_resv_regions); list_for_each_entry(region, &group_resv_regions, list) { + /* + * The presence of any 'real' MSI regions should take + * precedence over the software-managed one if the + * IOMMU driver happens to advertise both types. + */ + if (region->type == IOMMU_RESV_MSI) { + ret = false; + break; + } + if (region->type == IOMMU_RESV_SW_MSI) { *base = region->start; ret = true; - goto out; } } -out: list_for_each_entry_safe(region, next, &group_resv_regions, list) kfree(region); return ret; @@ -1265,8 +1273,8 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, INIT_LIST_HEAD(&domain->group_list); list_add(&group->next, &domain->group_list); - msi_remap = resv_msi ? irq_domain_check_msi_remap() : - iommu_capable(bus, IOMMU_CAP_INTR_REMAP); + msi_remap = irq_domain_check_msi_remap() || + iommu_capable(bus, IOMMU_CAP_INTR_REMAP); if (!allow_unsafe_interrupts && !msi_remap) { pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n", diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 9cb3f722dce1..d6dbb28245e6 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -1271,7 +1271,7 @@ static struct vhost_umem *vhost_umem_alloc(void) if (!umem) return NULL; - umem->umem_tree = RB_ROOT; + umem->umem_tree = RB_ROOT_CACHED; umem->numem = 0; INIT_LIST_HEAD(&umem->umem_list); diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index bb7c29b8b9fc..d59a9cc65f9d 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -71,7 +71,7 @@ struct vhost_umem_node { }; struct vhost_umem { - struct rb_root umem_tree; + struct rb_root_cached umem_tree; struct list_head umem_list; int numem; }; diff --git a/drivers/video/backlight/kb3886_bl.c b/drivers/video/backlight/kb3886_bl.c index 84a110a719cb..96312c3afc07 100644 --- a/drivers/video/backlight/kb3886_bl.c +++ b/drivers/video/backlight/kb3886_bl.c @@ -78,7 +78,7 @@ static struct kb3886bl_machinfo *bl_machinfo; static unsigned long kb3886bl_flags; #define KB3886BL_SUSPENDED 0x01 -static struct dmi_system_id kb3886bl_device_table[] __initdata = { +static const struct dmi_system_id kb3886bl_device_table[] __initconst = { { .ident = "Sahara Touch-iT", .matches = { diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index bb4d7ed2ce55..3c945f9f5f0f 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -148,10 +148,4 @@ config W1_SLAVE_DS28E04 If you are unsure, say N. -config W1_SLAVE_BQ27000 - tristate "BQ27000 slave support" - help - Say Y here if you want to use a hdq - bq27000 slave support. - endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 4622d8fed362..36b22fb2d3a1 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -16,5 +16,4 @@ obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o -obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c deleted file mode 100644 index 8046ac45381a..000000000000 --- a/drivers/w1/slaves/w1_bq27000.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * drivers/w1/slaves/w1_bq27000.c - * - * Copyright (C) 2007 Texas Instruments, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/types.h> -#include <linux/platform_device.h> -#include <linux/mutex.h> -#include <linux/power/bq27xxx_battery.h> - -#include <linux/w1.h> - -#define W1_FAMILY_BQ27000 0x01 - -#define HDQ_CMD_READ (0) -#define HDQ_CMD_WRITE (1<<7) - -static int F_ID; -module_param(F_ID, int, S_IRUSR); -MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ device"); - -static int w1_bq27000_read(struct device *dev, unsigned int reg) -{ - u8 val; - struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev); - - mutex_lock(&sl->master->bus_mutex); - w1_write_8(sl->master, HDQ_CMD_READ | reg); - val = w1_read_8(sl->master); - mutex_unlock(&sl->master->bus_mutex); - - return val; -} - -static struct bq27xxx_platform_data bq27000_battery_info = { - .read = w1_bq27000_read, - .name = "bq27000-battery", - .chip = BQ27000, -}; - -static int w1_bq27000_add_slave(struct w1_slave *sl) -{ - int ret; - struct platform_device *pdev; - - pdev = platform_device_alloc("bq27000-battery", -1); - if (!pdev) { - ret = -ENOMEM; - return ret; - } - ret = platform_device_add_data(pdev, - &bq27000_battery_info, - sizeof(bq27000_battery_info)); - if (ret) - goto pdev_add_failed; - pdev->dev.parent = &sl->dev; - - ret = platform_device_add(pdev); - if (ret) - goto pdev_add_failed; - - dev_set_drvdata(&sl->dev, pdev); - - goto success; - -pdev_add_failed: - platform_device_put(pdev); -success: - return ret; -} - -static void w1_bq27000_remove_slave(struct w1_slave *sl) -{ - struct platform_device *pdev = dev_get_drvdata(&sl->dev); - - platform_device_unregister(pdev); -} - -static struct w1_family_ops w1_bq27000_fops = { - .add_slave = w1_bq27000_add_slave, - .remove_slave = w1_bq27000_remove_slave, -}; - -static struct w1_family w1_bq27000_family = { - .fid = W1_FAMILY_BQ27000, - .fops = &w1_bq27000_fops, -}; - -static int __init w1_bq27000_init(void) -{ - if (F_ID) - w1_bq27000_family.fid = F_ID; - - return w1_register_family(&w1_bq27000_family); -} - -static void __exit w1_bq27000_exit(void) -{ - w1_unregister_family(&w1_bq27000_family); -} - -module_init(w1_bq27000_init); -module_exit(w1_bq27000_exit); - -MODULE_AUTHOR("Texas Instruments Ltd"); -MODULE_DESCRIPTION("HDQ/1-wire slave driver bq27000 battery monitor chip"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_BQ27000)); diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c index 1bf55a32a4b3..3fa40c723e8e 100644 --- a/drivers/xen/gntalloc.c +++ b/drivers/xen/gntalloc.c @@ -294,7 +294,7 @@ static long gntalloc_ioctl_alloc(struct gntalloc_file_private_data *priv, goto out; } - gref_ids = kcalloc(op.count, sizeof(gref_ids[0]), GFP_TEMPORARY); + gref_ids = kcalloc(op.count, sizeof(gref_ids[0]), GFP_KERNEL); if (!gref_ids) { rc = -ENOMEM; goto out; |