Commit ba7c8d27 authored by Álvaro Fernández Rojas's avatar Álvaro Fernández Rojas Committed by Stephen Boyd

clk: bcm: Add BCM63268 timer clock and reset driver

Add driver for BCM63268 timer clock and reset controller.
Signed-off-by: default avatarÁlvaro Fernández Rojas <noltari@gmail.com>
Link: https://lore.kernel.org/r/20230322171515.120353-5-noltari@gmail.com
[sboyd@kernel.org: Mark reset ops const, fixup includes]
Signed-off-by: default avatarStephen Boyd <sboyd@kernel.org>
parent cd04bbb9
...@@ -37,6 +37,15 @@ config CLK_BCM_63XX_GATE ...@@ -37,6 +37,15 @@ config CLK_BCM_63XX_GATE
Enable common clock framework support for Broadcom BCM63xx DSL SoCs Enable common clock framework support for Broadcom BCM63xx DSL SoCs
based on the MIPS architecture based on the MIPS architecture
config CLK_BCM63268_TIMER
bool "Broadcom BCM63268 timer clock and reset support"
depends on BMIPS_GENERIC || COMPILE_TEST
default BMIPS_GENERIC
select RESET_CONTROLLER
help
Enable timer clock and reset support for Broadcom BCM63268 DSL SoCs
based on the MIPS architecture.
config CLK_BCM_KONA config CLK_BCM_KONA
bool "Broadcom Kona CCU clock support" bool "Broadcom Kona CCU clock support"
depends on ARCH_BCM_MOBILE || COMPILE_TEST depends on ARCH_BCM_MOBILE || COMPILE_TEST
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CLK_BCM_63XX) += clk-bcm63xx.o obj-$(CONFIG_CLK_BCM_63XX) += clk-bcm63xx.o
obj-$(CONFIG_CLK_BCM_63XX_GATE) += clk-bcm63xx-gate.o obj-$(CONFIG_CLK_BCM_63XX_GATE) += clk-bcm63xx-gate.o
obj-$(CONFIG_CLK_BCM63268_TIMER) += clk-bcm63268-timer.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
......
// SPDX-License-Identifier: GPL-2.0
/*
* BCM63268 Timer Clock and Reset Controller Driver
*
* Copyright (C) 2023 Álvaro Fernández Rojas <noltari@gmail.com>
*/
#include <linux/clk-provider.h>
#include <linux/container_of.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/spinlock.h>
#include <dt-bindings/clock/bcm63268-clock.h>
#define BCM63268_TIMER_RESET_SLEEP_MIN_US 10000
#define BCM63268_TIMER_RESET_SLEEP_MAX_US 20000
struct bcm63268_tclkrst_hw {
void __iomem *regs;
spinlock_t lock;
struct reset_controller_dev rcdev;
struct clk_hw_onecell_data data;
};
struct bcm63268_tclk_table_entry {
const char * const name;
u8 bit;
};
static const struct bcm63268_tclk_table_entry bcm63268_timer_clocks[] = {
{
.name = "ephy1",
.bit = BCM63268_TCLK_EPHY1,
}, {
.name = "ephy2",
.bit = BCM63268_TCLK_EPHY2,
}, {
.name = "ephy3",
.bit = BCM63268_TCLK_EPHY3,
}, {
.name = "gphy1",
.bit = BCM63268_TCLK_GPHY1,
}, {
.name = "dsl",
.bit = BCM63268_TCLK_DSL,
}, {
.name = "wakeon_ephy",
.bit = BCM63268_TCLK_WAKEON_EPHY,
}, {
.name = "wakeon_dsl",
.bit = BCM63268_TCLK_WAKEON_DSL,
}, {
.name = "fap1_pll",
.bit = BCM63268_TCLK_FAP1,
}, {
.name = "fap2_pll",
.bit = BCM63268_TCLK_FAP2,
}, {
.name = "uto_50",
.bit = BCM63268_TCLK_UTO_50,
}, {
.name = "uto_extin",
.bit = BCM63268_TCLK_UTO_EXTIN,
}, {
.name = "usb_ref",
.bit = BCM63268_TCLK_USB_REF,
}, {
/* sentinel */
}
};
static inline struct bcm63268_tclkrst_hw *
to_bcm63268_timer_reset(struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct bcm63268_tclkrst_hw, rcdev);
}
static int bcm63268_timer_reset_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev);
unsigned long flags;
uint32_t val;
spin_lock_irqsave(&reset->lock, flags);
val = __raw_readl(reset->regs);
if (assert)
val &= ~BIT(id);
else
val |= BIT(id);
__raw_writel(val, reset->regs);
spin_unlock_irqrestore(&reset->lock, flags);
return 0;
}
static int bcm63268_timer_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return bcm63268_timer_reset_update(rcdev, id, true);
}
static int bcm63268_timer_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return bcm63268_timer_reset_update(rcdev, id, false);
}
static int bcm63268_timer_reset_reset(struct reset_controller_dev *rcdev,
unsigned long id)
{
bcm63268_timer_reset_update(rcdev, id, true);
usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US,
BCM63268_TIMER_RESET_SLEEP_MAX_US);
bcm63268_timer_reset_update(rcdev, id, false);
/*
* Ensure component is taken out reset state by sleeping also after
* deasserting the reset. Otherwise, the component may not be ready
* for operation.
*/
usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US,
BCM63268_TIMER_RESET_SLEEP_MAX_US);
return 0;
}
static int bcm63268_timer_reset_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev);
return !(__raw_readl(reset->regs) & BIT(id));
}
static const struct reset_control_ops bcm63268_timer_reset_ops = {
.assert = bcm63268_timer_reset_assert,
.deassert = bcm63268_timer_reset_deassert,
.reset = bcm63268_timer_reset_reset,
.status = bcm63268_timer_reset_status,
};
static int bcm63268_tclk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct bcm63268_tclk_table_entry *entry;
struct bcm63268_tclkrst_hw *hw;
struct clk_hw *clk;
u8 maxbit = 0;
int i, ret;
for (entry = bcm63268_timer_clocks; entry->name; entry++)
maxbit = max(maxbit, entry->bit);
maxbit++;
hw = devm_kzalloc(&pdev->dev, struct_size(hw, data.hws, maxbit),
GFP_KERNEL);
if (!hw)
return -ENOMEM;
platform_set_drvdata(pdev, hw);
spin_lock_init(&hw->lock);
hw->data.num = maxbit;
for (i = 0; i < maxbit; i++)
hw->data.hws[i] = ERR_PTR(-ENODEV);
hw->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hw->regs))
return PTR_ERR(hw->regs);
for (entry = bcm63268_timer_clocks; entry->name; entry++) {
clk = devm_clk_hw_register_gate(dev, entry->name, NULL, 0,
hw->regs, entry->bit,
CLK_GATE_BIG_ENDIAN,
&hw->lock);
if (IS_ERR(clk))
return PTR_ERR(clk);
hw->data.hws[entry->bit] = clk;
}
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
&hw->data);
if (ret)
return ret;
hw->rcdev.of_node = dev->of_node;
hw->rcdev.ops = &bcm63268_timer_reset_ops;
ret = devm_reset_controller_register(dev, &hw->rcdev);
if (ret)
dev_err(dev, "Failed to register reset controller\n");
return 0;
}
static const struct of_device_id bcm63268_tclk_dt_ids[] = {
{ .compatible = "brcm,bcm63268-timer-clocks" },
{ /* sentinel */ }
};
static struct platform_driver bcm63268_tclk = {
.probe = bcm63268_tclk_probe,
.driver = {
.name = "bcm63268-timer-clock",
.of_match_table = bcm63268_tclk_dt_ids,
},
};
builtin_platform_driver(bcm63268_tclk);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment