Commit f7867cff authored by Daniel Lezcano's avatar Daniel Lezcano
Browse files

Merge branch 'timers/drivers/timer-ti-dm' into timers/drivers/next

parents 05852445 02e6d546
Loading
Loading
Loading
Loading
+109 −74
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0+
/*
 * linux/arch/arm/plat-omap/dmtimer.c
 *
@@ -15,28 +16,11 @@
 *
 * Copyright (C) 2009 Texas Instruments
 * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.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.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * 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.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/cpu_pm.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/device.h>
@@ -109,6 +93,47 @@ static void omap_timer_restore_context(struct omap_dm_timer *timer)
				timer->context.tclr);
}

static void omap_timer_save_context(struct omap_dm_timer *timer)
{
	timer->context.tclr =
			omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
	timer->context.twer =
			omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG);
	timer->context.tldr =
			omap_dm_timer_read_reg(timer, OMAP_TIMER_LOAD_REG);
	timer->context.tmar =
			omap_dm_timer_read_reg(timer, OMAP_TIMER_MATCH_REG);
	timer->context.tier = readl_relaxed(timer->irq_ena);
	timer->context.tsicr =
			omap_dm_timer_read_reg(timer, OMAP_TIMER_IF_CTRL_REG);
}

static int omap_timer_context_notifier(struct notifier_block *nb,
				       unsigned long cmd, void *v)
{
	struct omap_dm_timer *timer;

	timer = container_of(nb, struct omap_dm_timer, nb);

	switch (cmd) {
	case CPU_CLUSTER_PM_ENTER:
		if ((timer->capability & OMAP_TIMER_ALWON) ||
		    !atomic_read(&timer->enabled))
			break;
		omap_timer_save_context(timer);
		break;
	case CPU_CLUSTER_PM_ENTER_FAILED:
	case CPU_CLUSTER_PM_EXIT:
		if ((timer->capability & OMAP_TIMER_ALWON) ||
		    !atomic_read(&timer->enabled))
			break;
		omap_timer_restore_context(timer);
		break;
	}

	return NOTIFY_OK;
}

static int omap_dm_timer_reset(struct omap_dm_timer *timer)
{
	u32 l, timeout = 100000;
@@ -196,21 +221,7 @@ static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)

static void omap_dm_timer_enable(struct omap_dm_timer *timer)
{
	int c;

	pm_runtime_get_sync(&timer->pdev->dev);

	if (!(timer->capability & OMAP_TIMER_ALWON)) {
		if (timer->get_context_loss_count) {
			c = timer->get_context_loss_count(&timer->pdev->dev);
			if (c != timer->ctx_loss_count) {
				omap_timer_restore_context(timer);
				timer->ctx_loss_count = c;
			}
		} else {
			omap_timer_restore_context(timer);
		}
	}
}

static void omap_dm_timer_disable(struct omap_dm_timer *timer)
@@ -477,7 +488,7 @@ __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)

int omap_dm_timer_trigger(struct omap_dm_timer *timer)
{
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
	if (unlikely(!timer || !atomic_read(&timer->enabled))) {
		pr_err("%s: timer not available or enabled.\n", __func__);
		return -EINVAL;
	}
@@ -501,8 +512,6 @@ static int omap_dm_timer_start(struct omap_dm_timer *timer)
		omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
	}

	/* Save the context */
	timer->context.tclr = l;
	return 0;
}

@@ -518,37 +527,19 @@ static int omap_dm_timer_stop(struct omap_dm_timer *timer)

	__omap_dm_timer_stop(timer, timer->posted, rate);

	/*
	 * Since the register values are computed and written within
	 * __omap_dm_timer_stop, we need to use read to retrieve the
	 * context.
	 */
	timer->context.tclr =
			omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
	omap_dm_timer_disable(timer);
	return 0;
}

static int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
static int omap_dm_timer_set_load(struct omap_dm_timer *timer,
				  unsigned int load)
{
	u32 l;

	if (unlikely(!timer))
		return -EINVAL;

	omap_dm_timer_enable(timer);
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
	if (autoreload)
		l |= OMAP_TIMER_CTRL_AR;
	else
		l &= ~OMAP_TIMER_CTRL_AR;
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
	omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);

	/* Save the context */
	timer->context.tclr = l;
	timer->context.tldr = load;
	omap_dm_timer_disable(timer);
	return 0;
}
@@ -570,15 +561,12 @@ static int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
	omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);

	/* Save the context */
	timer->context.tclr = l;
	timer->context.tmar = match;
	omap_dm_timer_disable(timer);
	return 0;
}

