Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI / scan: Add special handler for Intel Lynxpoint LPSS devices

Devices on the Intel Lynxpoint Low Power Subsystem (LPSS) have some
common features that aren't shared with any other platform devices,
including the clock and LTR (Latency Tolerance Reporting) registers.
It is better to handle those features in common code than to bother
device drivers with doing that (I/O functionality-wise the LPSS
devices are generally compatible with other devices that don't
have those special registers and may be handled by the same drivers).

The clock registers of the LPSS devices are now taken care of by
the special clk-x86-lpss driver, but the MMIO mappings used for
accessing those registers can also be used for accessing the LTR
registers on those devices (LTR support for the Lynxpoint LPSS is
going to be added by a subsequent patch). Thus it is convenient
to add a special ACPI scan handler for the Lynxpoint LPSS devices
that will create the MMIO mappings for accessing the clock (and
LTR in the future) registers and will register the LPSS devices'
clocks, so the clk-x86-lpss driver will only need to take care of
the main Lynxpoint LPSS clock.

Introduce a special ACPI scan handler for Intel Lynxpoint LPSS
devices as described above. This also reduces overhead related to
browsing the ACPI namespace in search of the LPSS devices before the
registration of their clocks, removes some LPSS-specific (and
somewhat ugly) code from acpi_platform.c and shrinks the overall code
size slightly.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Mike Turquette <mturquette@linaro.org>

