summaryrefslogtreecommitdiffstats
path: root/kernel/stop_machine.c
AgeCommit message (Expand)Author
2011-11-06Merge branch 'modsplit-Oct31_2011' of git://git.kernel.org/pub/scm/linux/kern...Linus Torvalds
2011-10-31stop_machine: make stop_machine safe and efficient to call earlyJeremy Fitzhardinge
2011-10-31kernel: Map most files to use export.h instead of module.hPaul Gortmaker
2011-07-26atomic: use <linux/atomic.h>Arun Sharma
2011-06-27x86, mtrr: use stop_machine APIs for doing MTRR rendezvousSuresh Siddha
2011-06-27stop_machine: implement stop_machine_from_inactive_cpu()Tejun Heo
2011-06-27stop_machine: reorganize stop_cpus() implementationTejun Heo
2011-06-27x86, mtrr: lock stop machine during MTRR rendezvous sequenceSuresh Siddha
2011-03-22kthread: use kthread_create_on_node()Eric Dumazet
2010-10-26stop_machine: convert cpu notifier to return encapsulate errno valueAkinobu Mita
2010-10-26kernel/stop_machine.c: fix unused variable warningRakib Mullick
2010-10-18sched: Create special class for stop/migrate workPeter Zijlstra
2010-08-09stop_machine: struct cpu_stopper, remove alignment padding on 64 bitsRichard Kennedy
2010-05-31sched: Make sure timers have migrated before killing the migration_threadAmit K. Arora
2010-05-18stop_machine: Move local variable closer to the usage site in cpu_stop_cpu_ca...Ingo Molnar
2010-05-08cpu_stop: add dummy implementation for UPTejun Heo
2010-05-06sched: replace migration_thread with cpu_stopTejun Heo
2010-05-06stop_machine: reimplement using cpu_stopTejun Heo
2010-05-06cpu_stop: implement stop_cpu[s]()Tejun Heo
2010-02-17percpu: add __percpu sparse annotations to core kernel subsystemsTejun Heo
2009-03-30cpumask: remove cpumask_t from coreRusty Russell
2009-02-20alloc_percpu: change percpu_ptr to per_cpu_ptrRusty Russell
2009-01-05stop_machine: introduce stop_machine_create/destroy.Heiko Carstens
2009-01-01cpumask: convert rest of files in kernel/Rusty Russell
2008-11-16stop_machine: fix race with return value (fixes Bug #11989)Rusty Russell
2008-10-25Revert "Call init_workqueues before pre smp initcalls."Linus Torvalds
2008-10-22stop_machine: fix error code handling on multiple cpusHeiko Carstens
2008-10-22stop_machine: use workqueues instead of kernel threadsHeiko Carstens
2008-08-12stop_machine: remove unused variableLi Zefan
2008-07-28stop_machine(): stop_machine_run() changed to use cpu maskRusty Russell
2008-07-28Simplify stop_machineRusty Russell
2008-07-28stop_machine: add ALL_CPUS optionJason Baron
2008-07-18cpumask: Replace cpumask_of_cpu with cpumask_of_cpu_ptrMike Travis
2008-06-23sched: add new API sched_setscheduler_nocheck: add a flag to control access c...Rusty Russell
2008-05-23stop_machine: make stop_machine_run more virtualization friendlyChristian Borntraeger
2008-04-21Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/juh...Linus Torvalds
2008-04-21Merge branch 'semaphore' of git://git.kernel.org/pub/scm/linux/kernel/git/wil...Linus Torvalds
2008-04-21trivial: small cleanupsPavel Machek
2008-04-19generic: use new set_cpus_allowed_ptr functionMike Travis
2008-04-18kernel: Remove unnecessary inclusions of asm/semaphore.hMatthew Wilcox
2008-02-06stopmachine: semaphore to mutexDaniel Walker
2008-01-25cpu-hotplug: replace lock_cpu_hotplug() with get_online_cpus()Gautham R Shenoy
2007-07-16Fix stop_machine_run problem with naughty real time processSatoru Takeuchi
2007-05-11stop_machine() now uses hard_irq_disableBenjamin Herrenschmidt
2007-05-08Use stop_machine_run in the Intel RNG driverPrarit Bhargava
2006-09-29[PATCH] stop_machine.c copyrightRusty Russell
2006-08-27[PATCH] Remove redundant up() in stop_machine()Yingchao Zhou
2006-07-03[PATCH] revert "kthread: convert stop_machine into a kthread"Andrew Morton
2006-06-25[PATCH] kthread: convert stop_machine into a kthreadSerge E. Hallyn
2006-01-10[PATCH] Remove set_fs() in stop_machine()akpm@osdl.org
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Generic Exynos Bus frequency driver with DEVFREQ Framework
 *
 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
 * Author : Chanwoo Choi <cw00.choi@samsung.com>
 *
 * This driver support Exynos Bus frequency feature by using
 * DEVFREQ framework and is based on drivers/devfreq/exynos/exynos4_bus.c.
 */

#include <linux/clk.h>
#include <linux/devfreq.h>
#include <linux/devfreq-event.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>

#define DEFAULT_SATURATION_RATIO	40
#define DEFAULT_VOLTAGE_TOLERANCE	2

struct exynos_bus {
	struct device *dev;

	struct devfreq *devfreq;
	struct devfreq_event_dev **edev;
	unsigned int edev_count;
	struct mutex lock;

	unsigned long curr_freq;

	struct regulator *regulator;
	struct clk *clk;
	unsigned int voltage_tolerance;
	unsigned int ratio;
};

/*
 * Control the devfreq-event device to get the current state of bus
 */
#define exynos_bus_ops_edev(ops)				\
static int exynos_bus_##ops(struct exynos_bus *bus)		\
{								\
	int i, ret;						\
								\
	for (i = 0; i < bus->edev_count; i++) {			\
		if (!bus->edev[i])				\
			continue;				\
		ret = devfreq_event_##ops(bus->edev[i]);	\
		if (ret < 0)					\
			return ret;				\
	}							\
								\
	return 0;						\
}
exynos_bus_ops_edev(enable_edev);
exynos_bus_ops_edev(disable_edev);
exynos_bus_ops_edev(set_event);

static int exynos_bus_get_event(struct exynos_bus *bus,
				struct devfreq_event_data *edata)
{
	struct devfreq_event_data event_data;
	unsigned long load_count = 0, total_count = 0;
	int i, ret = 0;

	for (i = 0; i < bus->edev_count; i++) {
		if (!bus->edev[i])
			continue;

		ret = devfreq_event_get_event(bus->edev[i], &event_data);
		if (ret < 0)
			return ret;

		if (i == 0 || event_data.load_count > load_count) {
			load_count = event_data.load_count;
			total_count = event_data.total_count;
		}
	}

	edata->load_count = load_count;
	edata->total_count = total_count;

	return ret;
}

/*
 * Must necessary function for devfreq simple-ondemand governor
 */
static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
{
	struct exynos_bus *bus = dev_get_drvdata(dev);
	struct dev_pm_opp *new_opp;
	unsigned long old_freq, new_freq, new_volt, tol;
	int ret = 0;

	/* Get new opp-bus instance according to new bus clock */
	new_opp = devfreq_recommended_opp(dev, freq, flags);
	if (IS_ERR(new_opp)) {
		dev_err(dev, "failed to get recommended opp instance\n");
		return PTR_ERR(new_opp);
	}

	new_freq = dev_pm_opp_get_freq(new_opp);
	new_volt = dev_pm_opp_get_voltage(new_opp);
	dev_pm_opp_put(new_opp);

	old_freq = bus->curr_freq;

	if (old_freq == new_freq)
		return 0;
	tol = new_volt * bus->voltage_tolerance / 100;

	/* Change voltage and frequency according to new OPP level */
	mutex_lock(&bus->lock);

	if (old_freq < new_freq) {
		ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
		if (ret < 0) {
			dev_err(bus->dev, "failed to set voltage\n");
			goto out;
		}
	}

	ret = clk_set_rate(bus->clk, new_freq);
	if (ret < 0) {
		dev_err(dev, "failed to change clock of bus\n");
		clk_set_rate(bus->clk, old_freq);
		goto out;
	}

	if (old_freq > new_freq) {
		ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
		if (ret < 0) {
			dev_err(bus->dev, "failed to set voltage\n");
			goto out;
		}
	}
	bus->curr_freq = new_freq;

	dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
			old_freq, new_freq, clk_get_rate(bus->clk));
out:
	mutex_unlock(&bus->lock);

	return ret;
}