static int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
				 int toggle, int trigger)
				 int toggle, int trigger, int autoreload)
{
	u32 l;

@@ -588,20 +576,34 @@ static int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
	omap_dm_timer_enable(timer);
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
	l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
	       OMAP_TIMER_CTRL_PT | (0x03 << 10));
	       OMAP_TIMER_CTRL_PT | (0x03 << 10) | OMAP_TIMER_CTRL_AR);
	if (def_on)
		l |= OMAP_TIMER_CTRL_SCPWM;
	if (toggle)
		l |= OMAP_TIMER_CTRL_PT;
	l |= trigger << 10;
	if (autoreload)
		l |= OMAP_TIMER_CTRL_AR;
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);

	/* Save the context */
	timer->context.tclr = l;
	omap_dm_timer_disable(timer);
	return 0;
}

static int omap_dm_timer_get_pwm_status(struct omap_dm_timer *timer)
{
	u32 l;

	if (unlikely(!timer))
		return -EINVAL;

	omap_dm_timer_enable(timer);
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
	omap_dm_timer_disable(timer);

	return l;
}

static int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer,
					int prescaler)
{
@@ -619,8 +621,6 @@ static int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer,
	}
	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);

	/* Save the context */
	timer->context.tclr = l;
	omap_dm_timer_disable(timer);
	return 0;
}
@@ -634,9 +634,6 @@ static int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
	omap_dm_timer_enable(timer);
	__omap_dm_timer_int_enable(timer, value);

	/* Save the context */
	timer->context.tier = value;
	timer->context.twer = value;
	omap_dm_timer_disable(timer);
	return 0;
}
@@ -664,9 +661,6 @@ static int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask)
	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask;
	omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l);

	/* Save the context */
	timer->context.tier &= ~mask;
	timer->context.twer &= ~mask;
	omap_dm_timer_disable(timer);
	return 0;
}
@@ -675,7 +669,7 @@ static unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
{
	unsigned int l;

	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
	if (unlikely(!timer || !atomic_read(&timer->enabled))) {
		pr_err("%s: timer not available or enabled.\n", __func__);
		return 0;
	}
@@ -687,7 +681,7 @@ static unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)

static int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
{
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev)))
	if (unlikely(!timer || !atomic_read(&timer->enabled)))
		return -EINVAL;

	__omap_dm_timer_write_status(timer, value);
@@ -697,7 +691,7 @@ static int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int

static unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
{
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
	if (unlikely(!timer || !atomic_read(&timer->enabled))) {
		pr_err("%s: timer not iavailable or enabled.\n", __func__);
		return 0;
	}
@@ -707,7 +701,7 @@ static unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)

static int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
{
	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) {
	if (unlikely(!timer || !atomic_read(&timer->enabled))) {
		pr_err("%s: timer not available or enabled.\n", __func__);
		return -EINVAL;
	}
@@ -735,6 +729,37 @@ int omap_dm_timers_active(void)
	return 0;
}

static int __maybe_unused omap_dm_timer_runtime_suspend(struct device *dev)
{
	struct omap_dm_timer *timer = dev_get_drvdata(dev);

	atomic_set(&timer->enabled, 0);

	if (timer->capability & OMAP_TIMER_ALWON || !timer->func_base)
		return 0;

	omap_timer_save_context(timer);

	return 0;
}

static int __maybe_unused omap_dm_timer_runtime_resume(struct device *dev)
{
	struct omap_dm_timer *timer = dev_get_drvdata(dev);

	if (!(timer->capability & OMAP_TIMER_ALWON) && timer->func_base)
		omap_timer_restore_context(timer);

	atomic_set(&timer->enabled, 1);

	return 0;
}

static const struct dev_pm_ops omap_dm_timer_pm_ops = {
	SET_RUNTIME_PM_OPS(omap_dm_timer_runtime_suspend,
			   omap_dm_timer_runtime_resume, NULL)
};

static const struct of_device_id omap_timer_match[];

