linux-acpi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] ACPI / LPSS: Solution for two issues seen on Asus T100
@ 2014-05-13 12:13 Heikki Krogerus
  2014-05-13 12:13 ` [PATCH 1/4] ACPI / PM: Export rest of the subsys functions Heikki Krogerus
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Heikki Krogerus @ 2014-05-13 12:13 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Mike Turquette, Mika Westerberg, Jin Yao, Li Aubrey,
	Andy Shevchenko, linux-acpi, linux-kernel

Hi,

This combines two patch sets for LPSS that I had already send for
review separately. They conflicted with each other.

The first two patches will fix a problem were the context of the
private LPSS registers is lost when entering D3. The last two will add
support for the M/N dividers on LPSS by adding a new basic clock type
for fractional dividers. The UART driver needs support for it in order
to get clock rates that suit the requested baud rates.

I've updated the patch for the fractional divider support according to
Andy's suggestions.


Heikki Krogerus (4):
  ACPI / PM: Export rest of the subsys functions
  ACPI / LPSS: custom power domain for LPSS
  clk: new basic clk type for fractional divider
  ACPI / LPSS: support for fractional divider clock

 drivers/acpi/acpi_lpss.c             | 196 ++++++++++++++++++++++++++++++-----
 drivers/acpi/device_pm.c             |   2 +
 drivers/clk/Makefile                 |   1 +
 drivers/clk/clk-fractional-divider.c | 132 +++++++++++++++++++++++
 include/linux/acpi.h                 |   4 +
 include/linux/clk-provider.h         |  31 ++++++
 6 files changed, 338 insertions(+), 28 deletions(-)
 create mode 100644 drivers/clk/clk-fractional-divider.c

-- 
2.0.0.rc2


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] ACPI / PM: Export rest of the subsys functions
  2014-05-13 12:13 [PATCH 0/4] ACPI / LPSS: Solution for two issues seen on Asus T100 Heikki Krogerus
@ 2014-05-13 12:13 ` Heikki Krogerus
  2014-05-13 12:13 ` [PATCH 2/4] ACPI / LPSS: custom power domain for LPSS Heikki Krogerus
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Heikki Krogerus @ 2014-05-13 12:13 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Mike Turquette, Mika Westerberg, Jin Yao, Li Aubrey,
	Andy Shevchenko, linux-acpi, linux-kernel

No reason for excluding those two.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/acpi/device_pm.c | 2 ++
 include/linux/acpi.h     | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index d047739..7dae90b 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -923,6 +923,7 @@ int acpi_subsys_suspend(struct device *dev)
 	pm_runtime_resume(dev);
 	return pm_generic_suspend(dev);
 }
+EXPORT_SYMBOL_GPL(acpi_subsys_suspend);
 
 /**
  * acpi_subsys_suspend_late - Suspend device using ACPI.
@@ -968,6 +969,7 @@ int acpi_subsys_freeze(struct device *dev)
 	pm_runtime_resume(dev);
 	return pm_generic_freeze(dev);
 }
+EXPORT_SYMBOL_GPL(acpi_subsys_freeze);
 
 #endif /* CONFIG_PM_SLEEP */
 
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 7a8f2cd..47dcc0e 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -556,12 +556,16 @@ int acpi_dev_resume_early(struct device *dev);
 int acpi_subsys_prepare(struct device *dev);
 int acpi_subsys_suspend_late(struct device *dev);
 int acpi_subsys_resume_early(struct device *dev);
+int acpi_subsys_suspend(struct device *dev);
+int acpi_subsys_freeze(struct device *dev);
 #else
 static inline int acpi_dev_suspend_late(struct device *dev) { return 0; }
 static inline int acpi_dev_resume_early(struct device *dev) { return 0; }
 static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
 static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; }
 static inline int acpi_subsys_resume_early(struct device *dev) { return 0; }
+static inline int acpi_subsys_suspend(struct device *dev) { return 0; }
+static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
 #endif
 
 #if defined(CONFIG_ACPI) && defined(CONFIG_PM)
-- 
2.0.0.rc2

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] ACPI / LPSS: custom power domain for LPSS
  2014-05-13 12:13 [PATCH 0/4] ACPI / LPSS: Solution for two issues seen on Asus T100 Heikki Krogerus
  2014-05-13 12:13 ` [PATCH 1/4] ACPI / PM: Export rest of the subsys functions Heikki Krogerus
