@@ -79,6 +79,7 @@ config ARM_EXYNOS_BUS_DEVFREQ
bool "ARM EXYNOS Generic Memory Bus DEVFREQ Driver"
depends on ARCH_EXYNOS
select DEVFREQ_GOV_SIMPLE_ONDEMAND
+ select DEVFREQ_GOV_PASSIVE
select DEVFREQ_EVENT_EXYNOS_PPMU
select PM_DEVFREQ_EVENT
select PM_OPP
@@ -91,7 +91,7 @@ static int exynos_bus_get_event(struct exynos_bus *bus,
}
/*
- * Must necessary function for devfreq governor
+ * Must necessary function for devfreq simple-ondemand governor
*/
static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
{
@@ -205,57 +205,74 @@ static void exynos_bus_exit(struct device *dev)
dev_pm_opp_of_remove_table(dev);
}
-static int exynos_bus_parse_of(struct device_node *np,
- struct exynos_bus *bus)
+/*
+ * Must necessary function for devfreq passive governor
+ */
+static int exynos_bus_passive_target(struct device *dev, unsigned long *freq,
+ u32 flags)
{
- struct device *dev = bus->dev;
- unsigned long rate;
- int i, ret, count, size;
+ struct exynos_bus *bus = dev_get_drvdata(dev);
+ struct dev_pm_opp *new_opp;
+ unsigned long old_freq, new_freq;
+ int ret = 0;
- /* Get the clock to provide each bus with source clock */
- bus->clk = devm_clk_get(dev, "bus");
- if (IS_ERR(bus->clk)) {
- dev_err(dev, "failed to get bus clock\n");
- return PTR_ERR(bus->clk);
+ /* Get new opp-bus instance according to new bus clock */
+ rcu_read_lock();
+ new_opp = devfreq_recommended_opp(dev, freq, flags);
+ if (IS_ERR_OR_NULL(new_opp)) {
+ dev_err(dev, "failed to get recommed opp instance\n");
+ rcu_read_unlock();
+ return PTR_ERR(new_opp);
}
- ret = clk_prepare_enable(bus->clk);
- if (ret < 0) {
- dev_err(dev, "failed to get enable clock\n");
- return ret;
- }
+ new_freq = dev_pm_opp_get_freq(new_opp);
+ old_freq = dev_pm_opp_get_freq(bus->curr_opp);
+ rcu_read_unlock();
- /* Get the freq/voltage OPP table to scale the bus frequency */
- rcu_read_lock();
- ret = dev_pm_opp_of_add_table(dev);
+ 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 get OPP table\n");
- rcu_read_unlock();
- return ret;
+ dev_err(dev, "failed to set the clock of bus\n");
+ goto out;
}
- rate = clk_get_rate(bus->clk);
- bus->curr_opp = dev_pm_opp_find_freq_ceil(dev, &rate);
- if (IS_ERR(bus->curr_opp)) {
- dev_err(dev, "failed to find dev_pm_opp\n");
- rcu_read_unlock();
- ret = PTR_ERR(bus->curr_opp);
- goto err_opp;
- }
- rcu_read_unlock();
+ bus->curr_opp = new_opp;
+
+ dev_dbg(dev, "Set the frequency of bus (%ldkHz -> %ldkHz)\n",
+ old_freq/1000, new_freq/1000);
+out:
+ mutex_unlock(&bus->lock);
+
+ return ret;
+}
+
+static void exynos_bus_passive_exit(struct device *dev)
+{
+ dev_pm_opp_of_remove_table(dev);
+}
+
+static int exynos_bus_parent_parse_of(struct device_node *np,
+ struct exynos_bus *bus)
+{
+ struct device *dev = bus->dev;
+ int i, ret, count, size;
/* Get the regulator to provide each bus with the power */
bus->regulator = devm_regulator_get(dev, "vdd");
if (IS_ERR(bus->regulator)) {
dev_err(dev, "failed to get VDD regulator\n");
- ret = PTR_ERR(bus->regulator);
- goto err_opp;
+ return PTR_ERR(bus->regulator);
}
ret = regulator_enable(bus->regulator);
if (ret < 0) {
dev_err(dev, "failed to enable VDD regulator\n");
- goto err_opp;
+ return ret;
}
/*
@@ -302,6 +319,51 @@ static int exynos_bus_parse_of(struct device_node *np,
err_regulator:
regulator_disable(bus->regulator);
+
+ return ret;
+}
+
+static int exynos_bus_parse_of(struct device_node *np,
+ struct exynos_bus *bus)
+{
+ struct device *dev = bus->dev;
+ unsigned long rate;
+ int ret;
+
+ /* Get the clock to provide each bus with source clock */
+ bus->clk = devm_clk_get(dev, "bus");
+ if (IS_ERR(bus->clk)) {
+ dev_err(dev, "failed to get bus clock\n");
+ return PTR_ERR(bus->clk);
+ }
+
+ ret = clk_prepare_enable(bus->clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to get enable clock\n");
+ return ret;
+ }
+
+ /* Get the freq and voltage from OPP table to scale the bus freq */
+ rcu_read_lock();
+ ret = dev_pm_opp_of_add_table(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to get OPP table\n");
+ rcu_read_unlock();
+ return ret;
+ }
+
+ rate = clk_get_rate(bus->clk);
+ bus->curr_opp = dev_pm_opp_find_freq_ceil(dev, &rate);
+ if (IS_ERR(bus->curr_opp)) {
+ dev_err(dev, "failed to find dev_pm_opp\n");
+ rcu_read_unlock();
+ ret = PTR_ERR(bus->curr_opp);
+ goto err_opp;
+ }
+ rcu_read_unlock();
+
+ return 0;
+
err_opp:
dev_pm_opp_of_remove_table(dev);
@@ -314,8 +376,11 @@ static int exynos_bus_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct devfreq_dev_profile *profile;
struct devfreq_simple_ondemand_data *ondemand_data;
+ struct devfreq_passive_data *passive_data;
+ struct devfreq *parent_devfreq;
struct exynos_bus *bus;
- int ret;
+ int ret, max_state;
+ unsigned long min_freq, max_freq;
if (!np) {
dev_err(dev, "failed to find devicetree node\n");
@@ -334,10 +399,19 @@ static int exynos_bus_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- /* Initalize the struct profile and governor data */
profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL);
if (!profile)
return -ENOMEM;
+
+ if (of_parse_phandle(dev->of_node, "devfreq", 0))
+ goto passive;
+ else
+ ret = exynos_bus_parent_parse_of(np, bus);
+
+ if (ret < 0)
+ return ret;
+
+ /* Initalize the struct profile and governor data for parent device */
profile->polling_ms = 50;
profile->target = exynos_bus_target;
profile->get_dev_status = exynos_bus_get_dev_status;
@@ -380,6 +454,39 @@ static int exynos_bus_probe(struct platform_device *pdev)
return ret;
}
+ goto out;
+passive:
+ /* Initalize the struct profile and governor data for passive device */
+ profile->target = exynos_bus_passive_target;
+ profile->exit = exynos_bus_passive_exit;
+
+ passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL);
+ if (!passive_data)
+ return -ENOMEM;
+
+ /* Get the instance of parent devfreq device */
+ parent_devfreq = devfreq_get_devfreq_by_phandle(dev, 0);
+ if (IS_ERR(parent_devfreq)) {
+ return -EPROBE_DEFER;
+ }
+ passive_data->parent = parent_devfreq;
+
+ /* Add devfreq device for exynos bus with passive governor */
+ bus->devfreq = devm_devfreq_add_device(dev, profile, "passive",
+ passive_data);
+ if (IS_ERR_OR_NULL(bus->devfreq)) {
+ dev_err(dev,
+ "failed to add devfreq dev with passive governor\n");
+ return -EPROBE_DEFER;
+ }
+
+out:
+ max_state = bus->devfreq->profile->max_state;
+ min_freq = (bus->devfreq->profile->freq_table[0] / 1000);
+ max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000);
+ pr_info("exynos-bus: new bus device registered: %s (%6ld KHz ~ %6ld KHz)\n",
+ dev_name(dev), min_freq, max_freq);
+
return 0;
}