1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2016 Maxime Ripard |
4 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
5 | */ |
6 | |
7 | #include <linux/delay.h> |
8 | #include <linux/io.h> |
9 | #include <linux/reset-controller.h> |
10 | |
11 | #include "ccu_reset.h" |
12 | |
13 | static int ccu_reset_assert(struct reset_controller_dev *rcdev, |
14 | unsigned long id) |
15 | { |
16 | struct ccu_reset *ccu = rcdev_to_ccu_reset(rcdev); |
17 | const struct ccu_reset_map *map = &ccu->reset_map[id]; |
18 | unsigned long flags; |
19 | u32 reg; |
20 | |
21 | spin_lock_irqsave(ccu->lock, flags); |
22 | |
23 | reg = readl(addr: ccu->base + map->reg); |
24 | writel(val: reg & ~map->bit, addr: ccu->base + map->reg); |
25 | |
26 | spin_unlock_irqrestore(lock: ccu->lock, flags); |
27 | |
28 | return 0; |
29 | } |
30 | |
31 | static int ccu_reset_deassert(struct reset_controller_dev *rcdev, |
32 | unsigned long id) |
33 | { |
34 | struct ccu_reset *ccu = rcdev_to_ccu_reset(rcdev); |
35 | const struct ccu_reset_map *map = &ccu->reset_map[id]; |
36 | unsigned long flags; |
37 | u32 reg; |
38 | |
39 | spin_lock_irqsave(ccu->lock, flags); |
40 | |
41 | reg = readl(addr: ccu->base + map->reg); |
42 | writel(val: reg | map->bit, addr: ccu->base + map->reg); |
43 | |
44 | spin_unlock_irqrestore(lock: ccu->lock, flags); |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | static int ccu_reset_reset(struct reset_controller_dev *rcdev, |
50 | unsigned long id) |
51 | { |
52 | ccu_reset_assert(rcdev, id); |
53 | udelay(10); |
54 | ccu_reset_deassert(rcdev, id); |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static int ccu_reset_status(struct reset_controller_dev *rcdev, |
60 | unsigned long id) |
61 | { |
62 | struct ccu_reset *ccu = rcdev_to_ccu_reset(rcdev); |
63 | const struct ccu_reset_map *map = &ccu->reset_map[id]; |
64 | |
65 | /* |
66 | * The reset control API expects 0 if reset is not asserted, |
67 | * which is the opposite of what our hardware uses. |
68 | */ |
69 | return !(map->bit & readl(addr: ccu->base + map->reg)); |
70 | } |
71 | |
72 | const struct reset_control_ops ccu_reset_ops = { |
73 | .assert = ccu_reset_assert, |
74 | .deassert = ccu_reset_deassert, |
75 | .reset = ccu_reset_reset, |
76 | .status = ccu_reset_status, |
77 | }; |
78 | EXPORT_SYMBOL_NS_GPL(ccu_reset_ops, SUNXI_CCU); |
79 | |