diff options
author | Arnd Bergmann <arnd@arndb.de> | 2018-10-10 07:41:16 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2018-10-10 07:41:22 -0400 |
commit | f7d87826fe482dbe4b0675f99c1eae2890a2d8fb (patch) | |
tree | 2640aec2ab83e9958930a7ba9b1738ed5103355e /drivers | |
parent | b0a2cea5eb637d540ba86fd31dfd750f26ee0161 (diff) | |
parent | b790c8ea5593d6dc3580adfad8e117eeb56af874 (diff) |
Merge tag 'reset-for-4.20' of git://git.pengutronix.de/git/pza/linux into next/drivers
Reset controller changes for v4.20
This adds a new driver for the PDC Global (Power Domain Controller)
reset controller found on Qualcomm SDM845 SoCs, fixes a potential
use-after-free issue in reset_controller_dev.of_xlate() callbacks
from __of_reset_control_get(), and trivially fixes a documentation
grammar issue.
* tag 'reset-for-4.20' of git://git.pengutronix.de/git/pza/linux:
reset: Fix potential use-after-free in __of_reset_control_get()
reset: qcom: PDC Global (Power Domain Controller) reset controller
dt-bindings: reset: Add PDC Global binding for SDM845 SoCs
reset: Grammar s/more then once/more than once/
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/reset/Kconfig | 9 | ||||
-rw-r--r-- | drivers/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/reset/core.c | 15 | ||||
-rw-r--r-- | drivers/reset/reset-qcom-pdc.c | 124 |
4 files changed, 142 insertions, 7 deletions
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 13d28fdbdbb5..c21da9fe51ec 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig | |||
@@ -98,6 +98,15 @@ config RESET_QCOM_AOSS | |||
98 | reset signals provided by AOSS for Modem, Venus, ADSP, | 98 | reset signals provided by AOSS for Modem, Venus, ADSP, |
99 | GPU, Camera, Wireless, Display subsystem. Otherwise, say N. | 99 | GPU, Camera, Wireless, Display subsystem. Otherwise, say N. |
100 | 100 | ||
101 | config RESET_QCOM_PDC | ||
102 | tristate "Qualcomm PDC Reset Driver" | ||
103 | depends on ARCH_QCOM || COMPILE_TEST | ||
104 | help | ||
105 | This enables the PDC (Power Domain Controller) reset driver | ||
106 | for Qualcomm Technologies Inc SDM845 SoCs. Say Y if you want | ||
107 | to control reset signals provided by PDC for Modem, Compute, | ||
108 | Display, GPU, Debug, AOP, Sensors, Audio, SP and APPS. | ||
109 | |||
101 | config RESET_SIMPLE | 110 | config RESET_SIMPLE |
102 | bool "Simple Reset Controller Driver" if COMPILE_TEST | 111 | bool "Simple Reset Controller Driver" if COMPILE_TEST |
103 | default ARCH_SOCFPGA || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED | 112 | default ARCH_SOCFPGA || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED |
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 4243c38228e2..d08e8b90046a 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile | |||
@@ -16,6 +16,7 @@ obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o | |||
16 | obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o | 16 | obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o |
17 | obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o | 17 | obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o |
18 | obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o | 18 | obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o |
19 | obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o | ||
19 | obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o | 20 | obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o |
20 | obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o | 21 | obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o |
21 | obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o | 22 | obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o |
diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 225e34c56b94..d1887c0ed5d3 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c | |||
@@ -496,28 +496,29 @@ struct reset_control *__of_reset_control_get(struct device_node *node, | |||
496 | break; | 496 | break; |
497 | } | 497 | } |
498 | } | 498 | } |
499 | of_node_put(args.np); | ||
500 | 499 | ||
501 | if (!rcdev) { | 500 | if (!rcdev) { |
502 | mutex_unlock(&reset_list_mutex); | 501 | rstc = ERR_PTR(-EPROBE_DEFER); |
503 | return ERR_PTR(-EPROBE_DEFER); | 502 | goto out; |
504 | } | 503 | } |
505 | 504 | ||
506 | if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { | 505 | if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { |
507 | mutex_unlock(&reset_list_mutex); | 506 | rstc = ERR_PTR(-EINVAL); |
508 | return ERR_PTR(-EINVAL); | 507 | goto out; |
509 | } | 508 | } |
510 | 509 | ||
511 | rstc_id = rcdev->of_xlate(rcdev, &args); | 510 | rstc_id = rcdev->of_xlate(rcdev, &args); |
512 | if (rstc_id < 0) { | 511 | if (rstc_id < 0) { |
513 | mutex_unlock(&reset_list_mutex); | 512 | rstc = ERR_PTR(rstc_id); |
514 | return ERR_PTR(rstc_id); | 513 | goto out; |
515 | } | 514 | } |
516 | 515 | ||
517 | /* reset_list_mutex also protects the rcdev's reset_control list */ | 516 | /* reset_list_mutex also protects the rcdev's reset_control list */ |
518 | rstc = __reset_control_get_internal(rcdev, rstc_id, shared); | 517 | rstc = __reset_control_get_internal(rcdev, rstc_id, shared); |
519 | 518 | ||
519 | out: | ||
520 | mutex_unlock(&reset_list_mutex); | 520 | mutex_unlock(&reset_list_mutex); |
521 | of_node_put(args.np); | ||
521 | 522 | ||
522 | return rstc; | 523 | return rstc; |
523 | } | 524 | } |
diff --git a/drivers/reset/reset-qcom-pdc.c b/drivers/reset/reset-qcom-pdc.c new file mode 100644 index 000000000000..ab74bccd4a5b --- /dev/null +++ b/drivers/reset/reset-qcom-pdc.c | |||
@@ -0,0 +1,124 @@ | |||
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/of_device.h> | ||
8 | #include <linux/platform_device.h> | ||
9 | #include <linux/regmap.h> | ||
10 | #include <linux/reset-controller.h> | ||
11 | |||
12 | #include <dt-bindings/reset/qcom,sdm845-pdc.h> | ||
13 | |||
14 | #define RPMH_PDC_SYNC_RESET 0x100 | ||
15 | |||
16 | struct qcom_pdc_reset_map { | ||
17 | u8 bit; | ||
18 | }; | ||
19 | |||
20 | struct qcom_pdc_reset_data { | ||
21 | struct reset_controller_dev rcdev; | ||
22 | struct regmap *regmap; | ||
23 | }; | ||
24 | |||
25 | static const struct regmap_config sdm845_pdc_regmap_config = { | ||
26 | .name = "pdc-reset", | ||
27 | .reg_bits = 32, | ||
28 | .reg_stride = 4, | ||
29 | .val_bits = 32, | ||
30 | .max_register = 0x20000, | ||
31 | .fast_io = true, | ||
32 | }; | ||
33 | |||
34 | static const struct qcom_pdc_reset_map sdm845_pdc_resets[] = { | ||
35 | [PDC_APPS_SYNC_RESET] = {0}, | ||
36 | [PDC_SP_SYNC_RESET] = {1}, | ||
37 | [PDC_AUDIO_SYNC_RESET] = {2}, | ||
38 | [PDC_SENSORS_SYNC_RESET] = {3}, | ||
39 | [PDC_AOP_SYNC_RESET] = {4}, | ||
40 | [PDC_DEBUG_SYNC_RESET] = {5}, | ||
41 | [PDC_GPU_SYNC_RESET] = {6}, | ||
42 | [PDC_DISPLAY_SYNC_RESET] = {7}, | ||
43 | [PDC_COMPUTE_SYNC_RESET] = {8}, | ||
44 | [PDC_MODEM_SYNC_RESET] = {9}, | ||
45 | }; | ||
46 | |||
47 | static inline struct qcom_pdc_reset_data *to_qcom_pdc_reset_data( | ||
48 | struct reset_controller_dev *rcdev) | ||
49 | { | ||
50 | return container_of(rcdev, struct qcom_pdc_reset_data, rcdev); | ||
51 | } | ||
52 | |||
53 | static int qcom_pdc_control_assert(struct reset_controller_dev *rcdev, | ||
54 | unsigned long idx) | ||
55 | { | ||
56 | struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev); | ||
57 | |||
58 | return regmap_update_bits(data->regmap, RPMH_PDC_SYNC_RESET, | ||
59 | BIT(sdm845_pdc_resets[idx].bit), | ||
60 | BIT(sdm845_pdc_resets[idx].bit)); | ||
61 | } | ||
62 | |||
63 | static int qcom_pdc_control_deassert(struct reset_controller_dev *rcdev, | ||
64 | unsigned long idx) | ||
65 | { | ||
66 | struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev); | ||
67 | |||
68 | return regmap_update_bits(data->regmap, RPMH_PDC_SYNC_RESET, | ||
69 | BIT(sdm845_pdc_resets[idx].bit), 0); | ||
70 | } | ||
71 | |||
72 | static const struct reset_control_ops qcom_pdc_reset_ops = { | ||
73 | .assert = qcom_pdc_control_assert, | ||
74 | .deassert = qcom_pdc_control_deassert, | ||
75 | }; | ||
76 | |||
77 | static int qcom_pdc_reset_probe(struct platform_device *pdev) | ||
78 | { | ||
79 | struct qcom_pdc_reset_data *data; | ||
80 | struct device *dev = &pdev->dev; | ||
81 | void __iomem *base; | ||
82 | struct resource *res; | ||
83 | |||
84 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||
85 | if (!data) | ||
86 | return -ENOMEM; | ||
87 | |||
88 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
89 | base = devm_ioremap_resource(dev, res); | ||
90 | if (IS_ERR(base)) | ||
91 | return PTR_ERR(base); | ||
92 | |||
93 | data->regmap = devm_regmap_init_mmio(dev, base, | ||
94 | &sdm845_pdc_regmap_config); | ||
95 | if (IS_ERR(data->regmap)) { | ||
96 | dev_err(dev, "Unable to initialize regmap\n"); | ||
97 | return PTR_ERR(data->regmap); | ||
98 | } | ||
99 | |||
100 | data->rcdev.owner = THIS_MODULE; | ||
101 | data->rcdev.ops = &qcom_pdc_reset_ops; | ||
102 | data->rcdev.nr_resets = ARRAY_SIZE(sdm845_pdc_resets); | ||
103 | data->rcdev.of_node = dev->of_node; | ||
104 | |||
105 | return devm_reset_controller_register(dev, &data->rcdev); | ||
106 | } | ||
107 | |||
108 | static const struct of_device_id qcom_pdc_reset_of_match[] = { | ||
109 | { .compatible = "qcom,sdm845-pdc-global" }, | ||
110 | {} | ||
111 | }; | ||
112 | MODULE_DEVICE_TABLE(of, qcom_pdc_reset_of_match); | ||
113 | |||
114 | static struct platform_driver qcom_pdc_reset_driver = { | ||
115 | .probe = qcom_pdc_reset_probe, | ||
116 | .driver = { | ||
117 | .name = "qcom_pdc_reset", | ||
118 | .of_match_table = qcom_pdc_reset_of_match, | ||
119 | }, | ||
120 | }; | ||
121 | module_platform_driver(qcom_pdc_reset_driver); | ||
122 | |||
123 | MODULE_DESCRIPTION("Qualcomm PDC Reset Driver"); | ||
124 | MODULE_LICENSE("GPL v2"); | ||