Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

reset: Add Raspberry Pi 4 firmware reset controller

Raspberry Pi 4's co-processor controls some of the board's HW
initialization process, but it's up to Linux to trigger it when
relevant. Introduce a reset controller capable of interfacing with
RPi4's co-processor that models these firmware initialization routines as
reset lines.

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
Link: https://lore.kernel.org/r/20200629161845.6021-3-nsaenzjulienne@suse.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Nicolas Saenz Julienne and committed by
Greg Kroah-Hartman
abffc82a 24284199

+134
+11
drivers/reset/Kconfig
··· 140 140 to control reset signals provided by PDC for Modem, Compute, 141 141 Display, GPU, Debug, AOP, Sensors, Audio, SP and APPS. 142 142 143 + config RESET_RASPBERRYPI 144 + tristate "Raspberry Pi 4 Firmware Reset Driver" 145 + depends on RASPBERRYPI_FIRMWARE || (RASPBERRYPI_FIRMWARE=n && COMPILE_TEST) 146 + default USB_XHCI_PCI 147 + help 148 + Raspberry Pi 4's co-processor controls some of the board's HW 149 + initialization process, but it's up to Linux to trigger it when 150 + relevant. This driver provides a reset controller capable of 151 + interfacing with RPi4's co-processor and model these firmware 152 + initialization routines as reset lines. 153 + 143 154 config RESET_SCMI 144 155 tristate "Reset driver controlled via ARM SCMI interface" 145 156 depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
+1
drivers/reset/Makefile
··· 21 21 obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o 22 22 obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o 23 23 obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o 24 + obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o 24 25 obj-$(CONFIG_RESET_SCMI) += reset-scmi.o 25 26 obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o 26 27 obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o
+122
drivers/reset/reset-raspberrypi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Raspberry Pi 4 firmware reset driver 4 + * 5 + * Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de> 6 + */ 7 + #include <linux/delay.h> 8 + #include <linux/device.h> 9 + #include <linux/module.h> 10 + #include <linux/of.h> 11 + #include <linux/platform_device.h> 12 + #include <linux/reset-controller.h> 13 + #include <soc/bcm2835/raspberrypi-firmware.h> 14 + #include <dt-bindings/reset/raspberrypi,firmware-reset.h> 15 + 16 + struct rpi_reset { 17 + struct reset_controller_dev rcdev; 18 + struct rpi_firmware *fw; 19 + }; 20 + 21 + static inline struct rpi_reset *to_rpi(struct reset_controller_dev *rcdev) 22 + { 23 + return container_of(rcdev, struct rpi_reset, rcdev); 24 + } 25 + 26 + static int rpi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) 27 + { 28 + struct rpi_reset *priv = to_rpi(rcdev); 29 + u32 dev_addr; 30 + int ret; 31 + 32 + switch (id) { 33 + case RASPBERRYPI_FIRMWARE_RESET_ID_USB: 34 + /* 35 + * The Raspberry Pi 4 gets its USB functionality from VL805, a 36 + * PCIe chip that implements xHCI. After a PCI reset, VL805's 37 + * firmware may either be loaded directly from an EEPROM or, if 38 + * not present, by the SoC's co-processor, VideoCore. rpi's 39 + * VideoCore OS contains both the non public firmware load 40 + * logic and the VL805 firmware blob. This triggers the 41 + * aforementioned process. 42 + * 43 + * The pci device address is expected is expected by the 44 + * firmware encoded like this: 45 + * 46 + * PCI_BUS << 20 | PCI_SLOT << 15 | PCI_FUNC << 12 47 + * 48 + * But since rpi's PCIe is hardwired, we know the address in 49 + * advance. 50 + */ 51 + dev_addr = 0x100000; 52 + ret = rpi_firmware_property(priv->fw, RPI_FIRMWARE_NOTIFY_XHCI_RESET, 53 + &dev_addr, sizeof(dev_addr)); 54 + if (ret) 55 + return ret; 56 + 57 + /* Wait for vl805 to startup */ 58 + usleep_range(200, 1000); 59 + break; 60 + 61 + default: 62 + return -EINVAL; 63 + } 64 + 65 + return 0; 66 + } 67 + 68 + static const struct reset_control_ops rpi_reset_ops = { 69 + .reset = rpi_reset_reset, 70 + }; 71 + 72 + static int rpi_reset_probe(struct platform_device *pdev) 73 + { 74 + struct device *dev = &pdev->dev; 75 + struct rpi_firmware *fw; 76 + struct device_node *np; 77 + struct rpi_reset *priv; 78 + 79 + np = of_get_parent(dev->of_node); 80 + if (!np) { 81 + dev_err(dev, "Missing firmware node\n"); 82 + return -ENOENT; 83 + } 84 + 85 + fw = rpi_firmware_get(np); 86 + of_node_put(np); 87 + if (!fw) 88 + return -EPROBE_DEFER; 89 + 90 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 91 + if (!priv) 92 + return -ENOMEM; 93 + 94 + dev_set_drvdata(dev, priv); 95 + 96 + priv->fw = fw; 97 + priv->rcdev.owner = THIS_MODULE; 98 + priv->rcdev.nr_resets = RASPBERRYPI_FIRMWARE_RESET_NUM_IDS; 99 + priv->rcdev.ops = &rpi_reset_ops; 100 + priv->rcdev.of_node = dev->of_node; 101 + 102 + return devm_reset_controller_register(dev, &priv->rcdev); 103 + } 104 + 105 + static const struct of_device_id rpi_reset_of_match[] = { 106 + { .compatible = "raspberrypi,firmware-reset" }, 107 + { /* sentinel */ } 108 + }; 109 + MODULE_DEVICE_TABLE(of, rpi_reset_of_match); 110 + 111 + static struct platform_driver rpi_reset_driver = { 112 + .probe = rpi_reset_probe, 113 + .driver = { 114 + .name = "raspberrypi-reset", 115 + .of_match_table = rpi_reset_of_match, 116 + }, 117 + }; 118 + module_platform_driver(rpi_reset_driver); 119 + 120 + MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>"); 121 + MODULE_DESCRIPTION("Raspberry Pi 4 firmware reset driver"); 122 + MODULE_LICENSE("GPL");