diff options
Diffstat (limited to 'drivers/clk/x86')
-rw-r--r-- | drivers/clk/x86/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/x86/clk-lpss.c | 99 | ||||
-rw-r--r-- | drivers/clk/x86/clk-lpss.h | 36 | ||||
-rw-r--r-- | drivers/clk/x86/clk-lpt.c | 86 |
4 files changed, 223 insertions, 0 deletions
diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile new file mode 100644 index 00000000000..f9ba4fab0dd --- /dev/null +++ b/drivers/clk/x86/Makefile @@ -0,0 +1,2 @@ +clk-x86-lpss-objs := clk-lpss.o clk-lpt.o +obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o diff --git a/drivers/clk/x86/clk-lpss.c b/drivers/clk/x86/clk-lpss.c new file mode 100644 index 00000000000..b5e229f3c3d --- /dev/null +++ b/drivers/clk/x86/clk-lpss.c @@ -0,0 +1,99 @@ +/* + * Intel Low Power Subsystem clocks. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> + * Heikki Krogerus <heikki.krogerus@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/acpi.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> + +static int clk_lpss_is_mmio_resource(struct acpi_resource *res, void *data) +{ + struct resource r; + return !acpi_dev_resource_memory(res, &r); +} + +static acpi_status clk_lpss_find_mmio(acpi_handle handle, u32 level, + void *data, void **retval) +{ + struct resource_list_entry *rentry; + struct list_head resource_list; + struct acpi_device *adev; + const char *uid = data; + int ret; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (uid) { + if (!adev->pnp.unique_id) + return AE_OK; + if (strcmp(uid, adev->pnp.unique_id)) + return AE_OK; + } + + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, + clk_lpss_is_mmio_resource, NULL); + if (ret < 0) + return AE_NO_MEMORY; + + list_for_each_entry(rentry, &resource_list, node) + if (resource_type(&rentry->res) == IORESOURCE_MEM) { + *(struct resource *)retval = rentry->res; + break; + } + + acpi_dev_free_resource_list(&resource_list); + return AE_OK; +} + +/** + * clk_register_lpss_gate - register LPSS clock gate + * @name: name of this clock gate + * @parent_name: parent clock name + * @hid: ACPI _HID of the device + * @uid: ACPI _UID of the device (optional) + * @offset: LPSS PRV_CLOCK_PARAMS offset + * + * Creates and registers LPSS clock gate. + */ +struct clk *clk_register_lpss_gate(const char *name, const char *parent_name, + const char *hid, const char *uid, + unsigned offset) +{ + struct resource res = { }; + void __iomem *mmio_base; + acpi_status status; + struct clk *clk; + + /* + * First try to look the device and its mmio resource from the + * ACPI namespace. + */ + status = acpi_get_devices(hid, clk_lpss_find_mmio, (void *)uid, + (void **)&res); + if (ACPI_FAILURE(status) || !res.start) + return ERR_PTR(-ENODEV); + + mmio_base = ioremap(res.start, resource_size(&res)); + if (!mmio_base) + return ERR_PTR(-ENOMEM); + + clk = clk_register_gate(NULL, name, parent_name, 0, mmio_base + offset, + 0, 0, NULL); + if (IS_ERR(clk)) + iounmap(mmio_base); + + return clk; +} diff --git a/drivers/clk/x86/clk-lpss.h b/drivers/clk/x86/clk-lpss.h new file mode 100644 index 00000000000..e9460f44229 --- /dev/null +++ b/drivers/clk/x86/clk-lpss.h @@ -0,0 +1,36 @@ +/* + * Intel Low Power Subsystem clock. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> + * Heikki Krogerus <heikki.krogerus@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. + */ + +#ifndef __CLK_LPSS_H +#define __CLK_LPSS_H + +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/clk.h> + +#ifdef CONFIG_ACPI +extern struct clk *clk_register_lpss_gate(const char *name, + const char *parent_name, + const char *hid, const char *uid, + unsigned offset); +#else +static inline struct clk *clk_register_lpss_gate(const char *name, + const char *parent_name, + const char *hid, + const char *uid, + unsigned offset) +{ + return ERR_PTR(-ENODEV); +} +#endif + +#endif /* __CLK_LPSS_H */ diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c new file mode 100644 index 00000000000..81298aeef7e --- /dev/null +++ b/drivers/clk/x86/clk-lpt.c @@ -0,0 +1,86 @@ +/* + * Intel Lynxpoint LPSS clocks. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> + * Heikki Krogerus <heikki.krogerus@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/acpi.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "clk-lpss.h" + +#define PRV_CLOCK_PARAMS 0x800 + +static int lpt_clk_probe(struct platform_device *pdev) +{ + struct clk *clk; + + /* LPSS free running clock */ + clk = clk_register_fixed_rate(&pdev->dev, "lpss_clk", NULL, CLK_IS_ROOT, + 100000000); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + /* Shared DMA clock */ + clk_register_clkdev(clk, "hclk", "INTL9C60.0.auto"); + + /* SPI clocks */ + clk = clk_register_lpss_gate("spi0_clk", "lpss_clk", "INT33C0", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C0:00"); + + clk = clk_register_lpss_gate("spi1_clk", "lpss_clk", "INT33C1", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C1:00"); + + /* I2C clocks */ + clk = clk_register_lpss_gate("i2c0_clk", "lpss_clk", "INT33C2", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C2:00"); + + clk = clk_register_lpss_gate("i2c1_clk", "lpss_clk", "INT33C3", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C3:00"); + + /* UART clocks */ + clk = clk_register_lpss_gate("uart0_clk", "lpss_clk", "INT33C4", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C4:00"); + + clk = clk_register_lpss_gate("uart1_clk", "lpss_clk", "INT33C5", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C5:00"); + + return 0; +} + +static struct platform_driver lpt_clk_driver = { + .driver = { + .name = "clk-lpt", + .owner = THIS_MODULE, + }, + .probe = lpt_clk_probe, +}; + +static int __init lpt_clk_init(void) +{ + return platform_driver_register(&lpt_clk_driver); +} +arch_initcall(lpt_clk_init); |