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.
 -----BEGIN PGP SIGNATURE-----
 
 iI0EABYIADUWIQRRO6F6WdpH1R0vGibVhaclGDdiwAUCW7x5rhcccC56YWJlbEBw
 ZW5ndXRyb25peC5kZQAKCRDVhaclGDdiwDy9AP9Z7aMOwFUB0ie3RtL3TVdKq0dg
 ayIYm+iKAlWjiwvr5wD/XuHhygmuY3uDyvTaQP3yoWsQe5+/ugg606Shcxe4EQE=
 =+kaf
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Arnd Bergmann 2018-10-10 13:41:16 +02:00
commit f7d87826fe
7 changed files with 215 additions and 8 deletions

View File

@ -0,0 +1,52 @@
PDC Global
======================================
This binding describes a reset-controller found on PDC-Global (Power Domain
Controller) block for Qualcomm Technologies Inc SDM845 SoCs.
Required properties:
- compatible:
Usage: required
Value type: <string>
Definition: must be:
"qcom,sdm845-pdc-global"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: must specify the base address and size of the register
space.
- #reset-cells:
Usage: required
Value type: <uint>
Definition: must be 1; cell entry represents the reset index.
Example:
pdc_reset: reset-controller@b2e0000 {
compatible = "qcom,sdm845-pdc-global";
reg = <0xb2e0000 0x20000>;
#reset-cells = <1>;
};
PDC reset clients
======================================
Device nodes that need access to reset lines should
specify them as a reset phandle in their corresponding node as
specified in reset.txt.
For a list of all valid reset indices see
<dt-bindings/reset/qcom,sdm845-pdc.h>
Example:
modem-pil@4080000 {
...
resets = <&pdc_reset PDC_MODEM_SYNC_RESET>;
reset-names = "pdc_reset";
...
};

View File

@ -98,6 +98,15 @@ config RESET_QCOM_AOSS
reset signals provided by AOSS for Modem, Venus, ADSP, reset signals provided by AOSS for Modem, Venus, ADSP,
GPU, Camera, Wireless, Display subsystem. Otherwise, say N. GPU, Camera, Wireless, Display subsystem. Otherwise, say N.
config RESET_QCOM_PDC
tristate "Qualcomm PDC Reset Driver"
depends on ARCH_QCOM || COMPILE_TEST
help
This enables the PDC (Power Domain Controller) reset driver
for Qualcomm Technologies Inc SDM845 SoCs. Say Y if you want
to control reset signals provided by PDC for Modem, Compute,
Display, GPU, Debug, AOP, Sensors, Audio, SP and APPS.
config RESET_SIMPLE config RESET_SIMPLE
bool "Simple Reset Controller Driver" if COMPILE_TEST bool "Simple Reset Controller Driver" if COMPILE_TEST
default ARCH_SOCFPGA || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED default ARCH_SOCFPGA || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED

View File

@ -16,6 +16,7 @@ obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o

View File

@ -496,28 +496,29 @@ struct reset_control *__of_reset_control_get(struct device_node *node,
break; break;
} }
} }
of_node_put(args.np);
if (!rcdev) { if (!rcdev) {
mutex_unlock(&reset_list_mutex); rstc = ERR_PTR(-EPROBE_DEFER);
return ERR_PTR(-EPROBE_DEFER); goto out;
} }
if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
mutex_unlock(&reset_list_mutex); rstc = ERR_PTR(-EINVAL);
return ERR_PTR(-EINVAL); goto out;
} }
rstc_id = rcdev->of_xlate(rcdev, &args); rstc_id = rcdev->of_xlate(rcdev, &args);
if (rstc_id < 0) { if (rstc_id < 0) {
mutex_unlock(&reset_list_mutex); rstc = ERR_PTR(rstc_id);
return ERR_PTR(rstc_id); goto out;
} }
/* reset_list_mutex also protects the rcdev's reset_control list */ /* reset_list_mutex also protects the rcdev's reset_control list */
rstc = __reset_control_get_internal(rcdev, rstc_id, shared); rstc = __reset_control_get_internal(rcdev, rstc_id, shared);
out:
mutex_unlock(&reset_list_mutex); mutex_unlock(&reset_list_mutex);
of_node_put(args.np);
return rstc; return rstc;
} }

View File

