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(min: 100, max: 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(dev: kdev, size: sizeof(*priv), GFP_KERNEL); |
90 | if (!priv) |
91 | return -ENOMEM; |
92 | |
93 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
94 | priv->base = devm_ioremap_resource(dev: kdev, res); |
95 | if (IS_ERR(ptr: priv->base)) |
96 | return PTR_ERR(ptr: priv->base); |
97 | |
98 | dev_set_drvdata(dev: kdev, data: priv); |
99 | |
100 | priv->rcdev.owner = THIS_MODULE; |
101 | priv->rcdev.nr_resets = DIV_ROUND_DOWN_ULL(resource_size(res), |
102 | SW_INIT_BANK_SIZE) * 32; |
103 | priv->rcdev.ops = &brcmstb_reset_ops; |
104 | priv->rcdev.of_node = kdev->of_node; |
105 | /* Use defaults: 1 cell and simple xlate function */ |
106 | |
107 | return devm_reset_controller_register(dev: kdev, rcdev: &priv->rcdev); |
108 | } |
109 | |
110 | static const struct of_device_id brcmstb_reset_of_match[] = { |
111 | { .compatible = "brcm,brcmstb-reset" }, |
112 | { /* sentinel */ } |
113 | }; |
114 | MODULE_DEVICE_TABLE(of, brcmstb_reset_of_match); |
115 | |
116 | static struct platform_driver brcmstb_reset_driver = { |
117 | .probe = brcmstb_reset_probe, |
118 | .driver = { |
119 | .name = "brcmstb-reset" , |
120 | .of_match_table = brcmstb_reset_of_match, |
121 | }, |
122 | }; |
123 | module_platform_driver(brcmstb_reset_driver); |
124 | |
125 | MODULE_AUTHOR("Broadcom" ); |
126 | MODULE_DESCRIPTION("Broadcom STB reset controller" ); |
127 | MODULE_LICENSE("GPL" ); |
128 | |