@ 2014-05-13 12:13 ` Heikki Krogerus
  2014-05-13 12:13 ` [PATCH 3/4] clk: new basic clk type for fractional divider Heikki Krogerus
  2014-05-13 12:13 ` [PATCH 4/4] ACPI / LPSS: support for fractional divider clock Heikki Krogerus
  3 siblings, 0 replies; 6+ messages in thread
From: Heikki Krogerus @ 2014-05-13 12:13 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Mike Turquette, Mika Westerberg, Jin Yao, Li Aubrey,
	Andy Shevchenko, linux-acpi, linux-kernel

A Power domain where we save the context of the additional
LPSS registers. We need to do this or all LPSS devices are
left in reset state when resuming from D3 on some Baytrails.
The devices with the fractional clock divider also have
zeros for N and M values after resuming unless they are
reset.

Li Aubrey found the root cause for the issue. The idea of
using power domain for LPSS came from Mika Westerberg.

Reported-by: Jin Yao <yao.jin@linux.intel.com>
Suggested-by: Li Aubrey <aubrey.li@linux.intel.com>
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/acpi/acpi_lpss.c | 133 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 126 insertions(+), 7 deletions(-)

diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 69e29f4..24e49a5 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -19,6 +19,7 @@
 #include <linux/platform_device.h>
 #include <linux/platform_data/clk-lpss.h>
 #include <linux/pm_runtime.h>
+#include <linux/delay.h>
 
 #include "internal.h"
 
@@ -43,6 +44,8 @@ ACPI_MODULE_NAME("acpi_lpss");
 #define LPSS_TX_INT			0x20
 #define LPSS_TX_INT_MASK		BIT(1)
 
+#define LPSS_PRV_REG_COUNT		9
+
 struct lpss_shared_clock {
 	const char *name;
 	unsigned long rate;
@@ -58,6 +61,7 @@ struct lpss_device_desc {
 	unsigned int prv_offset;
 	size_t prv_size_override;
 	bool clk_gate;
+	bool save_ctx;
 	struct lpss_shared_clock *shared_clock;
 	void (*setup)(struct lpss_private_data *pdata);
 };
@@ -72,6 +76,7 @@ struct lpss_private_data {
 	resource_size_t mmio_size;
 	struct clk *clk;
 	const struct lpss_device_desc *dev_desc;
+	u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
 };
 
 static void lpss_uart_setup(struct lpss_private_data *pdata)
@@ -116,6 +121,7 @@ static struct lpss_shared_clock pwm_clock = {
 
 static struct lpss_device_desc byt_pwm_dev_desc = {
 	.clk_required = true,
+	.save_ctx = true,
 	.shared_clock = &pwm_clock,
 };
 
@@ -128,6 +134,7 @@ static struct lpss_device_desc byt_uart_dev_desc = {
 	.clk_required = true,
 	.prv_offset = 0x800,
 	.clk_gate = true,
+	.save_ctx = true,
 	.shared_clock = &uart_clock,
 	.setup = lpss_uart_setup,
 };
@@ -141,6 +148,7 @@ static struct lpss_device_desc byt_spi_dev_desc = {
 	.clk_required = true,
 	.prv_offset = 0x400,
 	.clk_gate = true,
+	.save_ctx = true,
 	.shared_clock = &spi_clock,
 };
 
@@ -156,6 +164,7 @@ static struct lpss_shared_clock i2c_clock = {
 static struct lpss_device_desc byt_i2c_dev_desc = {
 	.clk_required = true,
 	.prv_offset = 0x800,
+	.save_ctx = true,
 	.shared_clock = &i2c_clock,
 };
 
@@ -449,6 +458,102 @@ static void acpi_lpss_set_ltr(struct device *dev, s32 val)
 	}
 }
 
+#ifdef CONFIG_PM
+static void acpi_lpss_save_ctx(struct device *dev)
+{
+	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+	int i;
+
+	for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
+		pdata->prv_reg_ctx[i] = __lpss_reg_read(pdata, i * sizeof(u32));
+		dev_dbg(dev, "saving 0x%08x from LPSS reg at offset 0x%02x\n",
+			pdata->prv_reg_ctx[i], (unsigned int)(i * sizeof(u32)));
+	}
+}
+
+static void acpi_lpss_restore_ctx(struct device *dev)
+{
+	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+	int i;
+
+	/* PCI spec expects 10ms delay when resuming from D3 to D0 */
+	msleep(10);
+
+	for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
+		__lpss_reg_write(pdata->prv_reg_ctx[i], pdata, i * sizeof(u32));
+		dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02x\n",
+			pdata->prv_reg_ctx[i], (unsigned int)(i * sizeof(u32)));
+	}
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_lpss_suspend_late(struct device *dev)
+{
+	int ret = pm_generic_suspend_late(dev);
+
+	if (ret)
+		return ret;
+
+	acpi_lpss_save_ctx(dev);
+	return acpi_dev_suspend_late(dev);
+}
+
+static int acpi_lpss_restore_early(struct device *dev)
+{
+	int ret = acpi_dev_resume_early(dev);
+
+	if (ret)
+		return ret;
+
+	acpi_lpss_restore_ctx(dev);
+	return pm_generic_resume_early(dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static int acpi_lpss_runtime_suspend(struct device *dev)
+{
+	int ret = pm_generic_runtime_suspend(dev);
+
+	if (ret)
+		return ret;
+
+	acpi_lpss_save_ctx(dev);
+	return acpi_dev_runtime_suspend(dev);
+}
+
+static int acpi_lpss_runtime_resume(struct device *dev)
+{
+	int ret = acpi_dev_runtime_resume(dev);
+
+	if (ret)
+		return ret;
+
+	acpi_lpss_restore_ctx(dev);
+	return pm_generic_runtime_resume(dev);
+}
+#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
+
+static struct dev_pm_domain acpi_lpss_pm_domain = {
+	.ops = {
+#ifdef CONFIG_PM_SLEEP
+		.suspend_late = acpi_lpss_suspend_late,
+		.restore_early = acpi_lpss_restore_early,
+		.prepare = acpi_subsys_prepare,
+		.suspend = acpi_subsys_suspend,
+		.resume_early = acpi_subsys_resume_early,
+		.freeze = acpi_subsys_freeze,
+		.poweroff = acpi_subsys_suspend,
+		.poweroff_late = acpi_subsys_suspend_late,
+#endif
+#ifdef CONFIG_PM_RUNTIME
+		.runtime_suspend = acpi_lpss_runtime_suspend,
+		.runtime_resume = acpi_lpss_runtime_resume,
+#endif
+	},
+};
+
 static int acpi_lpss_platform_notify(struct notifier_block *nb,
 				     unsigned long action, void *data)
 {
@@ -456,7 +561,6 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
 	struct lpss_private_data *pdata;
 	struct acpi_device *adev;
 	const struct acpi_device_id *id;
-	int ret = 0;
 
 	id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev);
 	if (!id || !id->driver_data)
@@ -466,7 +570,7 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
 		return 0;
 
 	pdata = acpi_driver_data(adev);
-	if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required)
+	if (!pdata || !pdata->mmio_base)
 		return 0;
 
 	if (pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) {
@@ -474,12 +578,27 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
 		return 0;
 	}
 
-	if (action == BUS_NOTIFY_ADD_DEVICE)
-		ret = sysfs_create_group(&pdev->dev.kobj, &lpss_attr_group);
-	else if (action == BUS_NOTIFY_DEL_DEVICE)
-		sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
+	switch (action) {
+	case BUS_NOTIFY_BOUND_DRIVER:
+		if (pdata->dev_desc->save_ctx)
+			pdev->dev.pm_domain = &acpi_lpss_pm_domain;
+		break;
+	case BUS_NOTIFY_UNBOUND_DRIVER:
+		if (pdata->dev_desc->save_ctx)
+			pdev->dev.pm_domain = NULL;
+		break;
+	case BUS_NOTIFY_ADD_DEVICE:
+		if (pdata->dev_desc->ltr_required)
+			return sysfs_create_group(&pdev->dev.kobj,
+						  &lpss_attr_group);
+	case BUS_NOTIFY_DEL_DEVICE:
+		if (pdata->dev_desc->ltr_required)
+			sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
+	default:
+		break;
+	}
 
-	return ret;
+	return 0;
 }
 
 static struct notifier_block acpi_lpss_nb = {
-- 
2.0.0.rc2

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] clk: new basic clk type for fractional divider
  2014-05-13 12:13 [PATCH 0/4] ACPI / LPSS: Solution for two issues seen on Asus T100 Heikki Krogerus
  2014-05-13 12:13 ` [PATCH 1/4] ACPI / PM: Export rest of the subsys functions Heikki Krogerus
  2014-05-13 12:13 ` [PATCH 2/4] ACPI / LPSS: custom power domain for LPSS Heikki Krogerus
@ 2014-05-13 12:13 ` Heikki Krogerus
  2014-05-14 13:55   ` Heikki Krogerus
  2014-05-13 12:13 ` [PATCH 4/4] ACPI / LPSS: support for fractional divider clock Heikki Krogerus
  3 siblings, 1 reply; 6+ messages in thread
