Skip to content

Commit

Permalink
reset: Add Broadcom STB SW_INIT reset controller driver
Browse files Browse the repository at this point in the history
Add support for resetting blocks through the Linux reset controller
subsystem when reset lines are provided through a SW_INIT-style reset
controller on Broadcom STB SoCs.

Signed-off-by: Florian Fainelli <[email protected]>
Signed-off-by: Philipp Zabel <[email protected]>
  • Loading branch information
ffainelli authored and pH5 committed Jan 28, 2019
1 parent 0807caf commit 77750bc
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 0 deletions.
8 changes: 8 additions & 0 deletions drivers/reset/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ config RESET_BERLIN
help
This enables the reset controller driver for Marvell Berlin SoCs.

config RESET_BRCMSTB
tristate "Broadcom STB reset controller"
depends on ARCH_BRCMSTB || COMPILE_TEST
default ARCH_BRCMSTB
help
This enables the reset controller driver for Broadcom STB SoCs using
a SUN_TOP_CTRL_SW_INIT style controller.

config RESET_HSDK
bool "Synopsys HSDK Reset Driver"
depends on HAS_IOMEM
Expand Down
1 change: 1 addition & 0 deletions drivers/reset/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o
obj-$(CONFIG_RESET_ATH79) += reset-ath79.o
obj-$(CONFIG_RESET_AXS10X) += reset-axs10x.o
obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o
obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
Expand Down
132 changes: 132 additions & 0 deletions drivers/reset/reset-brcmstb.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Broadcom STB generic reset controller for SW_INIT style reset controller
*
* Author: Florian Fainelli <[email protected]>
* Copyright (C) 2018 Broadcom
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/types.h>

struct brcmstb_reset {
void __iomem *base;
struct reset_controller_dev rcdev;
};

#define SW_INIT_SET 0x00
#define SW_INIT_CLEAR 0x04
#define SW_INIT_STATUS 0x08

#define SW_INIT_BIT(id) BIT((id) & 0x1f)
#define SW_INIT_BANK(id) ((id) >> 5)

/* A full bank contains extra registers that we are not utilizing but still
* qualify as a single bank.
*/
#define SW_INIT_BANK_SIZE 0x18

static inline
struct brcmstb_reset *to_brcmstb(struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct brcmstb_reset, rcdev);
}

static int brcmstb_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE;
struct brcmstb_reset *priv = to_brcmstb(rcdev);

writel_relaxed(SW_INIT_BIT(id), priv->base + off + SW_INIT_SET);

return 0;
}

static int brcmstb_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE;
struct brcmstb_reset *priv = to_brcmstb(rcdev);

writel_relaxed(SW_INIT_BIT(id), priv->base + off + SW_INIT_CLEAR);
/* Maximum reset delay after de-asserting a line and seeing block
* operation is typically 14us for the worst case, build some slack
* here.
*/
usleep_range(100, 200);

return 0;
}

static int brcmstb_reset_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE;
struct brcmstb_reset *priv = to_brcmstb(rcdev);

return readl_relaxed(priv->base + off + SW_INIT_STATUS) &
SW_INIT_BIT(id);
}

static const struct reset_control_ops brcmstb_reset_ops = {
.assert = brcmstb_reset_assert,
.deassert = brcmstb_reset_deassert,
.status = brcmstb_reset_status,
};

static int brcmstb_reset_probe(struct platform_device *pdev)
{
struct device *kdev = &pdev->dev;
struct brcmstb_reset *priv;
struct resource *res;

priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!IS_ALIGNED(res->start, SW_INIT_BANK_SIZE) ||
!IS_ALIGNED(resource_size(res), SW_INIT_BANK_SIZE)) {
dev_err(kdev, "incorrect register range\n");
return -EINVAL;
}

priv->base = devm_ioremap_resource(kdev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);

dev_set_drvdata(kdev, priv);

priv->rcdev.owner = THIS_MODULE;
priv->rcdev.nr_resets = DIV_ROUND_DOWN_ULL(resource_size(res),
SW_INIT_BANK_SIZE) * 32;
priv->rcdev.ops = &brcmstb_reset_ops;
priv->rcdev.of_node = kdev->of_node;
/* Use defaults: 1 cell and simple xlate function */

return devm_reset_controller_register(kdev, &priv->rcdev);
}

static const struct of_device_id brcmstb_reset_of_match[] = {
{ .compatible = "brcm,brcmstb-reset" },
{ /* sentinel */ }
};

static struct platform_driver brcmstb_reset_driver = {
.probe = brcmstb_reset_probe,
.driver = {
.name = "brcmstb-reset",
.of_match_table = brcmstb_reset_of_match,
},
};
module_platform_driver(brcmstb_reset_driver);

MODULE_AUTHOR("Broadcom");
MODULE_DESCRIPTION("Broadcom STB reset controller");
MODULE_LICENSE("GPL");

0 comments on commit 77750bc

Please sign in to comment.