From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753508AbbLKHxI (ORCPT ); Fri, 11 Dec 2015 02:53:08 -0500 Received: from mailout3.samsung.com ([203.254.224.33]:45242 "EHLO mailout3.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751924AbbLKHxC (ORCPT ); Fri, 11 Dec 2015 02:53:02 -0500 MIME-version: 1.0 Content-type: text/plain; charset=UTF-8 X-AuditID: cbfee68e-f793c6d00000136c-89-566a80dbed6f Content-transfer-encoding: 8BIT Message-id: <566A80D5.2070703@samsung.com> Date: Fri, 11 Dec 2015 16:52:53 +0900 From: Chanwoo Choi User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.6.0 To: myungjoo.ham@samsung.com, k.kozlowski@samsung.com, kgene@kernel.org Cc: kyungmin.park@samsung.com, robh+dt@kernel.org, pawel.moll@arm.com, mark.rutland@arm.com, ijc+devicetree@hellion.org.uk, galak@codeaurora.org, linux@arm.linux.org.uk, tjakobi@math.uni-bielefeld.de, linux.amoon@gmail.com, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-samsung-soc@vger.kernel.org, devicetree@vger.kernel.org Subject: Re: [PATCH v3 01/20] PM / devfreq: exynos: Add generic exynos bus frequency driver References: <1449810479-14763-1-git-send-email-cw00.choi@samsung.com> <1449810479-14763-2-git-send-email-cw00.choi@samsung.com> In-reply-to: <1449810479-14763-2-git-send-email-cw00.choi@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrPIsWRmVeSWpSXmKPExsWyRsSkWPd2Q1aYwf91mhbzj5xjteh/s5DV 4tyrlYwWr18YWvQ/fs1scbbpDbvF5V1z2Cw+9x5htJhxfh+TxbqNt9gtbl/mtVh6/SKTxe3G FWwWE6avZbFo3XuE3aJt9QdWBwGPNfPWMHq0NPeweVzu62Xy2DnrLrvHyuVf2Dw2repk8/h3 jN2jb8sqRo/Pm+QCOKO4bFJSczLLUov07RK4Mh6+OstccLORseLXryvsDYxP07oYOTkkBEwk HjTNYYawxSQu3FvP1sXIxSEksIJR4trXfiaYoqX7m8BsIYGljBINZ5NBbF4BQYkfk++xdDFy cDALyEscuZQNEmYWUJeYNG8RM8ScB4wSdy/9YYSo15K4sXgDK4jNIqAqMeHrezYQmw0ovv/F DTaQOaICERLdJypBwiIC7hJf7+0Gu4dZ4C6TxMX3T8DqhQViJXbtWcIOcU8jo8SP/SkgNqeA m8TkvfegntnCIbGyJwFil4DEt8mHwO6UEJCV2HQAqkRS4uCKGywTGMVmIflmFsI3s5B8s4CR eRWjaGpBckFxUnqRkV5xYm5xaV66XnJ+7iZGYLyf/vesbwfjzQPWhxgFOBiVeHg92LLChFgT y4orcw8xmgIdMZFZSjQ5H5hU8kriDY3NjCxMTUyNjcwtzZTEeROkfgYLCaQnlqRmp6YWpBbF F5XmpBYfYmTi4JRqYJTffmDR+r2ey15ydGTqvrz5Lvbfh02s0n+n6uxb0D7ThIdv7ZIdc+dV 25jMvc52sq1y8oklj/kr/LMk67mj3v1dHpHeIWm01TS7cuZmhXIzzUaBR+szwzcefPPs+ItF 5iGv916wub3i2nfHkC9buaRrtZM2vf1XeUf7z2VLzevtW1+/fKN9hM9MiaU4I9FQi7moOBEA qGn8wPICAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrLKsWRmVeSWpSXmKPExsVy+t9jAd3bDVlhBnPeK1jMP3KO1aL/zUJW i3OvVjJavH5haNH/+DWzxdmmN+wWl3fNYbP43HuE0WLG+X1MFus23mK3uH2Z12Lp9YtMFrcb V7BZTJi+lsWide8Rdou21R9YHQQ81sxbw+jR0tzD5nG5r5fJY+esu+weK5d/YfPYtKqTzePf MXaPvi2rGD0+b5IL4IxqYLTJSE1MSS1SSM1Lzk/JzEu3VfIOjneONzUzMNQ1tLQwV1LIS8xN tVVy8QnQdcvMAfpCSaEsMacUKBSQWFyspG+HaUJoiJuuBUxjhK5vSBBcj5EBGkhYw5jx8NVZ 5oKbjYwVv35dYW9gfJrWxcjJISFgIrF0fxMThC0mceHeejYQW0hgKaNEw9lkEJtXQFDix+R7 LF2MHBzMAvISRy5lg4SZBdQlJs1bxNzFyAVU/oBR4u6lP4wQ9VoSNxZvYAWxWQRUJSZ8fQ82 kw0ovv/FDTaQOaICERLdJypBwiIC7hJf7+1mA5nDLHCXSeLi+ydg9cICsRK79ixhh7inkVHi x/4UEJtTwE1i8t57zBMYBWYhOW8WwnmzkJy3gJF5FaNEakFyQXFSeq5hXmq5XnFibnFpXrpe cn7uJkZwUnkmtYPx4C73Q4wCHIxKPLwLOLLChFgTy4orcw8xSnAwK4nw8qcChXhTEiurUovy 44tKc1KLDzGaAv03kVlKNDkfmPDySuINjU3MjCyNzA0tjIzNlcR5ay9FhgkJpCeWpGanphak FsH0MXFwSjUw8k3Nzt1+SOmKKpeDsfB8rss7hYqly++mpa1in2uy1jCArXrfBbO+hG/n/7r5 fHz2YMO2fQxTXkgu4K+viktTaJkyZVZ59K2FIlLFyzbm/N7L7v83pSy658oh44krJ3MvDTl0 10ez4/K06xImUz726J2S++BmpF+SvelE16aNfxPK589d+Kl2lxJLcUaioRZzUXEiANEl6ydA AwAA DLP-Filter: Pass X-MTR: 20000000000000000@CPGS X-CFilter-Loop: Reflected Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Dear MyungJoo, Almost device tree patches in this series are reviewed by Exynos maintainer. Could you please review this series? Best Regards, Chanwoo Choi On 2015년 12월 11일 14:07, Chanwoo Choi wrote: > This patch adds the generic exynos bus frequency driver for AMBA AXI bus > of sub-blocks in exynos SoC with DEVFREQ framework. The Samsung Exynos SoC > have the common architecture for bus between DRAM and sub-blocks in SoC. > This driver can support the generic bus frequency driver for Exynos SoCs. > > In devicetree, Each bus block has a bus clock, regulator, operation-point > and devfreq-event devices which measure the utilization of each bus block. > > Signed-off-by: Chanwoo Choi > [linux.amoon: Tested on Odroid U3] > Tested-by: Anand Moon > --- > drivers/devfreq/Kconfig | 15 ++ > drivers/devfreq/Makefile | 1 + > drivers/devfreq/exynos/Makefile | 1 + > drivers/devfreq/exynos/exynos-bus.c | 449 ++++++++++++++++++++++++++++++++++++ > 4 files changed, 466 insertions(+) > create mode 100644 drivers/devfreq/exynos/exynos-bus.c > > diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig > index 64281bb2f650..55ec774f794c 100644 > --- a/drivers/devfreq/Kconfig > +++ b/drivers/devfreq/Kconfig > @@ -66,6 +66,21 @@ config DEVFREQ_GOV_USERSPACE > > comment "DEVFREQ Drivers" > > +config ARM_EXYNOS_BUS_DEVFREQ > + bool "ARM EXYNOS Generic Memory Bus DEVFREQ Driver" > + depends on ARCH_EXYNOS > + select DEVFREQ_GOV_SIMPLE_ONDEMAND > + select DEVFREQ_EVENT_EXYNOS_PPMU > + select PM_DEVFREQ_EVENT > + select PM_OPP > + help > + This adds the common DEVFREQ driver for Exynos Memory bus. Exynos > + Memory bus has one more group of memory bus (e.g, MIF and INT block). > + Each memory bus group could contain many memoby bus block. It reads > + PPMU counters of memory controllers by using DEVFREQ-event device > + and adjusts the operating frequencies and voltages with OPP support. > + This does not yet operate with optimal voltages. > + > config ARM_EXYNOS4_BUS_DEVFREQ > bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" > depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM > diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile > index 5134f9ee983d..375ebbb4fcfb 100644 > --- a/drivers/devfreq/Makefile > +++ b/drivers/devfreq/Makefile > @@ -6,6 +6,7 @@ obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o > obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o > > # DEVFREQ Drivers > +obj-$(CONFIG_ARCH_EXYNOS) += exynos/ > obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/ > obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/ > obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o > diff --git a/drivers/devfreq/exynos/Makefile b/drivers/devfreq/exynos/Makefile > index 49bc9175f923..4ec06d322996 100644 > --- a/drivers/devfreq/exynos/Makefile > +++ b/drivers/devfreq/exynos/Makefile > @@ -1,3 +1,4 @@ > # Exynos DEVFREQ Drivers > +obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o > obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos_ppmu.o exynos4_bus.o > obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o > diff --git a/drivers/devfreq/exynos/exynos-bus.c b/drivers/devfreq/exynos/exynos-bus.c > new file mode 100644 > index 000000000000..f1bc20839650 > --- /dev/null > +++ b/drivers/devfreq/exynos/exynos-bus.c > @@ -0,0 +1,449 @@ > +/* > + * Generic Exynos Bus frequency driver with DEVFREQ Framework > + * > + * Copyright (c) 2015 Samsung Electronics Co., Ltd. > + * Author : Chanwoo Choi > + * > + * This driver support Exynos Bus frequency feature by using > + * DEVFREQ framework and is based on drivers/devfreq/exynos/exynos4_bus.c. > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define DEFAULT_SATURATION_RATIO 40 > + > +struct exynos_bus { > + struct device *dev; > + > + struct devfreq *devfreq; > + struct devfreq_event_dev **edev; > + unsigned int edev_count; > + struct mutex lock; > + > + struct dev_pm_opp *curr_opp; > + > + struct regulator *regulator; > + struct clk *clk; > + 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 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, old_volt, new_volt; > + int ret = 0; > + > + /* 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); > + } > + > + new_freq = dev_pm_opp_get_freq(new_opp); > + new_volt = dev_pm_opp_get_voltage(new_opp); > + old_freq = dev_pm_opp_get_freq(bus->curr_opp); > + old_volt = dev_pm_opp_get_voltage(bus->curr_opp); > + rcu_read_unlock(); > + > + if (old_freq == new_freq) > + return 0; > + > + /* Change voltage and frequency according to new OPP level */ > + mutex_lock(&bus->lock); > + > + if (old_freq < new_freq) { > + ret = regulator_set_voltage(bus->regulator, new_volt, new_volt); > + if (ret < 0) { > + dev_err(bus->dev, "failed to set voltage\n"); > + regulator_set_voltage(bus->regulator, old_volt, > + old_volt); > + 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(bus->regulator, new_volt, new_volt); > + if (ret < 0) { > + dev_err(bus->dev, "failed to set voltage\n"); > + regulator_set_voltage(bus->regulator, old_volt, > + old_volt); > + goto out; > + } > + } > + 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 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; > + > + rcu_read_lock(); > + stat->current_frequency = dev_pm_opp_get_freq(bus->curr_opp); > + rcu_read_unlock(); > + > + 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 : %ld/%ld\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); > +} > + > +static int exynos_bus_parse_of(struct device_node *np, > + struct exynos_bus *bus) > +{ > + struct device *dev = bus->dev; > + unsigned long rate; > + int i, ret, count, size; > + > + /* 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/voltage OPP table to scale the bus frequency */ > + 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(); > + > + /* 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; > + } > + > + ret = regulator_enable(bus->regulator); > + if (ret < 0) { > + dev_err(dev, "failed to enable VDD regulator\n"); > + goto err_opp; > + } > + > + /* > + * Get the devfreq-event devices to get the current utilization of > + * buses. This raw data will be used in devfreq ondemand governor. > + */ > + count = devfreq_event_get_edev_count(dev); > + if (count < 0) { > + dev_err(dev, "failed to get the count of devfreq-event dev\n"); > + ret = count; > + goto err_regulator; > + } > + bus->edev_count = count; > + > + size = sizeof(*bus->edev) * count; > + bus->edev = devm_kzalloc(dev, size, GFP_KERNEL); > + if (!bus->edev) { > + ret = -ENOMEM; > + goto err_regulator; > + } > + > + for (i = 0; i < count; i++) { > + bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, i); > + if (IS_ERR(bus->edev[i])) { > + ret = -EPROBE_DEFER; > + goto err_regulator; > + } > + } > + > + /* > + * Optionally, Get the saturation ratio according to Exynos SoC > + * When measuring the utilization of each AXI bus with devfreq-event > + * devices, the measured real cycle might be much lower than the > + * total cycle of bus during sampling rate. In result, the devfreq > + * simple-ondemand governor might not decide to change the current > + * frequency due to too utilization (= real cycle/total cycle). > + * So, this property is used to adjust the utilization when calculating > + * the busy_time in exynos_bus_get_dev_status(). > + */ > + if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio)) > + bus->ratio = DEFAULT_SATURATION_RATIO; > + > + return 0; > + > +err_regulator: > + regulator_disable(bus->regulator); > +err_opp: > + dev_pm_opp_of_remove_table(dev); > + > + return ret; > +} > + > +static int exynos_bus_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct device_node *np = dev->of_node; > + struct devfreq_dev_profile *profile; > + struct devfreq_simple_ondemand_data *ondemand_data; > + struct exynos_bus *bus; > + int ret; > + > + if (!np) { > + dev_err(dev, "failed to find devicetree node\n"); > + return -EINVAL; > + } > + > + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); > + if (!bus) > + return -ENOMEM; > + mutex_init(&bus->lock); > + bus->dev = &pdev->dev; > + platform_set_drvdata(pdev, bus); > + > + /* Parse the device-tree to get the resource information */ > + ret = exynos_bus_parse_of(np, bus); > + if (ret < 0) > + return ret; > + > + /* Initalize the struct profile and governor data */ > + profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL); > + if (!profile) > + return -ENOMEM; > + profile->polling_ms = 50; > + profile->target = exynos_bus_target; > + profile->get_dev_status = exynos_bus_get_dev_status; > + profile->exit = exynos_bus_exit; > + > + ondemand_data = devm_kzalloc(dev, sizeof(*ondemand_data), GFP_KERNEL); > + if (!ondemand_data) > + return -ENOMEM; > + ondemand_data->upthreshold = 40; > + ondemand_data->downdifferential = 5; > + > + /* Add devfreq device to monitor and handle the exynos bus */ > + bus->devfreq = devm_devfreq_add_device(dev, profile, "simple_ondemand", > + ondemand_data); > + if (IS_ERR_OR_NULL(bus->devfreq)) { > + dev_err(dev, "failed to add devfreq device\n"); > + return PTR_ERR(bus->devfreq); > + } > + > + /* Register opp_notifier to catch the change of OPP */ > + ret = devm_devfreq_register_opp_notifier(dev, bus->devfreq); > + if (ret < 0) { > + dev_err(dev, "failed to register opp notifier\n"); > + return ret; > + } > + > + /* > + * Enable devfreq-event to get raw data which is used to determine > + * current bus load. > + */ > + ret = exynos_bus_enable_edev(bus); > + if (ret < 0) { > + dev_err(dev, "failed to enable devfreq-event devices\n"); > + return ret; > + } > + > + ret = exynos_bus_set_event(bus); > + if (ret < 0) { > + dev_err(dev, "failed to set event to devfreq-event devices\n"); > + return ret; > + } > + > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int exynos_bus_resume(struct device *dev) > +{ > + struct exynos_bus *bus = dev_get_drvdata(dev); > + int ret; > + > + if (bus->regulator) { > + ret = regulator_enable(bus->regulator); > + if (ret < 0) { > + dev_err(dev, "failed to enable VDD regulator\n"); > + return ret; > + } > + } > + > + ret = exynos_bus_enable_edev(bus); > + if (ret < 0) { > + dev_err(dev, "failed to enable the devfreq-event devices\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int exynos_bus_suspend(struct device *dev) > +{ > + struct exynos_bus *bus = dev_get_drvdata(dev); > + int ret; > + > + ret = exynos_bus_disable_edev(bus); > + if (ret < 0) { > + dev_err(dev, "failed to disable the devfreq-event devices\n"); > + return ret; > + } > + > + if (bus->regulator) > + regulator_disable(bus->regulator); > + > + return 0; > +} > +#endif > + > +static const struct dev_pm_ops exynos_bus_pm = { > + SET_SYSTEM_SLEEP_PM_OPS(exynos_bus_suspend, exynos_bus_resume) > +}; > + > +static const struct of_device_id exynos_bus_of_match[] = { > + { .compatible = "samsung,exynos-bus", }, > + { /* sentinel */ }, > +}; > +MODULE_DEVICE_TABLE(of, exynos_bus_of_match); > + > +static struct platform_driver exynos_bus_platdrv = { > + .probe = exynos_bus_probe, > + .driver = { > + .name = "exynos-bus", > + .pm = &exynos_bus_pm, > + .of_match_table = of_match_ptr(exynos_bus_of_match), > + }, > +}; > +module_platform_driver(exynos_bus_platdrv); > + > +MODULE_DESCRIPTION("Generic Exynos Bus frequency driver"); > +MODULE_AUTHOR("Chanwoo Choi "); > +MODULE_LICENSE("GPL v2"); >