• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
cs5535_mfd_probe(struct pci_dev * pdev,const struct pci_device_id * id)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 
cs5535_mfd_remove(struct pci_dev * pdev)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