@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
#include <dt-bindings/reset/qcom,sdm845-pdc.h>
#define RPMH_PDC_SYNC_RESET 0x100
struct qcom_pdc_reset_map {
u8 bit;
};
struct qcom_pdc_reset_data {
struct reset_controller_dev rcdev;
struct regmap *regmap;
};
static const struct regmap_config sdm845_pdc_regmap_config = {
.name = "pdc-reset",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x20000,
.fast_io = true,
};
static const struct qcom_pdc_reset_map sdm845_pdc_resets[] = {
[PDC_APPS_SYNC_RESET] = {0},
[PDC_SP_SYNC_RESET] = {1},
[PDC_AUDIO_SYNC_RESET] = {2},
[PDC_SENSORS_SYNC_RESET] = {3},
[PDC_AOP_SYNC_RESET] = {4},
[PDC_DEBUG_SYNC_RESET] = {5},
[PDC_GPU_SYNC_RESET] = {6},
[PDC_DISPLAY_SYNC_RESET] = {7},
[PDC_COMPUTE_SYNC_RESET] = {8},
[PDC_MODEM_SYNC_RESET] = {9},
};
static inline struct qcom_pdc_reset_data *to_qcom_pdc_reset_data(
struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct qcom_pdc_reset_data, rcdev);
}
static int qcom_pdc_control_assert(struct reset_controller_dev *rcdev,
unsigned long idx)
{
struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev);
return regmap_update_bits(data->regmap, RPMH_PDC_SYNC_RESET,
BIT(sdm845_pdc_resets[idx].bit),
BIT(sdm845_pdc_resets[idx].bit));
}
static int qcom_pdc_control_deassert(struct reset_controller_dev *rcdev,
unsigned long idx)
{
struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev);
return regmap_update_bits(data->regmap, RPMH_PDC_SYNC_RESET,
BIT(sdm845_pdc_resets[idx].bit), 0);
}
static const struct reset_control_ops qcom_pdc_reset_ops = {
.assert = qcom_pdc_control_assert,
.deassert = qcom_pdc_control_deassert,
};
static int qcom_pdc_reset_probe(struct platform_device *pdev)
{
struct qcom_pdc_reset_data *data;
struct device *dev = &pdev->dev;
void __iomem *base;
struct resource *res;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
data->regmap = devm_regmap_init_mmio(dev, base,
&sdm845_pdc_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(dev, "Unable to initialize regmap\n");
return PTR_ERR(data->regmap);
}
data->rcdev.owner = THIS_MODULE;
data->rcdev.ops = &qcom_pdc_reset_ops;
data->rcdev.nr_resets = ARRAY_SIZE(sdm845_pdc_resets);
data->rcdev.of_node = dev->of_node;
return devm_reset_controller_register(dev, &data->rcdev);
}
static const struct of_device_id qcom_pdc_reset_of_match[] = {
{ .compatible = "qcom,sdm845-pdc-global" },
{}
};
MODULE_DEVICE_TABLE(of, qcom_pdc_reset_of_match);
static struct platform_driver qcom_pdc_reset_driver = {
.probe = qcom_pdc_reset_probe,
.driver = {
.name = "qcom_pdc_reset",
.of_match_table = qcom_pdc_reset_of_match,
},
};
module_platform_driver(qcom_pdc_reset_driver);
MODULE_DESCRIPTION("Qualcomm PDC Reset Driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2018 The Linux Foundation. All rights reserved.
*/
#ifndef _DT_BINDINGS_RESET_PDC_SDM_845_H
#define _DT_BINDINGS_RESET_PDC_SDM_845_H
#define PDC_APPS_SYNC_RESET 0
#define PDC_SP_SYNC_RESET 1
#define PDC_AUDIO_SYNC_RESET 2
#define PDC_SENSORS_SYNC_RESET 3
#define PDC_AOP_SYNC_RESET 4
#define PDC_DEBUG_SYNC_RESET 5
#define PDC_GPU_SYNC_RESET 6
#define PDC_DISPLAY_SYNC_RESET 7
#define PDC_COMPUTE_SYNC_RESET 8
#define PDC_MODEM_SYNC_RESET 9
#endif

View File

@ -116,7 +116,7 @@ static inline int device_reset_optional(struct device *dev)
* @id: reset line name * @id: reset line name
* *
* Returns a struct reset_control or IS_ERR() condition containing errno. * Returns a struct reset_control or IS_ERR() condition containing errno.
* If this function is called more then once for the same reset_control it will * If this function is called more than once for the same reset_control it will
* return -EBUSY. * return -EBUSY.
* *
* See reset_control_get_shared for details on shared references to * See reset_control_get_shared for details on shared references to