1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2018 The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/module.h> |
7 | #include <linux/platform_device.h> |
8 | #include <linux/reset-controller.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/io.h> |
11 | #include <linux/of.h> |
12 | #include <dt-bindings/reset/qcom,sdm845-aoss.h> |
13 | |
14 | struct qcom_aoss_reset_map { |
15 | unsigned int reg; |
16 | }; |
17 | |
18 | struct qcom_aoss_desc { |
19 | const struct qcom_aoss_reset_map *resets; |
20 | size_t num_resets; |
21 | }; |
22 | |
23 | struct qcom_aoss_reset_data { |
24 | struct reset_controller_dev rcdev; |
25 | void __iomem *base; |
26 | const struct qcom_aoss_desc *desc; |
27 | }; |
28 | |
29 | static const struct qcom_aoss_reset_map sdm845_aoss_resets[] = { |
30 | [AOSS_CC_MSS_RESTART] = {0x10000}, |
31 | [AOSS_CC_CAMSS_RESTART] = {0x11000}, |
32 | [AOSS_CC_VENUS_RESTART] = {0x12000}, |
33 | [AOSS_CC_GPU_RESTART] = {0x13000}, |
34 | [AOSS_CC_DISPSS_RESTART] = {0x14000}, |
35 | [AOSS_CC_WCSS_RESTART] = {0x20000}, |
36 | [AOSS_CC_LPASS_RESTART] = {0x30000}, |
37 | }; |
38 | |
39 | static const struct qcom_aoss_desc sdm845_aoss_desc = { |
40 | .resets = sdm845_aoss_resets, |
41 | .num_resets = ARRAY_SIZE(sdm845_aoss_resets), |
42 | }; |
43 | |
44 | static inline struct qcom_aoss_reset_data *to_qcom_aoss_reset_data( |
45 | struct reset_controller_dev *rcdev) |
46 | { |
47 | return container_of(rcdev, struct qcom_aoss_reset_data, rcdev); |
48 | } |
49 | |
50 | static int qcom_aoss_control_assert(struct reset_controller_dev *rcdev, |
51 | unsigned long idx) |
52 | { |
53 | struct qcom_aoss_reset_data *data = to_qcom_aoss_reset_data(rcdev); |
54 | const struct qcom_aoss_reset_map *map = &data->desc->resets[idx]; |
55 | |
56 | writel(val: 1, addr: data->base + map->reg); |
57 | /* Wait 6 32kHz sleep cycles for reset */ |
58 | usleep_range(min: 200, max: 300); |
59 | return 0; |
60 | } |
61 | |
62 | static int qcom_aoss_control_deassert(struct reset_controller_dev *rcdev, |
63 | unsigned long idx) |
64 | { |
65 | struct qcom_aoss_reset_data *data = to_qcom_aoss_reset_data(rcdev); |
66 | const struct qcom_aoss_reset_map *map = &data->desc->resets[idx]; |
67 | |
68 | writel(val: 0, addr: data->base + map->reg); |
69 | /* Wait 6 32kHz sleep cycles for reset */ |
70 | usleep_range(min: 200, max: 300); |
71 | return 0; |
72 | } |
73 | |
74 | static int qcom_aoss_control_reset(struct reset_controller_dev *rcdev, |
75 | unsigned long idx) |
76 | { |
77 | qcom_aoss_control_assert(rcdev, idx); |
78 | |
79 | return qcom_aoss_control_deassert(rcdev, idx); |
80 | } |
81 | |
82 | static const struct reset_control_ops qcom_aoss_reset_ops = { |
83 | .reset = qcom_aoss_control_reset, |
84 | .assert = qcom_aoss_control_assert, |
85 | .deassert = qcom_aoss_control_deassert, |
86 | }; |
87 | |
88 | static int qcom_aoss_reset_probe(struct platform_device *pdev) |
89 | { |
90 | struct qcom_aoss_reset_data *data; |
91 | struct device *dev = &pdev->dev; |
92 | const struct qcom_aoss_desc *desc; |
93 | struct resource *res; |
94 | |
95 | desc = of_device_get_match_data(dev); |
96 | if (!desc) |
97 | return -EINVAL; |
98 | |
99 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
100 | if (!data) |
101 | return -ENOMEM; |
102 | |
103 | data->desc = desc; |
104 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
105 | data->base = devm_ioremap_resource(dev, res); |
106 | if (IS_ERR(ptr: data->base)) |
107 | return PTR_ERR(ptr: data->base); |
108 | |
109 | data->rcdev.owner = THIS_MODULE; |
110 | data->rcdev.ops = &qcom_aoss_reset_ops; |
111 | data->rcdev.nr_resets = desc->num_resets; |
112 | data->rcdev.of_node = dev->of_node; |
113 | |
114 | return devm_reset_controller_register(dev, rcdev: &data->rcdev); |
115 | } |
116 | |
117 | static const struct of_device_id qcom_aoss_reset_of_match[] = { |
118 | { .compatible = "qcom,sdm845-aoss-cc" , .data = &sdm845_aoss_desc }, |
119 | {} |
120 | }; |
121 | MODULE_DEVICE_TABLE(of, qcom_aoss_reset_of_match); |
122 | |
123 | static struct platform_driver qcom_aoss_reset_driver = { |
124 | .probe = qcom_aoss_reset_probe, |
125 | .driver = { |
126 | .name = "qcom_aoss_reset" , |
127 | .of_match_table = qcom_aoss_reset_of_match, |
128 | }, |
129 | }; |
130 | |
131 | module_platform_driver(qcom_aoss_reset_driver); |
132 | |
133 | MODULE_DESCRIPTION("Qualcomm AOSS Reset Driver" ); |
134 | MODULE_LICENSE("GPL v2" ); |
135 | |