mirror of
https://github.com/torvalds/linux.git
synced 2024-12-03 17:41:22 +00:00
cb92a19b99
Commit a53e35db70
("reset: Ensure drivers are explicit when requesting
reset lines") started to transition the reset control request API calls
to explicitly state whether the driver needs exclusive or shared reset
control behavior. Convert all drivers requesting exclusive resets to the
explicit API call so the temporary transition helpers can be removed.
No functional changes.
Cc: "Emilio López" <emilio@elopez.com.ar>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: linux-clk@vger.kernel.org
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
200 lines
5.1 KiB
C
200 lines
5.1 KiB
C
/*
|
|
* Copyright 2015 Chen-Yu Tsai
|
|
*
|
|
* Chen-Yu Tsai <wens@csie.org>
|
|
*
|
|
* 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 program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/init.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/reset.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/reset-controller.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
|
|
#define SUN9I_MMC_WIDTH 4
|
|
|
|
#define SUN9I_MMC_GATE_BIT 16
|
|
#define SUN9I_MMC_RESET_BIT 18
|
|
|
|
struct sun9i_mmc_clk_data {
|
|
spinlock_t lock;
|
|
void __iomem *membase;
|
|
struct clk *clk;
|
|
struct reset_control *reset;
|
|
struct clk_onecell_data clk_data;
|
|
struct reset_controller_dev rcdev;
|
|
};
|
|
|
|
static int sun9i_mmc_reset_assert(struct reset_controller_dev *rcdev,
|
|
unsigned long id)
|
|
{
|
|
struct sun9i_mmc_clk_data *data = container_of(rcdev,
|
|
struct sun9i_mmc_clk_data,
|
|
rcdev);
|
|
unsigned long flags;
|
|
void __iomem *reg = data->membase + SUN9I_MMC_WIDTH * id;
|
|
u32 val;
|
|
|
|
clk_prepare_enable(data->clk);
|
|
spin_lock_irqsave(&data->lock, flags);
|
|
|
|
val = readl(reg);
|
|
writel(val & ~BIT(SUN9I_MMC_RESET_BIT), reg);
|
|
|
|
spin_unlock_irqrestore(&data->lock, flags);
|
|
clk_disable_unprepare(data->clk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sun9i_mmc_reset_deassert(struct reset_controller_dev *rcdev,
|
|
unsigned long id)
|
|
{
|
|
struct sun9i_mmc_clk_data *data = container_of(rcdev,
|
|
struct sun9i_mmc_clk_data,
|
|
rcdev);
|
|
unsigned long flags;
|
|
void __iomem *reg = data->membase + SUN9I_MMC_WIDTH * id;
|
|
u32 val;
|
|
|
|
clk_prepare_enable(data->clk);
|
|
spin_lock_irqsave(&data->lock, flags);
|
|
|
|
val = readl(reg);
|
|
writel(val | BIT(SUN9I_MMC_RESET_BIT), reg);
|
|
|
|
spin_unlock_irqrestore(&data->lock, flags);
|
|
clk_disable_unprepare(data->clk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct reset_control_ops sun9i_mmc_reset_ops = {
|
|
.assert = sun9i_mmc_reset_assert,
|
|
.deassert = sun9i_mmc_reset_deassert,
|
|
};
|
|
|
|
static int sun9i_a80_mmc_config_clk_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct sun9i_mmc_clk_data *data;
|
|
struct clk_onecell_data *clk_data;
|
|
const char *clk_name = np->name;
|
|
const char *clk_parent;
|
|
struct resource *r;
|
|
int count, i, ret;
|
|
|
|
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
spin_lock_init(&data->lock);
|
|
|
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
/* one clock/reset pair per word */
|
|
count = DIV_ROUND_UP((resource_size(r)), SUN9I_MMC_WIDTH);
|
|
data->membase = devm_ioremap_resource(&pdev->dev, r);
|
|
if (IS_ERR(data->membase))
|
|
return PTR_ERR(data->membase);
|
|
|
|
clk_data = &data->clk_data;
|
|
clk_data->clk_num = count;
|
|
clk_data->clks = devm_kcalloc(&pdev->dev, count, sizeof(struct clk *),
|
|
GFP_KERNEL);
|
|
if (!clk_data->clks)
|
|
return -ENOMEM;
|
|
|
|
data->clk = devm_clk_get(&pdev->dev, NULL);
|
|
if (IS_ERR(data->clk)) {
|
|
dev_err(&pdev->dev, "Could not get clock\n");
|
|
return PTR_ERR(data->clk);
|
|
}
|
|
|
|
data->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
|
if (IS_ERR(data->reset)) {
|
|
dev_err(&pdev->dev, "Could not get reset control\n");
|
|
return PTR_ERR(data->reset);
|
|
}
|
|
|
|
ret = reset_control_deassert(data->reset);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Reset deassert err %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
clk_parent = __clk_get_name(data->clk);
|
|
for (i = 0; i < count; i++) {
|
|
of_property_read_string_index(np, "clock-output-names",
|
|
i, &clk_name);
|
|
|
|
clk_data->clks[i] = clk_register_gate(&pdev->dev, clk_name,
|
|
clk_parent, 0,
|
|
data->membase + SUN9I_MMC_WIDTH * i,
|
|
SUN9I_MMC_GATE_BIT, 0,
|
|
&data->lock);
|
|
|
|
if (IS_ERR(clk_data->clks[i])) {
|
|
ret = PTR_ERR(clk_data->clks[i]);
|
|
goto err_clk_register;
|
|
}
|
|
}
|
|
|
|
ret = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
|
|
if (ret)
|
|
goto err_clk_provider;
|
|
|
|
data->rcdev.owner = THIS_MODULE;
|
|
data->rcdev.nr_resets = count;
|
|
data->rcdev.ops = &sun9i_mmc_reset_ops;
|
|
data->rcdev.of_node = pdev->dev.of_node;
|
|
|
|
ret = reset_controller_register(&data->rcdev);
|
|
if (ret)
|
|
goto err_rc_reg;
|
|
|
|
platform_set_drvdata(pdev, data);
|
|
|
|
return 0;
|
|
|
|
err_rc_reg:
|
|
of_clk_del_provider(np);
|
|
|
|
err_clk_provider:
|
|
for (i = 0; i < count; i++)
|
|
clk_unregister(clk_data->clks[i]);
|
|
|
|
err_clk_register:
|
|
reset_control_assert(data->reset);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids[] = {
|
|
{ .compatible = "allwinner,sun9i-a80-mmc-config-clk" },
|
|
{ /* sentinel */ }
|
|
};
|
|
|
|
static struct platform_driver sun9i_a80_mmc_config_clk_driver = {
|
|
.driver = {
|
|
.name = "sun9i-a80-mmc-config-clk",
|
|
.suppress_bind_attrs = true,
|
|
.of_match_table = sun9i_a80_mmc_config_clk_dt_ids,
|
|
},
|
|
.probe = sun9i_a80_mmc_config_clk_probe,
|
|
};
|
|
builtin_platform_driver(sun9i_a80_mmc_config_clk_driver);
|