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