1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * cs5535-mfd.c - core MFD driver for CS5535/CS5536 southbridges 4 * 5 * The CS5535 and CS5536 has an ISA bridge on the PCI bus that is 6 * used for accessing GPIOs, MFGPTs, ACPI, etc. Each subdevice has 7 * an IO range that's specified in a single BAR. The BAR order is 8 * hardcoded in the CS553x specifications. 9 * 10 * Copyright (c) 2010 Andres Salomon <dilinger@queued.net> 11 */ 12 13 #include <linux/kernel.h> 14 #include <linux/mfd/core.h> 15 #include <linux/module.h> 16 #include <linux/pci.h> 17 #include <asm/olpc.h> 18 19 #define DRV_NAME "cs5535-mfd" 20 21 enum cs5535_mfd_bars { 22 SMB_BAR = 0, 23 GPIO_BAR = 1, 24 MFGPT_BAR = 2, 25 PMS_BAR = 4, 26 ACPI_BAR = 5, 27 NR_BARS, 28 }; 29 30 static struct resource cs5535_mfd_resources[NR_BARS]; 31 32 static struct mfd_cell cs5535_mfd_cells[] = { 33 { 34 .name = "cs5535-smb", 35 .num_resources = 1, 36 .resources = &cs5535_mfd_resources[SMB_BAR], 37 }, 38 { 39 .name = "cs5535-gpio", 40 .num_resources = 1, 41 .resources = &cs5535_mfd_resources[GPIO_BAR], 42 }, 43 { 44 .name = "cs5535-mfgpt", 45 .num_resources = 1, 46 .resources = &cs5535_mfd_resources[MFGPT_BAR], 47 }, 48 { 49 .name = "cs5535-pms", 50 .num_resources = 1, 51 .resources = &cs5535_mfd_resources[PMS_BAR], 52 }, 53 }; 54 55 static struct mfd_cell cs5535_olpc_mfd_cells[] = { 56 { 57 .name = "olpc-xo1-pm-acpi", 58 .num_resources = 1, 59 .resources = &cs5535_mfd_resources[ACPI_BAR], 60 }, 61 { 62 .name = "olpc-xo1-sci-acpi", 63 .num_resources = 1, 64 .resources = &cs5535_mfd_resources[ACPI_BAR], 65 }, 66 }; 67 68 static int cs5535_mfd_probe(struct pci_dev *pdev, 69 const struct pci_device_id *id) 70 { 71 int err, bar; 72 73 err = pci_enable_device(pdev); 74 if (err) 75 return err; 76 77 for (bar = 0; bar < NR_BARS; bar++) { 78 struct resource *r = &cs5535_mfd_resources[bar]; 79 80 r->flags = IORESOURCE_IO; 81 r->start = pci_resource_start(pdev, bar); 82 r->end = pci_resource_end(pdev, bar); 83 } 84 85 err = pci_request_region(pdev, PMS_BAR, DRV_NAME); 86 if (err) { 87 dev_err(&pdev->dev, "Failed to request PMS_BAR's IO region\n"); 88 goto err_disable; 89 } 90 91 err = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, cs5535_mfd_cells, 92 ARRAY_SIZE(cs5535_mfd_cells), NULL, 0, NULL); 93 if (err) { 94 dev_err(&pdev->dev, 95 "Failed to add CS5535 sub-devices: %d\n", err); 96 goto err_release_pms; 97 } 98 99 if (machine_is_olpc()) { 100 err = pci_request_region(pdev, ACPI_BAR, DRV_NAME); 101 if (err) { 102 dev_err(&pdev->dev, 103 "Failed to request ACPI_BAR's IO region\n"); 104 goto err_remove_devices; 105 } 106 107 err = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, 108 cs5535_olpc_mfd_cells, 109 ARRAY_SIZE(cs5535_olpc_mfd_cells), 110 NULL, 0, NULL); 111 if (err) { 112 dev_err(&pdev->dev, 113 "Failed to add CS5535 OLPC sub-devices: %d\n", 114 err); 115 goto err_release_acpi; 116 } 117 } 118 119 dev_info(&pdev->dev, "%zu devices registered.\n", 120 ARRAY_SIZE(cs5535_mfd_cells)); 121 122 return 0; 123 124 err_release_acpi: 125 pci_release_region(pdev, ACPI_BAR); 126 err_remove_devices: 127 mfd_remove_devices(&pdev->dev); 128 err_release_pms: 129 pci_release_region(pdev, PMS_BAR); 130 err_disable: 131 pci_disable_device(pdev); 132 return err; 133 } 134 135 static void cs5535_mfd_remove(struct pci_dev *pdev) 136 { 137 mfd_remove_devices(&pdev->dev); 138 139 if (machine_is_olpc()) 140 pci_release_region(pdev, ACPI_BAR); 141 142 pci_release_region(pdev, PMS_BAR); 143 pci_disable_device(pdev); 144 } 145 146 static const struct pci_device_id cs5535_mfd_pci_tbl[] = { 147 { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, 148 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, 149 { 0, } 150 }; 151 MODULE_DEVICE_TABLE(pci, cs5535_mfd_pci_tbl); 152 153 static struct pci_driver cs5535_mfd_driver = { 154 .name = DRV_NAME, 155 .id_table = cs5535_mfd_pci_tbl, 156 .probe = cs5535_mfd_probe, 157 .remove = cs5535_mfd_remove, 158 }; 159 160 module_pci_driver(cs5535_mfd_driver); 161 162 MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>"); 163 MODULE_DESCRIPTION("MFD driver for CS5535/CS5536 southbridge's ISA PCI device"); 164 MODULE_LICENSE("GPL"); 165