/**
@@ -776,6 +801,8 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
	if (IS_ERR(timer->io_base))
		return PTR_ERR(timer->io_base);

	platform_set_drvdata(pdev, timer);

	if (dev->of_node) {
		if (of_find_property(dev->of_node, "ti,timer-alwon", NULL))
			timer->capability |= OMAP_TIMER_ALWON;
@@ -789,7 +816,11 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
		timer->id = pdev->id;
		timer->capability = pdata->timer_capability;
		timer->reserved = omap_dm_timer_reserved_systimer(timer->id);
		timer->get_context_loss_count = pdata->get_context_loss_count;
	}

	if (!(timer->capability & OMAP_TIMER_ALWON)) {
		timer->nb.notifier_call = omap_timer_context_notifier;
		cpu_pm_register_notifier(&timer->nb);
	}

	if (pdata)
@@ -843,6 +874,8 @@ static int omap_dm_timer_remove(struct platform_device *pdev)
	list_for_each_entry(timer, &omap_timer_list, node)
		if (!strcmp(dev_name(&timer->pdev->dev),
			    dev_name(&pdev->dev))) {
			if (!(timer->capability & OMAP_TIMER_ALWON))
				cpu_pm_unregister_notifier(&timer->nb);
			list_del(&timer->node);
			ret = 0;
			break;
@@ -871,6 +904,7 @@ static const struct omap_dm_timer_ops dmtimer_ops = {
	.set_load = omap_dm_timer_set_load,
	.set_match = omap_dm_timer_set_match,
	.set_pwm = omap_dm_timer_set_pwm,
	.get_pwm_status = omap_dm_timer_get_pwm_status,
	.set_prescaler = omap_dm_timer_set_prescaler,
	.read_counter = omap_dm_timer_read_counter,
	.write_counter = omap_dm_timer_write_counter,
@@ -921,6 +955,7 @@ static struct platform_driver omap_dm_timer_driver = {
	.driver = {
		.name   = "omap_timer",
		.of_match_table = of_match_ptr(omap_timer_match),
		.pm = &omap_dm_timer_pm_ops,
	},
};

+5 −3
Original line number Diff line number Diff line
@@ -183,7 +183,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
	if (timer_active)
		omap->pdata->stop(omap->dm_timer);

	omap->pdata->set_load(omap->dm_timer, true, load_value);
	omap->pdata->set_load(omap->dm_timer, load_value);
	omap->pdata->set_match(omap->dm_timer, true, match_value);

	dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n",
@@ -192,7 +192,8 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
	omap->pdata->set_pwm(omap->dm_timer,
			      pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED,
			      true,
			      PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE);
			      PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE,
			      true);

	/* If config was called while timer was running it must be reenabled. */
	if (timer_active)
@@ -222,7 +223,8 @@ static int pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip,
	omap->pdata->set_pwm(omap->dm_timer,
			      polarity == PWM_POLARITY_INVERSED,
			      true,
			      PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE);
			      PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE,
			      true);
	mutex_unlock(&omap->mutex);

	return 0;
+2 −2
Original line number Diff line number Diff line
@@ -105,17 +105,17 @@ struct omap_dm_timer {
	void __iomem	*pend;		/* write pending */
	void __iomem	*func_base;	/* function register base */

	atomic_t enabled;
	unsigned long rate;
	unsigned reserved:1;
	unsigned posted:1;
	struct timer_regs context;
	int (*get_context_loss_count)(struct device *);
	int ctx_loss_count;
	int revision;
	u32 capability;
	u32 errata;
	struct platform_device *pdev;
	struct list_head node;
	struct notifier_block nb;
};

int omap_dm_timer_reserve_systimer(int id);
+3 −3
Original line number Diff line number Diff line
@@ -30,12 +30,12 @@ struct omap_dm_timer_ops {
	int	(*stop)(struct omap_dm_timer *timer);
	int	(*set_source)(struct omap_dm_timer *timer, int source);

	int	(*set_load)(struct omap_dm_timer *timer, int autoreload,
			    unsigned int value);
	int	(*set_load)(struct omap_dm_timer *timer, unsigned int value);
	int	(*set_match)(struct omap_dm_timer *timer, int enable,
			     unsigned int match);
	int	(*set_pwm)(struct omap_dm_timer *timer, int def_on,
			   int toggle, int trigger);
			   int toggle, int trigger, int autoreload);
	int	(*get_pwm_status)(struct omap_dm_timer *timer);
	int	(*set_prescaler)(struct omap_dm_timer *timer, int prescaler);

	unsigned int (*read_counter)(struct omap_dm_timer *timer);