static int exynos_bus_get_dev_status(struct device *dev,
				     struct devfreq_dev_status *stat)
{
	struct exynos_bus *bus = dev_get_drvdata(dev);
	struct devfreq_event_data edata;
	int ret;

	stat->current_frequency = bus->curr_freq;

	ret = exynos_bus_get_event(bus, &edata);
	if (ret < 0) {
		stat->total_time = stat->busy_time = 0;
		goto err;
	}

	stat->busy_time = (edata.load_count * 100) / bus->ratio;
	stat->total_time = edata.total_count;

	dev_dbg(dev, "Usage of devfreq-event : %lu/%lu\n", stat->busy_time,
							stat->total_time);

err:
	ret = exynos_bus_set_event(bus);
	if (ret < 0) {
		dev_err(dev, "failed to set event to devfreq-event devices\n");
		return ret;
	}

	return ret;
}

static void exynos_bus_exit(struct device *dev)
{
	struct exynos_bus *bus = dev_get_drvdata(dev);
	int ret;

	ret = exynos_bus_disable_edev(bus);
	if (ret < 0)
		dev_warn(dev, "failed to disable the devfreq-event devices\n");

	if (bus->regulator)
		regulator_disable(bus->regulator);

	dev_pm_opp_of_remove_table(dev);
	clk_disable_unprepare(bus->clk);
}

/*
 * Must necessary function for devfreq passive governor
 */
static int exynos_bus_passive_target(struct device *dev, unsigned long *freq,
					u32 flags)
{
	struct exynos_bus *bus = dev_get_drvdata(dev);
	struct dev_pm_opp *new_opp;
	unsigned long old_freq, new_freq;
	int ret = 0;

	/* Get new opp-bus instance according to new bus clock */
	new_opp = devfreq_recommended_opp(dev, freq, flags);
	if (IS_ERR(new_opp)) {
		dev_err(dev, "failed to get recommended opp instance\n");
		return PTR_ERR(new_opp);
	}

	new_freq = dev_pm_opp_get_freq(new_opp);
	dev_pm_opp_put(new_opp);

	old_freq = bus->curr_freq;

	if (old_freq == new_freq)
		return 0;

	/* Change the frequency according to new OPP level */
	mutex_lock(&bus->lock);

	ret = clk_set_rate(bus->clk, new_freq);
	if (ret < 0) {
		dev_err(dev, "failed to set the clock of bus\n");
		goto out;
	}

	*freq = new_freq;
	bus->curr_freq = new_freq;

	dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
			old_freq, new_freq, clk_get_rate(bus->clk));
out:
	mutex_unlock(&bus->lock);

	return ret;
}

static void<