mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
Merge branch 'for-linus-clk' of git://ftp.arm.linux.org.uk/~rmk/linux-arm
Pull clkdev updates from Russell King: "This series addresses some breakage in clkdev caused by a previous patch set from the clk tree which introduced per-user clk structures. This basically renamed the existing 'struct clk' to 'struct clk_hw', and introduced a new 'struct clk'. This change will break anyone using clk_add_alias() with the common clk code enabled. Thankfully, the intersection of users of clk_add_alias() and those using the common clk code is practically zero, but this is something which should be fixed to keep the code sane. The problem is that clk_add_alias() does this: r = clk_get(...); l = clkdev_alloc(r, ...); clk_put(...); which causes the alias to store a pointer to 'r', which has been freed. The original patch set tried to work around this problem incorrectly - at clk_get() time, it tried to convert the struct clk to a struct clk_hw, and then creating a new struct clk from that. Clearly, if the original struct clk has been freed, then we have a use-after-free bug. We have other places in the tree which do something similar, so this series also addresses those locations too. This series addresses this problem by converting clkdev to store and use the clk_hw pointer. This allows clk_get() to only have to create it's per-user struct clk from the clk_hw. We can also get to the desired clk_hw at clk_add_alias() or clk lookup creation time, when the struct clk is "alive". We also perform some cleanups of the code: - replacing looped calls to clkdev_add() with clkdev_add_table() - replacing open-coded lookup allocation (which should have been using clkdev_alloc()) and subsequent clkdev_add() with clkdev_create() - replacing open-coded clk_add_alias() with clk_add_alias()" * 'for-linus-clk' of git://ftp.arm.linux.org.uk/~rmk/linux-arm: clk: s2mps11: use clkdev_create() ASoC: migor: use clkdev_create() ARM: omap2: use clkdev_add_alias() ARM: omap2: use clkdev_create() ARM: orion: use clkdev_create() ARM: lpc32xx: convert to use clkdev_add_table() SH: use clkdev_add_table() clkdev: add clkdev_create() helper clkdev: const-ify connection id to clk_add_alias() clkdev: get rid of redundant clk_add_alias() prototype in linux/clk.h clkdev: drop __init from clkdev_add_table() clk: update clk API documentation to clarify clk_round_rate() clkdev: use clk_hw internally
This commit is contained in:
commit
2ad7b44f5d
@ -11,6 +11,7 @@
|
||||
* is licensed "as is" without any warranty of any kind, whether express
|
||||
* or implied.
|
||||
*/
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/clk.h>
|
||||
|
@ -1238,10 +1238,7 @@ static struct clk_lookup lookups[] = {
|
||||
|
||||
static int __init clk_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lookups); i++)
|
||||
clkdev_add(&lookups[i]);
|
||||
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
|
||||
|
||||
/*
|
||||
* Setup muxed SYSCLK for HCLK PLL base -this selects the
|
||||
|
@ -7,6 +7,7 @@
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -14,7 +15,6 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/omapfb.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
|
@ -232,14 +232,12 @@ void omap2xxx_clkt_vps_init(void)
|
||||
struct clk_hw_omap *hw = NULL;
|
||||
struct clk *clk;
|
||||
const char *parent_name = "mpu_ck";
|
||||
struct clk_lookup *lookup = NULL;
|
||||
|
||||
omap2xxx_clkt_vps_late_init();
|
||||
omap2xxx_clkt_vps_check_bootloader_rates();
|
||||
|
||||
hw = kzalloc(sizeof(*hw), GFP_KERNEL);
|
||||
lookup = kzalloc(sizeof(*lookup), GFP_KERNEL);
|
||||
if (!hw || !lookup)
|
||||
if (!hw)
|
||||
goto cleanup;
|
||||
init.name = "virt_prcm_set";
|
||||
init.ops = &virt_prcm_set_ops;
|
||||
@ -249,15 +247,9 @@ void omap2xxx_clkt_vps_init(void)
|
||||
hw->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &hw->hw);
|
||||
|
||||
lookup->dev_id = NULL;
|
||||
lookup->con_id = "cpufreq_ck";
|
||||
lookup->clk = clk;
|
||||
|
||||
clkdev_add(lookup);
|
||||
clkdev_create(clk, "cpufreq_ck", NULL);
|
||||
return;
|
||||
cleanup:
|
||||
kfree(hw);
|
||||
kfree(lookup);
|
||||
}
|
||||
#endif
|
||||
|
@ -47,7 +47,7 @@ static void _add_clkdev(struct omap_device *od, const char *clk_alias,
|
||||
const char *clk_name)
|
||||
{
|
||||
struct clk *r;
|
||||
struct clk_lookup *l;
|
||||
int rc;
|
||||
|
||||
if (!clk_alias || !clk_name)
|
||||
return;
|
||||
@ -62,21 +62,15 @@ static void _add_clkdev(struct omap_device *od, const char *clk_alias,
|
||||
return;
|
||||
}
|
||||
|
||||
r = clk_get(NULL, clk_name);
|
||||
if (IS_ERR(r)) {
|
||||
dev_err(&od->pdev->dev,
|
||||
"clk_get for %s failed\n", clk_name);
|
||||
return;
|
||||
rc = clk_add_alias(clk_alias, dev_name(&od->pdev->dev), clk_name, NULL);
|
||||
if (rc) {
|
||||
if (rc == -ENODEV || rc == -ENOMEM)
|
||||
dev_err(&od->pdev->dev,
|
||||
"clkdev_alloc for %s failed\n", clk_alias);
|
||||
else
|
||||
dev_err(&od->pdev->dev,
|
||||
"clk_get for %s failed\n", clk_name);
|
||||
}
|
||||
|
||||
l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev->dev));
|
||||
if (!l) {
|
||||
dev_err(&od->pdev->dev,
|
||||
"clkdev_alloc for %s failed\n", clk_alias);
|
||||
return;
|
||||
}
|
||||
|
||||
clkdev_add(l);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -10,6 +10,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gpio.h>
|
||||
|
@ -11,6 +11,7 @@
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -12,6 +12,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -28,11 +28,7 @@
|
||||
void __init orion_clkdev_add(const char *con_id, const char *dev_id,
|
||||
struct clk *clk)
|
||||
{
|
||||
struct clk_lookup *cl;
|
||||
|
||||
cl = clkdev_alloc(clk, con_id, dev_id);
|
||||
if (cl)
|
||||
clkdev_add(cl);
|
||||
clkdev_create(clk, con_id, "%s", dev_id);
|
||||
}
|
||||
|
||||
/* Create clkdev entries for all orion platforms except kirkwood.
|
||||
|
@ -246,8 +246,7 @@ int __init arch_clk_init(void)
|
||||
for (i = 0; i < ARRAY_SIZE(main_clks); i++)
|
||||
ret |= clk_register(main_clks[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lookups); i++)
|
||||
clkdev_add(&lookups[i]);
|
||||
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
|
||||
|
||||
if (!ret)
|
||||
ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
|
||||
|
@ -141,8 +141,8 @@ int __init arch_clk_init(void)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clks); i++)
|
||||
ret |= clk_register(clks[i]);
|
||||
for (i = 0; i < ARRAY_SIZE(lookups); i++)
|
||||
clkdev_add(&lookups[i]);
|
||||
|
||||
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
|
||||
|
||||
if (!ret)
|
||||
ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
|
||||
|
@ -164,8 +164,8 @@ int __init arch_clk_init(void)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clks); i++)
|
||||
ret |= clk_register(clks[i]);
|
||||
for (i = 0; i < ARRAY_SIZE(lookups); i++)
|
||||
clkdev_add(&lookups[i]);
|
||||
|
||||
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
|
||||
|
||||
if (!ret)
|
||||
ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
|
||||
|
@ -179,8 +179,8 @@ int __init arch_clk_init(void)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clks); i++)
|
||||
ret |= clk_register(clks[i]);
|
||||
for (i = 0; i < ARRAY_SIZE(lookups); i++)
|
||||
clkdev_add(&lookups[i]);
|
||||
|
||||
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
|
||||
|
||||
if (!ret)
|
||||
ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
|
||||
|
@ -138,8 +138,8 @@ int __init arch_clk_init(void)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clks); i++)
|
||||
ret |= clk_register(clks[i]);
|
||||
for (i = 0; i < ARRAY_SIZE(lookups); i++)
|
||||
clkdev_add(&lookups[i]);
|
||||
|
||||
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
|
||||
|
||||
if (!ret)
|
||||
ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
|
||||
|
@ -242,14 +242,12 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
|
||||
goto err_reg;
|
||||
}
|
||||
|
||||
s2mps11_clk->lookup = clkdev_alloc(s2mps11_clk->clk,
|
||||
s2mps11_clk->lookup = clkdev_create(s2mps11_clk->clk,
|
||||
s2mps11_name(s2mps11_clk), NULL);
|
||||
if (!s2mps11_clk->lookup) {
|
||||
ret = -ENOMEM;
|
||||
goto err_lup;
|
||||
}
|
||||
|
||||
clkdev_add(s2mps11_clk->lookup);
|
||||
}
|
||||
|
||||
for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
|
||||
|
@ -177,7 +177,7 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id)
|
||||
if (!cl)
|
||||
goto out;
|
||||
|
||||
clk = __clk_create_clk(__clk_get_hw(cl->clk), dev_id, con_id);
|
||||
clk = __clk_create_clk(cl->clk_hw, dev_id, con_id);
|
||||
if (IS_ERR(clk))
|
||||
goto out;
|
||||
|
||||
@ -215,18 +215,26 @@ void clk_put(struct clk *clk)
|
||||
}
|
||||
EXPORT_SYMBOL(clk_put);
|
||||
|
||||
void clkdev_add(struct clk_lookup *cl)
|
||||
static void __clkdev_add(struct clk_lookup *cl)
|
||||
{
|
||||
mutex_lock(&clocks_mutex);
|
||||
list_add_tail(&cl->node, &clocks);
|
||||
mutex_unlock(&clocks_mutex);
|
||||
}
|
||||
|
||||
void clkdev_add(struct clk_lookup *cl)
|
||||
{
|
||||
if (!cl->clk_hw)
|
||||
cl->clk_hw = __clk_get_hw(cl->clk);
|
||||
__clkdev_add(cl);
|
||||
}
|
||||
EXPORT_SYMBOL(clkdev_add);
|
||||
|
||||
void __init clkdev_add_table(struct clk_lookup *cl, size_t num)
|
||||
void clkdev_add_table(struct clk_lookup *cl, size_t num)
|
||||
{
|
||||
mutex_lock(&clocks_mutex);
|
||||
while (num--) {
|
||||
cl->clk_hw = __clk_get_hw(cl->clk);
|
||||
list_add_tail(&cl->node, &clocks);
|
||||
cl++;
|
||||
}
|
||||
@ -243,7 +251,7 @@ struct clk_lookup_alloc {
|
||||
};
|
||||
|
||||
static struct clk_lookup * __init_refok
|
||||
vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
|
||||
vclkdev_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
|
||||
va_list ap)
|
||||
{
|
||||
struct clk_lookup_alloc *cla;
|
||||
@ -252,7 +260,7 @@ vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
|
||||
if (!cla)
|
||||
return NULL;
|
||||
|
||||
cla->cl.clk = clk;
|
||||
cla->cl.clk_hw = hw;
|
||||
if (con_id) {
|
||||
strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
|
||||
cla->cl.con_id = cla->con_id;
|
||||
@ -266,6 +274,19 @@ vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
|
||||
return &cla->cl;
|
||||
}
|
||||
|
||||
static struct clk_lookup *
|
||||
vclkdev_create(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
|
||||
va_list ap)
|
||||
{
|
||||
struct clk_lookup *cl;
|
||||
|
||||
cl = vclkdev_alloc(hw, con_id, dev_fmt, ap);
|
||||
if (cl)
|
||||
__clkdev_add(cl);
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
struct clk_lookup * __init_refok
|
||||
clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
|
||||
{
|
||||
@ -273,28 +294,49 @@ clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, dev_fmt);
|
||||
cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);
|
||||
cl = vclkdev_alloc(__clk_get_hw(clk), con_id, dev_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return cl;
|
||||
}
|
||||
EXPORT_SYMBOL(clkdev_alloc);
|
||||
|
||||
int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
|
||||
struct device *dev)
|
||||
/**
|
||||
* clkdev_create - allocate and add a clkdev lookup structure
|
||||
* @clk: struct clk to associate with all clk_lookups
|
||||
* @con_id: connection ID string on device
|
||||
* @dev_fmt: format string describing device name
|
||||
*
|
||||
* Returns a clk_lookup structure, which can be later unregistered and
|
||||
* freed.
|
||||
*/
|
||||
struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id,
|
||||
const char *dev_fmt, ...)
|
||||
{
|
||||
struct clk *r = clk_get(dev, id);
|
||||
struct clk_lookup *cl;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, dev_fmt);
|
||||
cl = vclkdev_create(__clk_get_hw(clk), con_id, dev_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return cl;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clkdev_create);
|
||||
|
||||
int clk_add_alias(const char *alias, const char *alias_dev_name,
|
||||
const char *con_id, struct device *dev)
|
||||
{
|
||||
struct clk *r = clk_get(dev, con_id);
|
||||
struct clk_lookup *l;
|
||||
|
||||
if (IS_ERR(r))
|
||||
return PTR_ERR(r);
|
||||
|
||||
l = clkdev_alloc(r, alias, alias_dev_name);
|
||||
l = clkdev_create(r, alias, "%s", alias_dev_name);
|
||||
clk_put(r);
|
||||
if (!l)
|
||||
return -ENODEV;
|
||||
clkdev_add(l);
|
||||
return 0;
|
||||
|
||||
return l ? 0 : -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_add_alias);
|
||||
|
||||
@ -334,15 +376,10 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
|
||||
return PTR_ERR(clk);
|
||||
|
||||
va_start(ap, dev_fmt);
|
||||
cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);
|
||||
cl = vclkdev_create(__clk_get_hw(clk), con_id, dev_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!cl)
|
||||
return -ENOMEM;
|
||||
|
||||
clkdev_add(cl);
|
||||
|
||||
return 0;
|
||||
return cl ? 0 : -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_register_clkdev);
|
||||
|
||||
@ -365,8 +402,8 @@ int clk_register_clkdevs(struct clk *clk, struct clk_lookup *cl, size_t num)
|
||||
return PTR_ERR(clk);
|
||||
|
||||
for (i = 0; i < num; i++, cl++) {
|
||||
cl->clk = clk;
|
||||
clkdev_add(cl);
|
||||
cl->clk_hw = __clk_get_hw(clk);
|
||||
__clkdev_add(cl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -306,6 +306,20 @@ void devm_clk_put(struct device *dev, struct clk *clk);
|
||||
* @clk: clock source
|
||||
* @rate: desired clock rate in Hz
|
||||
*
|
||||
* This answers the question "if I were to pass @rate to clk_set_rate(),
|
||||
* what clock rate would I end up with?" without changing the hardware
|
||||
* in any way. In other words:
|
||||
*
|
||||
* rate = clk_round_rate(clk, r);
|
||||
*
|
||||
* and:
|
||||
*
|
||||
* clk_set_rate(clk, r);
|
||||
* rate = clk_get_rate(clk);
|
||||
*
|
||||
* are equivalent except the former does not modify the clock hardware
|
||||
* in any way.
|
||||
*
|
||||
* Returns rounded clock rate in Hz, or negative errno.
|
||||
*/
|
||||
long clk_round_rate(struct clk *clk, unsigned long rate);
|
||||
@ -471,19 +485,6 @@ static inline void clk_disable_unprepare(struct clk *clk)
|
||||
clk_unprepare(clk);
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_add_alias - add a new clock alias
|
||||
* @alias: name for clock alias
|
||||
* @alias_dev_name: device name
|
||||
* @id: platform specific clock name
|
||||
* @dev: device
|
||||
*
|
||||
* Allows using generic clock names for drivers by adding a new alias.
|
||||
* Assumes clkdev, see clkdev.h for more info.
|
||||
*/
|
||||
int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
|
||||
struct device *dev);
|
||||
|
||||
struct device_node;
|
||||
struct of_phandle_args;
|
||||
|
||||
|
@ -22,6 +22,7 @@ struct clk_lookup {
|
||||
const char *dev_id;
|
||||
const char *con_id;
|
||||
struct clk *clk;
|
||||
struct clk_hw *clk_hw;
|
||||
};
|
||||
|
||||
#define CLKDEV_INIT(d, n, c) \
|
||||
@ -37,8 +38,11 @@ struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id,
|
||||
void clkdev_add(struct clk_lookup *cl);
|
||||
void clkdev_drop(struct clk_lookup *cl);
|
||||
|
||||
struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id,
|
||||
const char *dev_fmt, ...);
|
||||
|
||||
void clkdev_add_table(struct clk_lookup *, size_t);
|
||||
int clk_add_alias(const char *, const char *, char *, struct device *);
|
||||
int clk_add_alias(const char *, const char *, const char *, struct device *);
|
||||
|
||||
int clk_register_clkdev(struct clk *, const char *, const char *, ...);
|
||||
int clk_register_clkdevs(struct clk *, struct clk_lookup *, size_t);
|
||||
|
@ -162,12 +162,11 @@ static int __init migor_init(void)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
siumckb_lookup = clkdev_alloc(&siumckb_clk, "siumckb_clk", NULL);
|
||||
siumckb_lookup = clkdev_create(&siumckb_clk, "siumckb_clk", NULL);
|
||||
if (!siumckb_lookup) {
|
||||
ret = -ENOMEM;
|
||||
goto eclkdevalloc;
|
||||
}
|
||||
clkdev_add(siumckb_lookup);
|
||||
|
||||
/* Port number used on this machine: port B */
|
||||
migor_snd_device = platform_device_alloc("soc-audio", 1);
|
||||
|
Loading…
Reference in New Issue
Block a user