From: Heikki Krogerus @ 2014-05-13 12:13 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Mike Turquette, Mika Westerberg, Jin Yao, Li Aubrey,
	Andy Shevchenko, linux-acpi, linux-kernel

Fractional divider clocks are fairly common. This adds basic
type for them.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/clk/Makefile                 |   1 +
 drivers/clk/clk-fractional-divider.c | 132 +++++++++++++++++++++++++++++++++++
 include/linux/clk-provider.h         |  31 ++++++++
 3 files changed, 164 insertions(+)
 create mode 100644 drivers/clk/clk-fractional-divider.c

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f651b2a..33bc79e 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-rate.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-gate.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-mux.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-composite.o
+obj-$(CONFIG_COMMON_CLK)	+= clk-fractional-divider.o
 
 # hardware specific clock types
 # please keep this section sorted lexicographically by file/directory path name
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
new file mode 100644
index 0000000..26f0f57
--- /dev/null
+++ b/drivers/clk/clk-fractional-divider.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * 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.
+ *
+ * Adjustable fractional divider clock implementation.
+ * Output rate = (m / n) * parent_rate.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gcd.h>
+
+#define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw)
+
+static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
+					unsigned long parent_rate)
+{
+	struct clk_fractional_divider *fd = to_clk_fd(hw);
+	unsigned long flags = 0;
+	u64 n, m;
+	u32 val;
+
+	if (fd->lock)
+		spin_lock_irqsave(fd->lock, flags);
+
+	val = clk_readl(fd->reg);
+
+	if (fd->lock)
+		spin_unlock_irqrestore(fd->lock, flags);
+
+	m = (val & fd->mmask) >> fd->mshift;
+	n = (val & fd->nmask) >> fd->nshift;
+
+	return parent_rate * m / n;
+}
+
+static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate,
+			      unsigned long *prate)
+{
+	struct clk_fractional_divider *fd = to_clk_fd(hw);
+	unsigned maxn = (fd->nmask >> fd->nshift) + 1;
+	unsigned div;
+
+	if (!rate || rate > *prate)
+		return *prate;
+
+	div = gcd(*prate, rate);
+
+	while ((*prate / div) > maxn) {
+		div <<= 1;
+		rate <<= 1;
+	}
+
+	return rate;
+}
+
+static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
+			   unsigned long parent_rate)
+{
+	struct clk_fractional_divider *fd = to_clk_fd(hw);
+	unsigned long flags = 0;
+	unsigned long div;
+	unsigned n, m;
+	u32 val;
+
+	div = gcd(parent_rate, rate);
+	m = rate / div;
+	n = parent_rate / div;
+
+	if (fd->lock)
+		spin_lock_irqsave(fd->lock, flags);
+
+	val = clk_readl(fd->reg);
+	val &= ~(fd->mmask | fd->nmask);
+	val |= (m << fd->mshift) | (n << fd->nshift);
+	clk_writel(val, fd->reg);
+
+	if (fd->lock)
+		spin_unlock_irqrestore(fd->lock, flags);
+
+	return 0;
+}
+
+const struct clk_ops clk_fractional_divider_ops = {
+	.recalc_rate = clk_fd_recalc_rate,
+	.round_rate = clk_fd_round_rate,
+	.set_rate = clk_fd_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fractional_divider_ops);
+
+struct clk *clk_register_fractional_divider(struct device *dev,
+		const char *name, const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
+		u8 clk_divider_flags, spinlock_t *lock)
+{
+	struct clk_fractional_divider *fd;
+	struct clk_init_data init;
+	struct clk *clk;
+
+	fd = kzalloc(sizeof(*fd), GFP_KERNEL);
+	if (!fd) {
+		pr_err("%s: could not allocate fractional divider clk\n",
+		       __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	init.name = name;
+	init.ops = &clk_fractional_divider_ops;
+	init.flags = flags | CLK_IS_BASIC;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	fd->reg = reg;
+	fd->mshift = mshift;
+	fd->mmask = (BIT(mwidth) - 1) << mshift;
+	fd->nshift = nshift;
+	fd->nmask = (BIT(nwidth) - 1) << nshift;
+	fd->flags = clk_divider_flags;
+	fd->lock = lock;
+	fd->hw.init = &init;
+
+	clk = clk_register(dev, &fd->hw);
+	if (IS_ERR(clk))
+		kfree(fd);
+
+	return clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_fractional_divider);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 47e353d..30d1e02 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -416,6 +416,37 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
 		const char *parent_name, unsigned long flags,
 		unsigned int mult, unsigned int div);
 
+/**
+ * struct clk_fractional_divider - adjustable fractional divider clock
+ *
+ * @hw:		handle between common and hardware-specific interfaces
+ * @reg:	register containing the divider
+ * @mshift:	shift to the numerator bit field
+ * @mwidth:	width of the numerator bit field
+ * @nshift:	shift to the denominator bit field
+ * @nwidth:	width of the denominator bit field
+ * @lock:	register lock
+ *
+ * Clock with adjustable fractional divider affecting its output frequency.
+ */
+
+struct clk_fractional_divider {
+	struct clk_hw	hw;
+	void __iomem	*reg;
+	u8		mshift;
+	u32		mmask;
+	u8		nshift;
+	u32		nmask;
+	u8		flags;
+	spinlock_t	*lock;
+};
+
+extern const struct clk_ops clk_fractional_divider_ops;
+struct clk *clk_register_fractional_divider(struct device *dev,
+		const char *name, const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
+		u8 clk_divider_flags, spinlock_t *lock);
+
 /***
  * struct clk_composite - aggregate clock of mux, divider and gate clocks
  *
-- 
2.0.0.rc2

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] ACPI / LPSS: support for fractional divider clock
  2014-05-13 12:13 [PATCH 0/4] ACPI / LPSS: Solution for two issues seen on Asus T100 Heikki Krogerus
                   ` (2 preceding siblings ...)
  2014-05-13 12:13 ` [PATCH 3/4] clk: new basic clk type for fractional divider Heikki Krogerus
@ 2014-05-13 12:13 ` Heikki Krogerus
  3 siblings, 0 replies; 6+ messages in thread
From: Heikki Krogerus @ 2014-05-13 12:13 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Mike Turquette, Mika Westerberg, Jin Yao, Li Aubrey,
	Andy Shevchenko, linux-acpi, linux-kernel

LPSS platforms offer the M/N divider clock for most devices.
This creates fractional divider type clock for the ones that
have it. It is needed with UART as the clock rate must
accommodate to the requested baud rate.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/acpi/acpi_lpss.c | 63 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 42 insertions(+), 21 deletions(-)

diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 24e49a5..3c7b97d 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -29,6 +29,7 @@ ACPI_MODULE_NAME("acpi_lpss");
 #define LPSS_LTR_SIZE	0x18
 
 /* Offsets relative to LPSS_PRIVATE_OFFSET */
+#define LPSS_CLK_DIVIDER_DEF_MASK	(BIT(1) | BIT(16))
 #define LPSS_GENERAL			0x08
 #define LPSS_GENERAL_LTR_MODE_SW	BIT(2)
 #define LPSS_GENERAL_UART_RTS_OVRD	BIT(3)
@@ -60,6 +61,7 @@ struct lpss_device_desc {
 	bool ltr_required;
 	unsigned int prv_offset;
 	size_t prv_size_override;
+	bool clk_divider;
 	bool clk_gate;
 	bool save_ctx;
 	struct lpss_shared_clock *shared_clock;
@@ -97,6 +99,14 @@ static struct lpss_device_desc lpt_dev_desc = {
 	.clk_required = true,
 	.prv_offset = 0x800,
 	.ltr_required = true,
+	.clk_divider = true,
+	.clk_gate = true,
+};
+
+static struct lpss_device_desc lpt_i2c_dev_desc = {
+	.clk_required = true,
+	.prv_offset = 0x800,
+	.ltr_required = true,
 	.clk_gate = true,
 };
 
@@ -104,6 +114,7 @@ static struct lpss_device_desc lpt_uart_dev_desc = {
 	.clk_required = true,
 	.prv_offset = 0x800,
 	.ltr_required = true,
+	.clk_divider = true,
 	.clk_gate = true,
 	.setup = lpss_uart_setup,
 };
@@ -125,31 +136,21 @@ static struct lpss_device_desc byt_pwm_dev_desc = {
 	.shared_clock = &pwm_clock,
 };
 
-static struct lpss_shared_clock uart_clock = {
-	.name = "uart_clk",
-	.rate = 44236800,
-};
-
 static struct lpss_device_desc byt_uart_dev_desc = {
 	.clk_required = true,
 	.prv_offset = 0x800,
+	.clk_divider = true,
 	.clk_gate = true,
 	.save_ctx = true,
-	.shared_clock = &uart_clock,
 	.setup = lpss_uart_setup,
 };
 
-static struct lpss_shared_clock spi_clock = {
-	.name = "spi_clk",
-	.rate = 50000000,
-};
-
 static struct lpss_device_desc byt_spi_dev_desc = {
 	.clk_required = true,
 	.prv_offset = 0x400,
+	.clk_divider = true,
 	.clk_gate = true,
 	.save_ctx = true,
-	.shared_clock = &spi_clock,
 };
 
 static struct lpss_device_desc byt_sdio_dev_desc = {
@@ -175,8 +176,8 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
 	/* Lynxpoint LPSS devices */
 	{ "INT33C0", (unsigned long)&lpt_dev_desc },
 	{ "INT33C1", (unsigned long)&lpt_dev_desc },
-	{ "INT33C2", (unsigned long)&lpt_dev_desc },
-	{ "INT33C3", (unsigned long)&lpt_dev_desc },
+	{ "INT33C2", (unsigned long)&lpt_i2c_dev_desc },
+	{ "INT33C3", (unsigned long)&lpt_i2c_dev_desc },
 	{ "INT33C4", (unsigned long)&lpt_uart_dev_desc },
 	{ "INT33C5", (unsigned long)&lpt_uart_dev_desc },
 	{ "INT33C6", (unsigned long)&lpt_sdio_dev_desc },
@@ -221,9 +222,11 @@ static int register_device_clock(struct acpi_device *adev,
 {
 	const struct lpss_device_desc *dev_desc = pdata->dev_desc;
 	struct lpss_shared_clock *shared_clock = dev_desc->shared_clock;
+	const char *devname = dev_name(&adev->dev);
 	struct clk *clk = ERR_PTR(-ENODEV);
 	struct lpss_clk_data *clk_data;
-	const char *parent;
+	const char *parent, *clk_name;
+	void __iomem *prv_base;
 
 	if (!lpss_clk_dev)
 		lpt_register_clock_device();
@@ -234,7 +237,7 @@ static int register_device_clock(struct acpi_device *adev,
 
 	if (dev_desc->clkdev_name) {
 		clk_register_clkdev(clk_data->clk, dev_desc->clkdev_name,
-				    dev_name(&adev->dev));
+				    devname);
 		return 0;
 	}
 
@@ -243,6 +246,7 @@ static int register_device_clock(struct acpi_device *adev,
 		return -ENODATA;
 
 	parent = clk_data->name;
+	prv_base = pdata->mmio_base + dev_desc->prv_offset;
 
 	if (shared_clock) {
 		clk = shared_clock->clk;
@@ -256,16 +260,33 @@ static int register_device_clock(struct acpi_device *adev,
 	}
 
 	if (dev_desc->clk_gate) {
-		clk = clk_register_gate(NULL, dev_name(&adev->dev), parent, 0,
-					pdata->mmio_base + dev_desc->prv_offset,
-					0, 0, NULL);
-		pdata->clk = clk;
+		clk = clk_register_gate(NULL, devname, parent, 0,
+					prv_base, 0, 0, NULL);
+		parent = devname;
+	}
+
+	if (dev_desc->clk_divider) {
+		/* Prevent division by zero */
+		if (!readl(prv_base))
+			writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base);
+
+		clk_name = kasprintf(GFP_KERNEL, "%s-div", devname);
+		clk = clk_register_fractional_divider(NULL, clk_name, parent,
+						      0, prv_base,
+						      1, 15, 16, 15, 0, NULL);
+		parent = clk_name;
+
+		clk_name = kasprintf(GFP_KERNEL, "%s-update", devname);
+		clk = clk_register_gate(NULL, clk_name, parent,
+					CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
+					prv_base, 31, 0, NULL);
 	}
 
 	if (IS_ERR(clk))
 		return PTR_ERR(clk);
 
-	clk_register_clkdev(clk, NULL, dev_name(&adev->dev));
+	pdata->clk = clk;
+	clk_register_clkdev(clk, NULL, devname);
 	return 0;
 }
 
-- 
2.0.0.rc2

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 3/4] clk: new basic clk type for fractional divider
  2014-05-13 12:13 ` [PATCH 3/4] clk: new basic clk type for fractional divider Heikki Krogerus
@ 2014-05-14 13:55   ` Heikki Krogerus
  0 siblings, 0 replies; 6+ messages in thread
From: Heikki Krogerus @ 2014-05-14 13:55 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Mike Turquette, Mika Westerberg, Jin Yao, Li Aubrey,
	Andy Shevchenko, linux-acpi, linux-kernel

On Tue, May 13, 2014 at 03:13:52PM +0300, Heikki Krogerus wrote:
> +static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
> +					unsigned long parent_rate)
> +{
> +	struct clk_fractional_divider *fd = to_clk_fd(hw);
> +	unsigned long flags = 0;
> +	u64 n, m;
> +	u32 val;
> +
> +	if (fd->lock)
> +		spin_lock_irqsave(fd->lock, flags);
> +
> +	val = clk_readl(fd->reg);
> +
> +	if (fd->lock)
> +		spin_unlock_irqrestore(fd->lock, flags);
> +
> +	m = (val & fd->mmask) >> fd->mshift;
> +	n = (val & fd->nmask) >> fd->nshift;
> +
> +	return parent_rate * m / n;
> +}

Andy noticed an issue here but he commented it outside the list. I
need to use do_div() here. He had also some nitpicks for the other
patches, so I'll prepare a new version for all of these tomorrow.


-- 
heikki

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2014-05-14 13:55 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-05-13 12:13 [PATCH 0/4] ACPI / LPSS: Solution for two issues seen on Asus T100 Heikki Krogerus
2014-05-13 12:13 ` [PATCH 1/4] ACPI / PM: Export rest of the subsys functions Heikki Krogerus
2014-05-13 12:13 ` [PATCH 2/4] ACPI / LPSS: custom power domain for LPSS Heikki Krogerus
2014-05-13 12:13 ` [PATCH 3/4] clk: new basic clk type for fractional divider Heikki Krogerus
2014-05-14 13:55   ` Heikki Krogerus
2014-05-13 12:13 ` [PATCH 4/4] ACPI / LPSS: support for fractional divider clock Heikki Krogerus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).