162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ipmi_si_pci.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Handling for IPMI devices on the PCI bus. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "ipmi_pci: " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include "ipmi_si.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic bool pci_registered; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic bool si_trypci = true; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cimodule_param_named(trypci, si_trypci, bool, 0); 1962306a36Sopenharmony_ciMODULE_PARM_DESC(trypci, 2062306a36Sopenharmony_ci "Setting this to zero will disable the default scan of the interfaces identified via pci"); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define PCI_DEVICE_ID_HP_MMC 0x121A 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int ipmi_pci_probe_regspacing(struct si_sm_io *io) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci if (io->si_type == SI_KCS) { 2762306a36Sopenharmony_ci unsigned char status; 2862306a36Sopenharmony_ci int regspacing; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci io->regsize = DEFAULT_REGSIZE; 3162306a36Sopenharmony_ci io->regshift = 0; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci /* detect 1, 4, 16byte spacing */ 3462306a36Sopenharmony_ci for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) { 3562306a36Sopenharmony_ci io->regspacing = regspacing; 3662306a36Sopenharmony_ci if (io->io_setup(io)) { 3762306a36Sopenharmony_ci dev_err(io->dev, "Could not setup I/O space\n"); 3862306a36Sopenharmony_ci return DEFAULT_REGSPACING; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci /* write invalid cmd */ 4162306a36Sopenharmony_ci io->outputb(io, 1, 0x10); 4262306a36Sopenharmony_ci /* read status back */ 4362306a36Sopenharmony_ci status = io->inputb(io, 1); 4462306a36Sopenharmony_ci io->io_cleanup(io); 4562306a36Sopenharmony_ci if (status) 4662306a36Sopenharmony_ci return regspacing; 4762306a36Sopenharmony_ci regspacing *= 4; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci return DEFAULT_REGSPACING; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic struct pci_device_id ipmi_pci_blacklist[] = { 5462306a36Sopenharmony_ci /* 5562306a36Sopenharmony_ci * This is a "Virtual IPMI device", whatever that is. It appears 5662306a36Sopenharmony_ci * as a KCS device by the class, but it is not one. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci { PCI_VDEVICE(REALTEK, 0x816c) }, 5962306a36Sopenharmony_ci { 0, } 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int ipmi_pci_probe(struct pci_dev *pdev, 6362306a36Sopenharmony_ci const struct pci_device_id *ent) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int rv; 6662306a36Sopenharmony_ci struct si_sm_io io; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (pci_match_id(ipmi_pci_blacklist, pdev)) 6962306a36Sopenharmony_ci return -ENODEV; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci memset(&io, 0, sizeof(io)); 7262306a36Sopenharmony_ci io.addr_source = SI_PCI; 7362306a36Sopenharmony_ci dev_info(&pdev->dev, "probing via PCI"); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci switch (pdev->class) { 7662306a36Sopenharmony_ci case PCI_CLASS_SERIAL_IPMI_SMIC: 7762306a36Sopenharmony_ci io.si_type = SI_SMIC; 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci case PCI_CLASS_SERIAL_IPMI_KCS: 8162306a36Sopenharmony_ci io.si_type = SI_KCS; 8262306a36Sopenharmony_ci break; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci case PCI_CLASS_SERIAL_IPMI_BT: 8562306a36Sopenharmony_ci io.si_type = SI_BT; 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci default: 8962306a36Sopenharmony_ci dev_info(&pdev->dev, "Unknown IPMI class: %x\n", pdev->class); 9062306a36Sopenharmony_ci return -ENOMEM; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci rv = pcim_enable_device(pdev); 9462306a36Sopenharmony_ci if (rv) { 9562306a36Sopenharmony_ci dev_err(&pdev->dev, "couldn't enable PCI device\n"); 9662306a36Sopenharmony_ci return rv; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) { 10062306a36Sopenharmony_ci io.addr_space = IPMI_IO_ADDR_SPACE; 10162306a36Sopenharmony_ci io.io_setup = ipmi_si_port_setup; 10262306a36Sopenharmony_ci } else { 10362306a36Sopenharmony_ci io.addr_space = IPMI_MEM_ADDR_SPACE; 10462306a36Sopenharmony_ci io.io_setup = ipmi_si_mem_setup; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci io.addr_data = pci_resource_start(pdev, 0); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci io.dev = &pdev->dev; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci io.regspacing = ipmi_pci_probe_regspacing(&io); 11162306a36Sopenharmony_ci io.regsize = DEFAULT_REGSIZE; 11262306a36Sopenharmony_ci io.regshift = 0; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci io.irq = pdev->irq; 11562306a36Sopenharmony_ci if (io.irq) 11662306a36Sopenharmony_ci io.irq_setup = ipmi_std_irq_setup; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n", 11962306a36Sopenharmony_ci &pdev->resource[0], io.regsize, io.regspacing, io.irq); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return ipmi_si_add_smi(&io); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void ipmi_pci_remove(struct pci_dev *pdev) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci ipmi_si_remove_by_dev(&pdev->dev); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic const struct pci_device_id ipmi_pci_devices[] = { 13062306a36Sopenharmony_ci { PCI_VDEVICE(HP, PCI_DEVICE_ID_HP_MMC) }, 13162306a36Sopenharmony_ci { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_SMIC, ~0) }, 13262306a36Sopenharmony_ci { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_KCS, ~0) }, 13362306a36Sopenharmony_ci { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_BT, ~0) }, 13462306a36Sopenharmony_ci { 0, } 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ipmi_pci_devices); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic struct pci_driver ipmi_pci_driver = { 13962306a36Sopenharmony_ci .name = SI_DEVICE_NAME, 14062306a36Sopenharmony_ci .id_table = ipmi_pci_devices, 14162306a36Sopenharmony_ci .probe = ipmi_pci_probe, 14262306a36Sopenharmony_ci .remove = ipmi_pci_remove, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_civoid ipmi_si_pci_init(void) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci if (si_trypci) { 14862306a36Sopenharmony_ci int rv = pci_register_driver(&ipmi_pci_driver); 14962306a36Sopenharmony_ci if (rv) 15062306a36Sopenharmony_ci pr_err("Unable to register PCI driver: %d\n", rv); 15162306a36Sopenharmony_ci else 15262306a36Sopenharmony_ci pci_registered = true; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_civoid ipmi_si_pci_shutdown(void) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci if (pci_registered) 15962306a36Sopenharmony_ci pci_unregister_driver(&ipmi_pci_driver); 16062306a36Sopenharmony_ci} 161