Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

reset: Add Broadcom STB SW_INIT reset controller driver

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 <f.fainelli@gmail.com>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>

authored by

Florian Fainelli and committed by
Philipp Zabel
77750bc0 0807caf6

+141
+8
drivers/reset/Kconfig
··· 40 40 help 41 41 This enables the reset controller driver for Marvell Berlin SoCs. 42 42 43 + config RESET_BRCMSTB 44 + tristate "Broadcom STB reset controller" 45 + depends on ARCH_BRCMSTB || COMPILE_TEST 46 + default ARCH_BRCMSTB 47 + help 48 + This enables the reset controller driver for Broadcom STB SoCs using 49 + a SUN_TOP_CTRL_SW_INIT style controller. 50 + 43 51 config RESET_HSDK 44 52 bool "Synopsys HSDK Reset Driver" 45 53 depends on HAS_IOMEM
+1
drivers/reset/Makefile
··· 7 7 obj-$(CONFIG_RESET_ATH79) += reset-ath79.o 8 8 obj-$(CONFIG_RESET_AXS10X) += reset-axs10x.o 9 9 obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o 10 + obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o 10 11 obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o 11 12 obj-$(CONFIG_RESET_IMX7) += reset-imx7.o 12 13 obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
+132
drivers/reset/reset-brcmstb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Broadcom STB generic reset controller for SW_INIT style reset controller 4 + * 5 + * Author: Florian Fainelli <f.fainelli@gmail.com> 6 + * Copyright (C) 2018 Broadcom 7 + */ 8 + #include <linux/delay.h> 9 + #include <linux/device.h> 10 + #include <linux/io.h> 11 + #include <linux/module.h> 12 + #include <linux/of.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/reset-controller.h> 15 + #include <linux/types.h> 16 + 17 + struct brcmstb_reset { 18 + void __iomem *base; 19 + struct reset_controller_dev rcdev; 20 + }; 21 + 22 + #define SW_INIT_SET 0x00 23 + #define SW_INIT_CLEAR 0x04 24 + #define SW_INIT_STATUS 0x08 25 + 26 + #define SW_INIT_BIT(id) BIT((id) & 0x1f) 27 + #define SW_INIT_BANK(id) ((id) >> 5) 28 + 29 + /* A full bank contains extra registers that we are not utilizing but still 30 + * qualify as a single bank. 31 + */ 32 + #define SW_INIT_BANK_SIZE 0x18 33 + 34 + static inline 35 + struct brcmstb_reset *to_brcmstb(struct reset_controller_dev *rcdev) 36 + { 37 + return container_of(rcdev, struct brcmstb_reset, rcdev); 38 + } 39 + 40 + static int brcmstb_reset_assert(struct reset_controller_dev *rcdev, 41 + unsigned long id) 42 + { 43 + unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE; 44 + struct brcmstb_reset *priv = to_brcmstb(rcdev); 45 + 46 + writel_relaxed(SW_INIT_BIT(id), priv->base + off + SW_INIT_SET); 47 + 48 + return 0; 49 + } 50 + 51 + static int brcmstb_reset_deassert(struct reset_controller_dev *rcdev, 52 + unsigned long id) 53 + { 54 + unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE; 55 + struct brcmstb_reset *priv = to_brcmstb(rcdev); 56 + 57 + writel_relaxed(SW_INIT_BIT(id), priv->base + off + SW_INIT_CLEAR); 58 + /* Maximum reset delay after de-asserting a line and seeing block 59 + * operation is typically 14us for the worst case, build some slack 60 + * here. 61 + */ 62 + usleep_range(100, 200); 63 + 64 + return 0; 65 + } 66 + 67 + static int brcmstb_reset_status(struct reset_controller_dev *rcdev, 68 + unsigned long id) 69 + { 70 + unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE; 71 + struct brcmstb_reset *priv = to_brcmstb(rcdev); 72 + 73 + return readl_relaxed(priv->base + off + SW_INIT_STATUS) & 74 + SW_INIT_BIT(id); 75 + } 76 + 77 + static const struct reset_control_ops brcmstb_reset_ops = { 78 + .assert = brcmstb_reset_assert, 79 + .deassert = brcmstb_reset_deassert, 80 + .status = brcmstb_reset_status, 81 + }; 82 + 83 + static int brcmstb_reset_probe(struct platform_device *pdev) 84 + { 85 + struct device *kdev = &pdev->dev; 86 + struct brcmstb_reset *priv; 87 + struct resource *res; 88 + 89 + priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL); 90 + if (!priv) 91 + return -ENOMEM; 92 + 93 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 94 + if (!IS_ALIGNED(res->start, SW_INIT_BANK_SIZE) || 95 + !IS_ALIGNED(resource_size(res), SW_INIT_BANK_SIZE)) { 96 + dev_err(kdev, "incorrect register range\n"); 97 + return -EINVAL; 98 + } 99 + 100 + priv->base = devm_ioremap_resource(kdev, res); 101 + if (IS_ERR(priv->base)) 102 + return PTR_ERR(priv->base); 103 + 104 + dev_set_drvdata(kdev, priv); 105 + 106 + priv->rcdev.owner = THIS_MODULE; 107 + priv->rcdev.nr_resets = DIV_ROUND_DOWN_ULL(resource_size(res), 108 + SW_INIT_BANK_SIZE) * 32; 109 + priv->rcdev.ops = &brcmstb_reset_ops; 110 + priv->rcdev.of_node = kdev->of_node; 111 + /* Use defaults: 1 cell and simple xlate function */ 112 + 113 + return devm_reset_controller_register(kdev, &priv->rcdev); 114 + } 115 + 116 + static const struct of_device_id brcmstb_reset_of_match[] = { 117 + { .compatible = "brcm,brcmstb-reset" }, 118 + { /* sentinel */ } 119 + }; 120 + 121 + static struct platform_driver brcmstb_reset_driver = { 122 + .probe = brcmstb_reset_probe, 123 + .driver = { 124 + .name = "brcmstb-reset", 125 + .of_match_table = brcmstb_reset_of_match, 126 + }, 127 + }; 128 + module_platform_driver(brcmstb_reset_driver); 129 + 130 + MODULE_AUTHOR("Broadcom"); 131 + MODULE_DESCRIPTION("Broadcom STB reset controller"); 132 + MODULE_LICENSE("GPL");