Commit | Line | Data |
---|---|---|
77750bc0 FF |
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"); |