1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * BCM6345 Reset Controller Driver |
4 | * |
5 | * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/delay.h> |
9 | #include <linux/init.h> |
10 | #include <linux/io.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/reset-controller.h> |
14 | |
15 | #define BCM6345_RESET_NUM 32 |
16 | #define BCM6345_RESET_SLEEP_MIN_US 10000 |
17 | #define BCM6345_RESET_SLEEP_MAX_US 20000 |
18 | |
19 | struct bcm6345_reset { |
20 | struct reset_controller_dev rcdev; |
21 | void __iomem *base; |
22 | spinlock_t lock; |
23 | }; |
24 | |
25 | static inline struct bcm6345_reset * |
26 | to_bcm6345_reset(struct reset_controller_dev *rcdev) |
27 | { |
28 | return container_of(rcdev, struct bcm6345_reset, rcdev); |
29 | } |
30 | |
31 | static int bcm6345_reset_update(struct reset_controller_dev *rcdev, |
32 | unsigned long id, bool assert) |
33 | { |
34 | struct bcm6345_reset *bcm6345_reset = to_bcm6345_reset(rcdev); |
35 | unsigned long flags; |
36 | uint32_t val; |
37 | |
38 | spin_lock_irqsave(&bcm6345_reset->lock, flags); |
39 | val = __raw_readl(addr: bcm6345_reset->base); |
40 | if (assert) |
41 | val &= ~BIT(id); |
42 | else |
43 | val |= BIT(id); |
44 | __raw_writel(val, addr: bcm6345_reset->base); |
45 | spin_unlock_irqrestore(lock: &bcm6345_reset->lock, flags); |
46 | |
47 | return 0; |
48 | } |
49 | |
50 | static int bcm6345_reset_assert(struct reset_controller_dev *rcdev, |
51 | unsigned long id) |
52 | { |
53 | return bcm6345_reset_update(rcdev, id, assert: true); |
54 | } |
55 | |
56 | static int bcm6345_reset_deassert(struct reset_controller_dev *rcdev, |
57 | unsigned long id) |
58 | { |
59 | return bcm6345_reset_update(rcdev, id, assert: false); |
60 | } |
61 | |
62 | static int bcm6345_reset_reset(struct reset_controller_dev *rcdev, |
63 | unsigned long id) |
64 | { |
65 | bcm6345_reset_update(rcdev, id, assert: true); |
66 | usleep_range(BCM6345_RESET_SLEEP_MIN_US, |
67 | BCM6345_RESET_SLEEP_MAX_US); |
68 | |
69 | bcm6345_reset_update(rcdev, id, assert: false); |
70 | /* |
71 | * Ensure component is taken out reset state by sleeping also after |
72 | * deasserting the reset. Otherwise, the component may not be ready |
73 | * for operation. |
74 | */ |
75 | usleep_range(BCM6345_RESET_SLEEP_MIN_US, |
76 | BCM6345_RESET_SLEEP_MAX_US); |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | static int bcm6345_reset_status(struct reset_controller_dev *rcdev, |
82 | unsigned long id) |
83 | { |
84 | struct bcm6345_reset *bcm6345_reset = to_bcm6345_reset(rcdev); |
85 | |
86 | return !(__raw_readl(addr: bcm6345_reset->base) & BIT(id)); |
87 | } |
88 | |
89 | static const struct reset_control_ops bcm6345_reset_ops = { |
90 | .assert = bcm6345_reset_assert, |
91 | .deassert = bcm6345_reset_deassert, |
92 | .reset = bcm6345_reset_reset, |
93 | .status = bcm6345_reset_status, |
94 | }; |
95 | |
96 | static int bcm6345_reset_probe(struct platform_device *pdev) |
97 | { |
98 | struct bcm6345_reset *bcm6345_reset; |
99 | |
100 | bcm6345_reset = devm_kzalloc(dev: &pdev->dev, |
101 | size: sizeof(*bcm6345_reset), GFP_KERNEL); |
102 | if (!bcm6345_reset) |
103 | return -ENOMEM; |
104 | |
105 | bcm6345_reset->base = devm_platform_ioremap_resource(pdev, index: 0); |
106 | if (IS_ERR(ptr: bcm6345_reset->base)) |
107 | return PTR_ERR(ptr: bcm6345_reset->base); |
108 | |
109 | spin_lock_init(&bcm6345_reset->lock); |
110 | bcm6345_reset->rcdev.ops = &bcm6345_reset_ops; |
111 | bcm6345_reset->rcdev.owner = THIS_MODULE; |
112 | bcm6345_reset->rcdev.of_node = pdev->dev.of_node; |
113 | bcm6345_reset->rcdev.of_reset_n_cells = 1; |
114 | bcm6345_reset->rcdev.nr_resets = BCM6345_RESET_NUM; |
115 | |
116 | return devm_reset_controller_register(dev: &pdev->dev, |
117 | rcdev: &bcm6345_reset->rcdev); |
118 | } |
119 | |
120 | static const struct of_device_id bcm6345_reset_of_match[] = { |
121 | { .compatible = "brcm,bcm6345-reset" }, |
122 | { /* sentinel */ }, |
123 | }; |
124 | |
125 | static struct platform_driver bcm6345_reset_driver = { |
126 | .probe = bcm6345_reset_probe, |
127 | .driver = { |
128 | .name = "bcm6345-reset" , |
129 | .of_match_table = bcm6345_reset_of_match, |
130 | .suppress_bind_attrs = true, |
131 | }, |
132 | }; |
133 | builtin_platform_driver(bcm6345_reset_driver); |
134 | |