Commit | Line | Data |
---|---|---|
b4cbe606 NI |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Toshiba Visconti ARM SoC reset controller | |
4 | * | |
5 | * Copyright (c) 2021 TOSHIBA CORPORATION | |
6 | * Copyright (c) 2021 Toshiba Electronic Devices & Storage Corporation | |
7 | * | |
8 | * Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp> | |
9 | */ | |
10 | #include <linux/delay.h> | |
11 | #include <linux/device.h> | |
12 | #include <linux/mfd/syscon.h> | |
13 | #include <linux/regmap.h> | |
14 | #include <linux/slab.h> | |
15 | ||
16 | #include "reset.h" | |
17 | ||
18 | static inline struct visconti_reset *to_visconti_reset(struct reset_controller_dev *rcdev) | |
19 | { | |
20 | return container_of(rcdev, struct visconti_reset, rcdev); | |
21 | } | |
22 | ||
23 | static int visconti_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) | |
24 | { | |
25 | struct visconti_reset *reset = to_visconti_reset(rcdev); | |
26 | const struct visconti_reset_data *data = &reset->resets[id]; | |
27 | u32 rst = BIT(data->rs_idx); | |
28 | unsigned long flags; | |
29 | int ret; | |
30 | ||
31 | spin_lock_irqsave(reset->lock, flags); | |
32 | ret = regmap_update_bits(reset->regmap, data->rson_offset, rst, rst); | |
33 | spin_unlock_irqrestore(reset->lock, flags); | |
34 | ||
35 | return ret; | |
36 | } | |
37 | ||
38 | static int visconti_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) | |
39 | { | |
40 | struct visconti_reset *reset = to_visconti_reset(rcdev); | |
41 | const struct visconti_reset_data *data = &reset->resets[id]; | |
42 | u32 rst = BIT(data->rs_idx); | |
43 | unsigned long flags; | |
44 | int ret; | |
45 | ||
46 | spin_lock_irqsave(reset->lock, flags); | |
47 | ret = regmap_update_bits(reset->regmap, data->rsoff_offset, rst, rst); | |
48 | spin_unlock_irqrestore(reset->lock, flags); | |
49 | ||
50 | return ret; | |
51 | } | |
52 | ||
53 | static int visconti_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) | |
54 | { | |
55 | visconti_reset_assert(rcdev, id); | |
56 | udelay(1); | |
57 | visconti_reset_deassert(rcdev, id); | |
58 | ||
59 | return 0; | |
60 | } | |
61 | ||
62 | static int visconti_reset_status(struct reset_controller_dev *rcdev, unsigned long id) | |
63 | { | |
64 | struct visconti_reset *reset = to_visconti_reset(rcdev); | |
65 | const struct visconti_reset_data *data = &reset->resets[id]; | |
66 | unsigned long flags; | |
67 | u32 reg; | |
68 | int ret; | |
69 | ||
70 | spin_lock_irqsave(reset->lock, flags); | |
71 | ret = regmap_read(reset->regmap, data->rson_offset, ®); | |
72 | spin_unlock_irqrestore(reset->lock, flags); | |
73 | if (ret) | |
74 | return ret; | |
75 | ||
76 | return !(reg & data->rs_idx); | |
77 | } | |
78 | ||
79 | const struct reset_control_ops visconti_reset_ops = { | |
80 | .assert = visconti_reset_assert, | |
81 | .deassert = visconti_reset_deassert, | |
82 | .reset = visconti_reset_reset, | |
83 | .status = visconti_reset_status, | |
84 | }; | |
85 | ||
86 | int visconti_register_reset_controller(struct device *dev, | |
87 | struct regmap *regmap, | |
88 | const struct visconti_reset_data *resets, | |
89 | unsigned int num_resets, | |
90 | const struct reset_control_ops *reset_ops, | |
91 | spinlock_t *lock) | |
92 | { | |
93 | struct visconti_reset *reset; | |
94 | ||
95 | reset = devm_kzalloc(dev, sizeof(*reset), GFP_KERNEL); | |
96 | if (!reset) | |
97 | return -ENOMEM; | |
98 | ||
99 | reset->regmap = regmap; | |
100 | reset->resets = resets; | |
101 | reset->rcdev.ops = reset_ops; | |
102 | reset->rcdev.nr_resets = num_resets; | |
103 | reset->rcdev.of_node = dev->of_node; | |
104 | reset->lock = lock; | |
105 | ||
106 | return devm_reset_controller_register(dev, &reset->rcdev); | |
107 | } |