+195 -213
+1
drivers/acpi/Makefile
··· 39 39 acpi-$(CONFIG_ACPI_DOCK) += dock.o 40 40 acpi-y += pci_root.o pci_link.o pci_irq.o 41 41 acpi-y += csrt.o 42 + acpi-$(CONFIG_X86_INTEL_LPSS) += acpi_lpss.o 42 43 acpi-y += acpi_platform.o 43 44 acpi-y += power.o 44 45 acpi-y += event.o
+163
drivers/acpi/acpi_lpss.c
··· 1 + /* 2 + * ACPI support for Intel Lynxpoint LPSS. 3 + * 4 + * Copyright (C) 2013, Intel Corporation 5 + * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> 6 + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + #include <linux/acpi.h> 14 + #include <linux/clk.h> 15 + #include <linux/clkdev.h> 16 + #include <linux/clk-provider.h> 17 + #include <linux/err.h> 18 + #include <linux/io.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/platform_data/clk-lpss.h> 21 + 22 + #include "internal.h" 23 + 24 + ACPI_MODULE_NAME("acpi_lpss"); 25 + 26 + #define LPSS_CLK_OFFSET 0x800 27 + #define LPSS_CLK_SIZE 0x04 28 + 29 + struct lpss_device_desc { 30 + bool clk_required; 31 + const char *clk_parent; 32 + }; 33 + 34 + struct lpss_private_data { 35 + void __iomem *mmio_base; 36 + resource_size_t mmio_size; 37 + struct clk *clk; 38 + const struct lpss_device_desc *dev_desc; 39 + }; 40 + 41 + static struct lpss_device_desc lpt_dev_desc = { 42 + .clk_required = true, 43 + .clk_parent = "lpss_clk", 44 + }; 45 + 46 + static const struct acpi_device_id acpi_lpss_device_ids[] = { 47 + /* Lynxpoint LPSS devices */ 48 + { "INT33C0", (unsigned long)&lpt_dev_desc }, 49 + { "INT33C1", (unsigned long)&lpt_dev_desc }, 50 + { "INT33C2", (unsigned long)&lpt_dev_desc }, 51 + { "INT33C3", (unsigned long)&lpt_dev_desc }, 52 + { "INT33C4", (unsigned long)&lpt_dev_desc }, 53 + { "INT33C5", (unsigned long)&lpt_dev_desc }, 54 + { "INT33C6", }, 55 + { "INT33C7", }, 56 + 57 + { } 58 + }; 59 + 60 + static int is_memory(struct acpi_resource *res, void *not_used) 61 + { 62 + struct resource r; 63 + return !acpi_dev_resource_memory(res, &r); 64 + } 65 + 66 + /* LPSS main clock device. */ 67 + static struct platform_device *lpss_clk_dev; 68 + 69 + static inline void lpt_register_clock_device(void) 70 + { 71 + lpss_clk_dev = platform_device_register_simple("clk-lpt", -1, NULL, 0); 72 + } 73 + 74 + static int register_device_clock(struct acpi_device *adev, 75 + struct lpss_private_data *pdata) 76 + { 77 + const struct lpss_device_desc *dev_desc = pdata->dev_desc; 78 + 79 + if (!lpss_clk_dev) 80 + lpt_register_clock_device(); 81 + 82 + if (!dev_desc->clk_parent || !pdata->mmio_base 83 + || pdata->mmio_size < LPSS_CLK_OFFSET + LPSS_CLK_SIZE) 84 + return -ENODATA; 85 + 86 + pdata->clk = clk_register_gate(NULL, dev_name(&adev->dev), 87 + dev_desc->clk_parent, 0, 88 + pdata->mmio_base + LPSS_CLK_OFFSET, 89 + 0, 0, NULL); 90 + if (IS_ERR(pdata->clk)) 91 + return PTR_ERR(pdata->clk); 92 + 93 + clk_register_clkdev(pdata->clk, NULL, dev_name(&adev->dev)); 94 + return 0; 95 + } 96 + 97 + static int acpi_lpss_create_device(struct acpi_device *adev, 98 + const struct acpi_device_id *id) 99 + { 100 + struct lpss_device_desc *dev_desc; 101 + struct lpss_private_data *pdata; 102 + struct resource_list_entry *rentry; 103 + struct list_head resource_list; 104 + int ret; 105 + 106 + dev_desc = (struct lpss_device_desc *)id->driver_data; 107 + if (!dev_desc) 108 + return acpi_create_platform_device(adev, id); 109 + 110 + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); 111 + if (!pdata) 112 + return -ENOMEM; 113 + 114 + INIT_LIST_HEAD(&resource_list); 115 + ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL); 116 + if (ret < 0) 117 + goto err_out; 118 + 119 + list_for_each_entry(rentry, &resource_list, node) 120 + if (resource_type(&rentry->res) == IORESOURCE_MEM) { 121 + pdata->mmio_size = resource_size(&rentry->res); 122 + pdata->mmio_base = ioremap(rentry->res.start, 123 + pdata->mmio_size); 124 + pdata->dev_desc = dev_desc; 125 + break; 126 + } 127 + 128 + acpi_dev_free_resource_list(&resource_list); 129 + 130 + if (dev_desc->clk_required) { 131 + ret = register_device_clock(adev, pdata); 132 + if (ret) { 133 + /* 134 + * Skip the device, but don't terminate the namespace 135 + * scan. 136 + */ 137 + ret = 0; 138 + goto err_out; 139 + } 140 + } 141 + 142 + adev->driver_data = pdata; 143 + ret = acpi_create_platform_device(adev, id); 144 + if (ret > 0) 145 + return ret; 146 + 147 + adev->driver_data = NULL; 148 + 149 + err_out: 150 + kfree(pdata); 151 + return ret; 152 + } 153 + 154 + static struct acpi_scan_handler lpss_handler = { 155 + .ids = acpi_lpss_device_ids, 156 + .attach = acpi_lpss_create_device, 157 + }; 158 + 159 + void __init acpi_lpss_init(void) 160 + { 161 + if (!lpt_clk_init()) 162 + acpi_scan_add_handler(&lpss_handler); 163 + }
+2 -38
drivers/acpi/acpi_platform.c
··· 22 22 23 23 ACPI_MODULE_NAME("platform"); 24 24 25 - /* Flags for acpi_create_platform_device */ 26 - #define ACPI_PLATFORM_CLK BIT(0) 27 - 28 25 /* 29 26 * The following ACPI IDs are known to be suitable for representing as 30 27 * platform devices. ··· 30 33 31 34 { "PNP0D40" }, 32 35 33 - /* Haswell LPSS devices */ 34 - { "INT33C0", ACPI_PLATFORM_CLK }, 35 - { "INT33C1", ACPI_PLATFORM_CLK }, 36 - { "INT33C2", ACPI_PLATFORM_CLK }, 37 - { "INT33C3", ACPI_PLATFORM_CLK }, 38 - { "INT33C4", ACPI_PLATFORM_CLK }, 39 - { "INT33C5", ACPI_PLATFORM_CLK }, 40 - { "INT33C6", ACPI_PLATFORM_CLK }, 41 - { "INT33C7", ACPI_PLATFORM_CLK }, 42 - 43 36 { } 44 37 }; 45 - 46 - static int acpi_create_platform_clks(struct acpi_device *adev) 47 - { 48 - static struct platform_device *pdev; 49 - 50 - /* Create Lynxpoint LPSS clocks */ 51 - if (!pdev && !strncmp(acpi_device_hid(adev), "INT33C", 6)) { 52 - pdev = platform_device_register_simple("clk-lpt", -1, NULL, 0); 53 - if (IS_ERR(pdev)) 54 - return PTR_ERR(pdev); 55 - } 56 - 57 - return 0; 58 - } 59 38 60 39 /** 61 40 * acpi_create_platform_device - Create platform device for ACPI device node ··· 44 71 * 45 72 * Name of the platform device will be the same as @adev's. 46 73 */ 47 - static int acpi_create_platform_device(struct acpi_device *adev, 48 - const struct acpi_device_id *id) 74 + int acpi_create_platform_device(struct acpi_device *adev, 75 + const struct acpi_device_id *id) 49 76 { 50 - unsigned long flags = id->driver_data; 51 77 struct platform_device *pdev = NULL; 52 78 struct acpi_device *acpi_parent; 53 79 struct platform_device_info pdevinfo; ··· 54 82 struct list_head resource_list; 55 83 struct resource *resources; 56 84 int count; 57 - 58 - if (flags & ACPI_PLATFORM_CLK) { 59 - int ret = acpi_create_platform_clks(adev); 60 - if (ret) { 61 - dev_err(&adev->dev, "failed to create clocks\n"); 62 - return ret; 63 - } 64 - } 65 85 66 86 /* If the ACPI node already has a physical device attached, skip it. */ 67 87 if (adev->physical_node_count)
+8
drivers/acpi/internal.h
··· 48 48 #else 49 49 static inline void acpi_debugfs_init(void) { return; } 50 50 #endif 51 + #ifdef CONFIG_X86_INTEL_LPSS 52 + void acpi_lpss_init(void); 53 + #else 54 + static inline void acpi_lpss_init(void) {} 55 + #endif 51 56 52 57 /* -------------------------------------------------------------------------- 53 58 Device Node Initialization / Removal ··· 135 130 Platform bus support 136 131 -------------------------------------------------------------------------- */ 137 132 struct platform_device; 133 + 134 + int acpi_create_platform_device(struct acpi_device *adev, 135 + const struct acpi_device_id *id); 138 136 139 137 #endif /* _ACPI_INTERNAL_H_ */
+1
drivers/acpi/scan.c
··· 1788 1788 acpi_pci_root_init(); 1789 1789 acpi_pci_link_init(); 1790 1790 acpi_platform_init(); 1791 + acpi_lpss_init(); 1791 1792 acpi_csrt_init(); 1792 1793 acpi_container_init(); 1793 1794 acpi_pci_slot_init();
+1 -1
drivers/clk/x86/Makefile
··· 1 - clk-x86-lpss-objs := clk-lpss.o clk-lpt.o 1 + clk-x86-lpss-objs := clk-lpt.o 2 2 obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o
-99
drivers/clk/x86/clk-lpss.c
··· 1 - /* 2 - * Intel Low Power Subsystem clocks. 3 - * 4 - * Copyright (C) 2013, Intel Corporation 5 - * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> 6 - * Heikki Krogerus <heikki.krogerus@linux.intel.com> 7 - * 8 - * This program is free software; you can redistribute it and/or modify 9 - * it under the terms of the GNU General Public License version 2 as 10 - * published by the Free Software Foundation. 11 - */ 12 - 13 - #include <linux/acpi.h> 14 - #include <linux/clk.h> 15 - #include <linux/clk-provider.h> 16 - #include <linux/err.h> 17 - #include <linux/io.h> 18 - #include <linux/module.h> 19 - 20 - static int clk_lpss_is_mmio_resource(struct acpi_resource *res, void *data) 21 - { 22 - struct resource r; 23 - return !acpi_dev_resource_memory(res, &r); 24 - } 25 - 26 - static acpi_status clk_lpss_find_mmio(acpi_handle handle, u32 level, 27 - void *data, void **retval) 28 - { 29 - struct resource_list_entry *rentry; 30 - struct list_head resource_list; 31 - struct acpi_device *adev; 32 - const char *uid = data; 33 - int ret; 34 - 35 - if (acpi_bus_get_device(handle, &adev)) 36 - return AE_OK; 37 - 38 - if (uid) { 39 - if (!adev->pnp.unique_id) 40 - return AE_OK; 41 - if (strcmp(uid, adev->pnp.unique_id)) 42 - return AE_OK; 43 - } 44 - 45 - INIT_LIST_HEAD(&resource_list); 46 - ret = acpi_dev_get_resources(adev, &resource_list, 47 - clk_lpss_is_mmio_resource, NULL); 48 - if (ret < 0) 49 - return AE_NO_MEMORY; 50 - 51 - list_for_each_entry(rentry, &resource_list, node) 52 - if (resource_type(&rentry->res) == IORESOURCE_MEM) { 53 - *(struct resource *)retval = rentry->res; 54 - break; 55 - } 56 - 57 - acpi_dev_free_resource_list(&resource_list); 58 - return AE_OK; 59 - } 60 - 61 - /** 62 - * clk_register_lpss_gate - register LPSS clock gate 63 - * @name: name of this clock gate 64 - * @parent_name: parent clock name 65 - * @hid: ACPI _HID of the device 66 - * @uid: ACPI _UID of the device (optional) 67 - * @offset: LPSS PRV_CLOCK_PARAMS offset 68 - * 69 - * Creates and registers LPSS clock gate. 70 - */ 71 - struct clk *clk_register_lpss_gate(const char *name, const char *parent_name, 72 - const char *hid, const char *uid, 73 - unsigned offset) 74 - { 75 - struct resource res = { }; 76 - void __iomem *mmio_base; 77 - acpi_status status; 78 - struct clk *clk; 79 - 80 - /* 81 - * First try to look the device and its mmio resource from the 82 - * ACPI namespace. 83 - */ 84 - status = acpi_get_devices(hid, clk_lpss_find_mmio, (void *)uid, 85 - (void **)&res); 86 - if (ACPI_FAILURE(status) || !res.start) 87 - return ERR_PTR(-ENODEV); 88 - 89 - mmio_base = ioremap(res.start, resource_size(&res)); 90 - if (!mmio_base) 91 - return ERR_PTR(-ENOMEM); 92 - 93 - clk = clk_register_gate(NULL, name, parent_name, 0, mmio_base + offset, 94 - 0, 0, NULL); 95 - if (IS_ERR(clk)) 96 - iounmap(mmio_base); 97 - 98 - return clk; 99 - }
-36
drivers/clk/x86/clk-lpss.h
··· 1 - /* 2 - * Intel Low Power Subsystem clock. 3 - * 4 - * Copyright (C) 2013, Intel Corporation 5 - * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> 6 - * Heikki Krogerus <heikki.krogerus@linux.intel.com> 7 - * 8 - * This program is free software; you can redistribute it and/or modify 9 - * it under the terms of the GNU General Public License version 2 as 10 - * published by the Free Software Foundation. 11 - */ 12 - 13 - #ifndef __CLK_LPSS_H 14 - #define __CLK_LPSS_H 15 - 16 - #include <linux/err.h> 17 - #include <linux/errno.h> 18 - #include <linux/clk.h> 19 - 20 - #ifdef CONFIG_ACPI 21 - extern struct clk *clk_register_lpss_gate(const char *name, 22 - const char *parent_name, 23 - const char *hid, const char *uid, 24 - unsigned offset); 25 - #else 26 - static inline struct clk *clk_register_lpss_gate(const char *name, 27 - const char *parent_name, 28 - const char *hid, 29 - const char *uid, 30 - unsigned offset) 31 - { 32 - return ERR_PTR(-ENODEV); 33 - } 34 - #endif 35 - 36 - #endif /* __CLK_LPSS_H */
+1 -39
drivers/clk/x86/clk-lpt.c
··· 10 10 * published by the Free Software Foundation. 11 11 */ 12 12 13 - #include <linux/acpi.h> 14 13 #include <linux/clk.h> 15 14 #include <linux/clkdev.h> 16 15 #include <linux/clk-provider.h> 17 16 #include <linux/err.h> 18 17 #include <linux/module.h> 19 18 #include <linux/platform_device.h> 20 - 21 - #include "clk-lpss.h" 22 19 23 20 #define PRV_CLOCK_PARAMS 0x800 24 21 ··· 31 34 32 35 /* Shared DMA clock */ 33 36 clk_register_clkdev(clk, "hclk", "INTL9C60.0.auto"); 34 - 35 - /* SPI clocks */ 36 - clk = clk_register_lpss_gate("spi0_clk", "lpss_clk", "INT33C0", NULL, 37 - PRV_CLOCK_PARAMS); 38 - if (!IS_ERR(clk)) 39 - clk_register_clkdev(clk, NULL, "INT33C0:00"); 40 - 41 - clk = clk_register_lpss_gate("spi1_clk", "lpss_clk", "INT33C1", NULL, 42 - PRV_CLOCK_PARAMS); 43 - if (!IS_ERR(clk)) 44 - clk_register_clkdev(clk, NULL, "INT33C1:00"); 45 - 46 - /* I2C clocks */ 47 - clk = clk_register_lpss_gate("i2c0_clk", "lpss_clk", "INT33C2", NULL, 48 - PRV_CLOCK_PARAMS); 49 - if (!IS_ERR(clk)) 50 - clk_register_clkdev(clk, NULL, "INT33C2:00"); 51 - 52 - clk = clk_register_lpss_gate("i2c1_clk", "lpss_clk", "INT33C3", NULL, 53 - PRV_CLOCK_PARAMS); 54 - if (!IS_ERR(clk)) 55 - clk_register_clkdev(clk, NULL, "INT33C3:00"); 56 - 57 - /* UART clocks */ 58 - clk = clk_register_lpss_gate("uart0_clk", "lpss_clk", "INT33C4", NULL, 59 - PRV_CLOCK_PARAMS); 60 - if (!IS_ERR(clk)) 61 - clk_register_clkdev(clk, NULL, "INT33C4:00"); 62 - 63 - clk = clk_register_lpss_gate("uart1_clk", "lpss_clk", "INT33C5", NULL, 64 - PRV_CLOCK_PARAMS); 65 - if (!IS_ERR(clk)) 66 - clk_register_clkdev(clk, NULL, "INT33C5:00"); 67 - 68 37 return 0; 69 38 } 70 39 ··· 42 79 .probe = lpt_clk_probe, 43 80 }; 44 81 45 - static int __init lpt_clk_init(void) 82 + int __init lpt_clk_init(void) 46 83 { 47 84 return platform_driver_register(&lpt_clk_driver); 48 85 } 49 - arch_initcall(lpt_clk_init);
+18
include/linux/platform_data/clk-lpss.h
··· 1 + /* 2 + * Intel Low Power Subsystem clocks. 3 + * 4 + * Copyright (C) 2013, Intel Corporation 5 + * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> 6 + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + #ifndef __CLK_LPSS_H 14 + #define __CLK_LPSS_H 15 + 16 + extern int lpt_clk_init(void); 17 + 18 + #endif /* __CLK_LPSS_H */