xref: /openbmc/linux/drivers/char/ipmi/ipmi_si_pci.c (revision 104fb25f)
1243ac210SCorey Minyard // SPDX-License-Identifier: GPL-2.0+
213d0b35cSCorey Minyard /*
313d0b35cSCorey Minyard  * ipmi_si_pci.c
413d0b35cSCorey Minyard  *
513d0b35cSCorey Minyard  * Handling for IPMI devices on the PCI bus.
613d0b35cSCorey Minyard  */
725880f7dSJoe Perches 
825880f7dSJoe Perches #define pr_fmt(fmt) "ipmi_pci: " fmt
925880f7dSJoe Perches 
1013d0b35cSCorey Minyard #include <linux/module.h>
1113d0b35cSCorey Minyard #include <linux/pci.h>
1213d0b35cSCorey Minyard #include "ipmi_si.h"
1313d0b35cSCorey Minyard 
1413d0b35cSCorey Minyard static bool pci_registered;
1513d0b35cSCorey Minyard 
1613d0b35cSCorey Minyard static bool si_trypci = true;
1713d0b35cSCorey Minyard 
1813d0b35cSCorey Minyard module_param_named(trypci, si_trypci, bool, 0);
1913d0b35cSCorey Minyard MODULE_PARM_DESC(trypci, "Setting this to zero will disable the"
2013d0b35cSCorey Minyard 		 " default scan of the interfaces identified via pci");
2113d0b35cSCorey Minyard 
22ad2575f8SCorey Minyard #define PCI_DEVICE_ID_HP_MMC 0x121A
2313d0b35cSCorey Minyard 
2413d0b35cSCorey Minyard static void ipmi_pci_cleanup(struct si_sm_io *io)
2513d0b35cSCorey Minyard {
2613d0b35cSCorey Minyard 	struct pci_dev *pdev = io->addr_source_data;
2713d0b35cSCorey Minyard 
2813d0b35cSCorey Minyard 	pci_disable_device(pdev);
2913d0b35cSCorey Minyard }
3013d0b35cSCorey Minyard 
3113d0b35cSCorey Minyard static int ipmi_pci_probe_regspacing(struct si_sm_io *io)
3213d0b35cSCorey Minyard {
3313d0b35cSCorey Minyard 	if (io->si_type == SI_KCS) {
3413d0b35cSCorey Minyard 		unsigned char	status;
3513d0b35cSCorey Minyard 		int		regspacing;
3613d0b35cSCorey Minyard 
3713d0b35cSCorey Minyard 		io->regsize = DEFAULT_REGSIZE;
3813d0b35cSCorey Minyard 		io->regshift = 0;
3913d0b35cSCorey Minyard 
4013d0b35cSCorey Minyard 		/* detect 1, 4, 16byte spacing */
4113d0b35cSCorey Minyard 		for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) {
4213d0b35cSCorey Minyard 			io->regspacing = regspacing;
4313d0b35cSCorey Minyard 			if (io->io_setup(io)) {
4425880f7dSJoe Perches 				dev_err(io->dev, "Could not setup I/O space\n");
4513d0b35cSCorey Minyard 				return DEFAULT_REGSPACING;
4613d0b35cSCorey Minyard 			}
4713d0b35cSCorey Minyard 			/* write invalid cmd */
4813d0b35cSCorey Minyard 			io->outputb(io, 1, 0x10);
4913d0b35cSCorey Minyard 			/* read status back */
5013d0b35cSCorey Minyard 			status = io->inputb(io, 1);
5113d0b35cSCorey Minyard 			io->io_cleanup(io);
5213d0b35cSCorey Minyard 			if (status)
5313d0b35cSCorey Minyard 				return regspacing;
5413d0b35cSCorey Minyard 			regspacing *= 4;
5513d0b35cSCorey Minyard 		}
5613d0b35cSCorey Minyard 	}
5713d0b35cSCorey Minyard 	return DEFAULT_REGSPACING;
5813d0b35cSCorey Minyard }
5913d0b35cSCorey Minyard 
60bc48fa1bSCorey Minyard static struct pci_device_id ipmi_pci_blacklist[] = {
61bc48fa1bSCorey Minyard 	/*
62bc48fa1bSCorey Minyard 	 * This is a "Virtual IPMI device", whatever that is.  It appears
63bc48fa1bSCorey Minyard 	 * as a KCS device by the class, but it is not one.
64bc48fa1bSCorey Minyard 	 */
65bc48fa1bSCorey Minyard 	{ PCI_VDEVICE(REALTEK, 0x816c) },
66bc48fa1bSCorey Minyard 	{ 0, }
67bc48fa1bSCorey Minyard };
68bc48fa1bSCorey Minyard 
6913d0b35cSCorey Minyard static int ipmi_pci_probe(struct pci_dev *pdev,
7013d0b35cSCorey Minyard 				    const struct pci_device_id *ent)
7113d0b35cSCorey Minyard {
7213d0b35cSCorey Minyard 	int rv;
7313d0b35cSCorey Minyard 	struct si_sm_io io;
7413d0b35cSCorey Minyard 
75bc48fa1bSCorey Minyard 	if (pci_match_id(ipmi_pci_blacklist, pdev))
76bc48fa1bSCorey Minyard 		return -ENODEV;
77bc48fa1bSCorey Minyard 
7813d0b35cSCorey Minyard 	memset(&io, 0, sizeof(io));
7913d0b35cSCorey Minyard 	io.addr_source = SI_PCI;
8013d0b35cSCorey Minyard 	dev_info(&pdev->dev, "probing via PCI");
8113d0b35cSCorey Minyard 
82ad2575f8SCorey Minyard 	switch (pdev->class) {
83ad2575f8SCorey Minyard 	case PCI_CLASS_SERIAL_IPMI_SMIC:
8413d0b35cSCorey Minyard 		io.si_type = SI_SMIC;
8513d0b35cSCorey Minyard 		break;
8613d0b35cSCorey Minyard 
87ad2575f8SCorey Minyard 	case PCI_CLASS_SERIAL_IPMI_KCS:
8813d0b35cSCorey Minyard 		io.si_type = SI_KCS;
8913d0b35cSCorey Minyard 		break;
9013d0b35cSCorey Minyard 
91ad2575f8SCorey Minyard 	case PCI_CLASS_SERIAL_IPMI_BT:
9213d0b35cSCorey Minyard 		io.si_type = SI_BT;
9313d0b35cSCorey Minyard 		break;
9413d0b35cSCorey Minyard 
9513d0b35cSCorey Minyard 	default:
96ad2575f8SCorey Minyard 		dev_info(&pdev->dev, "Unknown IPMI class: %x\n", pdev->class);
9713d0b35cSCorey Minyard 		return -ENOMEM;
9813d0b35cSCorey Minyard 	}
9913d0b35cSCorey Minyard 
10013d0b35cSCorey Minyard 	rv = pci_enable_device(pdev);
10113d0b35cSCorey Minyard 	if (rv) {
10213d0b35cSCorey Minyard 		dev_err(&pdev->dev, "couldn't enable PCI device\n");
10313d0b35cSCorey Minyard 		return rv;
10413d0b35cSCorey Minyard 	}
10513d0b35cSCorey Minyard 
10613d0b35cSCorey Minyard 	io.addr_source_cleanup = ipmi_pci_cleanup;
10713d0b35cSCorey Minyard 	io.addr_source_data = pdev;
10813d0b35cSCorey Minyard 
1091ac8aa8dSCorey Minyard 	if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
110f6296bdcSCorey Minyard 		io.addr_space = IPMI_IO_ADDR_SPACE;
1111ac8aa8dSCorey Minyard 		io.io_setup = ipmi_si_port_setup;
1121ac8aa8dSCorey Minyard 	} else {
113f6296bdcSCorey Minyard 		io.addr_space = IPMI_MEM_ADDR_SPACE;
1141ac8aa8dSCorey Minyard 		io.io_setup = ipmi_si_mem_setup;
1151ac8aa8dSCorey Minyard 	}
11613d0b35cSCorey Minyard 	io.addr_data = pci_resource_start(pdev, 0);
11713d0b35cSCorey Minyard 
11801508d9eSMeelis Roos 	io.dev = &pdev->dev;
11901508d9eSMeelis Roos 
12013d0b35cSCorey Minyard 	io.regspacing = ipmi_pci_probe_regspacing(&io);
12113d0b35cSCorey Minyard 	io.regsize = DEFAULT_REGSIZE;
12213d0b35cSCorey Minyard 	io.regshift = 0;
12313d0b35cSCorey Minyard 
12413d0b35cSCorey Minyard 	io.irq = pdev->irq;
12513d0b35cSCorey Minyard 	if (io.irq)
12613d0b35cSCorey Minyard 		io.irq_setup = ipmi_std_irq_setup;
12713d0b35cSCorey Minyard 
12813d0b35cSCorey Minyard 	dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n",
12913d0b35cSCorey Minyard 		 &pdev->resource[0], io.regsize, io.regspacing, io.irq);
13013d0b35cSCorey Minyard 
13113d0b35cSCorey Minyard 	rv = ipmi_si_add_smi(&io);
13213d0b35cSCorey Minyard 	if (rv)
13313d0b35cSCorey Minyard 		pci_disable_device(pdev);
13413d0b35cSCorey Minyard 
13513d0b35cSCorey Minyard 	return rv;
13613d0b35cSCorey Minyard }
13713d0b35cSCorey Minyard 
13813d0b35cSCorey Minyard static void ipmi_pci_remove(struct pci_dev *pdev)
13913d0b35cSCorey Minyard {
14013d0b35cSCorey Minyard 	ipmi_si_remove_by_dev(&pdev->dev);
14113d0b35cSCorey Minyard }
14213d0b35cSCorey Minyard 
14313d0b35cSCorey Minyard static const struct pci_device_id ipmi_pci_devices[] = {
144ad2575f8SCorey Minyard 	{ PCI_VDEVICE(HP, PCI_DEVICE_ID_HP_MMC) },
145ad2575f8SCorey Minyard 	{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_SMIC, ~0) },
146ad2575f8SCorey Minyard 	{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_KCS, ~0) },
147ad2575f8SCorey Minyard 	{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_BT, ~0) },
14813d0b35cSCorey Minyard 	{ 0, }
14913d0b35cSCorey Minyard };
15013d0b35cSCorey Minyard MODULE_DEVICE_TABLE(pci, ipmi_pci_devices);
15113d0b35cSCorey Minyard 
15213d0b35cSCorey Minyard static struct pci_driver ipmi_pci_driver = {
153104fb25fSCorey Minyard 	.name =         SI_DEVICE_NAME,
15413d0b35cSCorey Minyard 	.id_table =     ipmi_pci_devices,
15513d0b35cSCorey Minyard 	.probe =        ipmi_pci_probe,
15613d0b35cSCorey Minyard 	.remove =       ipmi_pci_remove,
15713d0b35cSCorey Minyard };
15813d0b35cSCorey Minyard 
15913d0b35cSCorey Minyard void ipmi_si_pci_init(void)
16013d0b35cSCorey Minyard {
16113d0b35cSCorey Minyard 	if (si_trypci) {
16213d0b35cSCorey Minyard 		int rv = pci_register_driver(&ipmi_pci_driver);
16313d0b35cSCorey Minyard 		if (rv)
16425880f7dSJoe Perches 			pr_err("Unable to register PCI driver: %d\n", rv);
16513d0b35cSCorey Minyard 		else
16613d0b35cSCorey Minyard 			pci_registered = true;
16713d0b35cSCorey Minyard 	}
16813d0b35cSCorey Minyard }
16913d0b35cSCorey Minyard 
17013d0b35cSCorey Minyard void ipmi_si_pci_shutdown(void)
17113d0b35cSCorey Minyard {
17213d0b35cSCorey Minyard 	if (pci_registered)
17313d0b35cSCorey Minyard 		pci_unregister_driver(&ipmi_pci_driver);
17413d0b35cSCorey Minyard }
175