alistair23-linux/drivers/clk/x86/clk-lpss.c
Mika Westerberg 701190fd74 clk: x86: add support for Lynxpoint LPSS clocks
Intel Lynxpoint Low Power Subsystem hosts peripherals like UART, I2C and
SPI controllers. For most of these there is a configuration register that
allows software to enable and disable the functional clock. Disabling the
clock while the peripheral is not used saves power.

In order to take advantage of this we add a new clock gate of type
lpss_gate that just re-uses the ordinary clk_gate but in addition is able
to enumerate the base address register of the device using ACPI.

We then create a clock tree that models the Lynxpoint LPSS clocks using
these gates and fixed clocks so that we can pass clock rate to the drivers
as well.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-01-23 21:14:22 +01:00

100 lines
2.5 KiB
C

/*
* 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;
}