xref: /openbmc/linux/drivers/reset/reset-raspberrypi.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
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