1abffc82aSNicolas Saenz Julienne // SPDX-License-Identifier: GPL-2.0
2abffc82aSNicolas Saenz Julienne /*
3abffc82aSNicolas Saenz Julienne * Raspberry Pi 4 firmware reset driver
4abffc82aSNicolas Saenz Julienne *
5abffc82aSNicolas Saenz Julienne * Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
6abffc82aSNicolas Saenz Julienne */
7abffc82aSNicolas Saenz Julienne #include <linux/delay.h>
8abffc82aSNicolas Saenz Julienne #include <linux/device.h>
9abffc82aSNicolas Saenz Julienne #include <linux/module.h>
10abffc82aSNicolas Saenz Julienne #include <linux/of.h>
11abffc82aSNicolas Saenz Julienne #include <linux/platform_device.h>
12abffc82aSNicolas Saenz Julienne #include <linux/reset-controller.h>
13abffc82aSNicolas Saenz Julienne #include <soc/bcm2835/raspberrypi-firmware.h>
14abffc82aSNicolas Saenz Julienne #include <dt-bindings/reset/raspberrypi,firmware-reset.h>
15abffc82aSNicolas Saenz Julienne
16abffc82aSNicolas Saenz Julienne struct rpi_reset {
17abffc82aSNicolas Saenz Julienne struct reset_controller_dev rcdev;
18abffc82aSNicolas Saenz Julienne struct rpi_firmware *fw;
19abffc82aSNicolas Saenz Julienne };
20abffc82aSNicolas Saenz Julienne
to_rpi(struct reset_controller_dev * rcdev)21abffc82aSNicolas Saenz Julienne static inline struct rpi_reset *to_rpi(struct reset_controller_dev *rcdev)
22abffc82aSNicolas Saenz Julienne {
23abffc82aSNicolas Saenz Julienne return container_of(rcdev, struct rpi_reset, rcdev);
24abffc82aSNicolas Saenz Julienne }
25abffc82aSNicolas Saenz Julienne
rpi_reset_reset(struct reset_controller_dev * rcdev,unsigned long id)26abffc82aSNicolas Saenz Julienne static int rpi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
27abffc82aSNicolas Saenz Julienne {
28abffc82aSNicolas Saenz Julienne struct rpi_reset *priv = to_rpi(rcdev);
29abffc82aSNicolas Saenz Julienne u32 dev_addr;
30abffc82aSNicolas Saenz Julienne int ret;
31abffc82aSNicolas Saenz Julienne
32abffc82aSNicolas Saenz Julienne switch (id) {
33abffc82aSNicolas Saenz Julienne case RASPBERRYPI_FIRMWARE_RESET_ID_USB:
34abffc82aSNicolas Saenz Julienne /*
35abffc82aSNicolas Saenz Julienne * The Raspberry Pi 4 gets its USB functionality from VL805, a
36abffc82aSNicolas Saenz Julienne * PCIe chip that implements xHCI. After a PCI reset, VL805's
37abffc82aSNicolas Saenz Julienne * firmware may either be loaded directly from an EEPROM or, if
38abffc82aSNicolas Saenz Julienne * not present, by the SoC's co-processor, VideoCore. rpi's
39abffc82aSNicolas Saenz Julienne * VideoCore OS contains both the non public firmware load
40abffc82aSNicolas Saenz Julienne * logic and the VL805 firmware blob. This triggers the
41abffc82aSNicolas Saenz Julienne * aforementioned process.
42abffc82aSNicolas Saenz Julienne *
43abffc82aSNicolas Saenz Julienne * The pci device address is expected is expected by the
44abffc82aSNicolas Saenz Julienne * firmware encoded like this:
45abffc82aSNicolas Saenz Julienne *
46abffc82aSNicolas Saenz Julienne * PCI_BUS << 20 | PCI_SLOT << 15 | PCI_FUNC << 12
47abffc82aSNicolas Saenz Julienne *
48abffc82aSNicolas Saenz Julienne * But since rpi's PCIe is hardwired, we know the address in
49abffc82aSNicolas Saenz Julienne * advance.
50abffc82aSNicolas Saenz Julienne */
51abffc82aSNicolas Saenz Julienne dev_addr = 0x100000;
52abffc82aSNicolas Saenz Julienne ret = rpi_firmware_property(priv->fw, RPI_FIRMWARE_NOTIFY_XHCI_RESET,
53abffc82aSNicolas Saenz Julienne &dev_addr, sizeof(dev_addr));
54abffc82aSNicolas Saenz Julienne if (ret)
55abffc82aSNicolas Saenz Julienne return ret;
56abffc82aSNicolas Saenz Julienne
57abffc82aSNicolas Saenz Julienne /* Wait for vl805 to startup */
58abffc82aSNicolas Saenz Julienne usleep_range(200, 1000);
59abffc82aSNicolas Saenz Julienne break;
60abffc82aSNicolas Saenz Julienne
61abffc82aSNicolas Saenz Julienne default:
62abffc82aSNicolas Saenz Julienne return -EINVAL;
63abffc82aSNicolas Saenz Julienne }
64abffc82aSNicolas Saenz Julienne
65abffc82aSNicolas Saenz Julienne return 0;
66abffc82aSNicolas Saenz Julienne }
67abffc82aSNicolas Saenz Julienne
68abffc82aSNicolas Saenz Julienne static const struct reset_control_ops rpi_reset_ops = {
69abffc82aSNicolas Saenz Julienne .reset = rpi_reset_reset,
70abffc82aSNicolas Saenz Julienne };
71abffc82aSNicolas Saenz Julienne
rpi_reset_probe(struct platform_device * pdev)72abffc82aSNicolas Saenz Julienne static int rpi_reset_probe(struct platform_device *pdev)
73abffc82aSNicolas Saenz Julienne {
74abffc82aSNicolas Saenz Julienne struct device *dev = &pdev->dev;
75abffc82aSNicolas Saenz Julienne struct rpi_firmware *fw;
76abffc82aSNicolas Saenz Julienne struct device_node *np;
77abffc82aSNicolas Saenz Julienne struct rpi_reset *priv;
78abffc82aSNicolas Saenz Julienne
79abffc82aSNicolas Saenz Julienne np = of_get_parent(dev->of_node);
80abffc82aSNicolas Saenz Julienne if (!np) {
81abffc82aSNicolas Saenz Julienne dev_err(dev, "Missing firmware node\n");
82abffc82aSNicolas Saenz Julienne return -ENOENT;
83abffc82aSNicolas Saenz Julienne }
84abffc82aSNicolas Saenz Julienne
85*9419f7f4SNicolas Saenz Julienne fw = devm_rpi_firmware_get(&pdev->dev, np);
86abffc82aSNicolas Saenz Julienne of_node_put(np);
87abffc82aSNicolas Saenz Julienne if (!fw)
88abffc82aSNicolas Saenz Julienne return -EPROBE_DEFER;
89abffc82aSNicolas Saenz Julienne
90abffc82aSNicolas Saenz Julienne priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
91abffc82aSNicolas Saenz Julienne if (!priv)
92abffc82aSNicolas Saenz Julienne return -ENOMEM;
93abffc82aSNicolas Saenz Julienne
94abffc82aSNicolas Saenz Julienne dev_set_drvdata(dev, priv);
95abffc82aSNicolas Saenz Julienne
96abffc82aSNicolas Saenz Julienne priv->fw = fw;
97abffc82aSNicolas Saenz Julienne priv->rcdev.owner = THIS_MODULE;
98abffc82aSNicolas Saenz Julienne priv->rcdev.nr_resets = RASPBERRYPI_FIRMWARE_RESET_NUM_IDS;
99abffc82aSNicolas Saenz Julienne priv->rcdev.ops = &rpi_reset_ops;
100abffc82aSNicolas Saenz Julienne priv->rcdev.of_node = dev->of_node;
101abffc82aSNicolas Saenz Julienne
102abffc82aSNicolas Saenz Julienne return devm_reset_controller_register(dev, &priv->rcdev);
103abffc82aSNicolas Saenz Julienne }
104abffc82aSNicolas Saenz Julienne
105abffc82aSNicolas Saenz Julienne static const struct of_device_id rpi_reset_of_match[] = {
106abffc82aSNicolas Saenz Julienne { .compatible = "raspberrypi,firmware-reset" },
107abffc82aSNicolas Saenz Julienne { /* sentinel */ }
108abffc82aSNicolas Saenz Julienne };
109abffc82aSNicolas Saenz Julienne MODULE_DEVICE_TABLE(of, rpi_reset_of_match);
110abffc82aSNicolas Saenz Julienne
111abffc82aSNicolas Saenz Julienne static struct platform_driver rpi_reset_driver = {
112abffc82aSNicolas Saenz Julienne .probe = rpi_reset_probe,
113abffc82aSNicolas Saenz Julienne .driver = {
114abffc82aSNicolas Saenz Julienne .name = "raspberrypi-reset",
115abffc82aSNicolas Saenz Julienne .of_match_table = rpi_reset_of_match,
116abffc82aSNicolas Saenz Julienne },
117abffc82aSNicolas Saenz Julienne };
118abffc82aSNicolas Saenz Julienne module_platform_driver(rpi_reset_driver);
119abffc82aSNicolas Saenz Julienne
120abffc82aSNicolas Saenz Julienne MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
121abffc82aSNicolas Saenz Julienne MODULE_DESCRIPTION("Raspberry Pi 4 firmware reset driver");
122abffc82aSNicolas Saenz Julienne MODULE_LICENSE("GPL");
123