1 /*
2  * PCI Hot Plug Controller Driver for System z
3  *
4  * Copyright 2012 IBM Corp.
5  *
6  * Author(s):
7  *   Jan Glauber <jang@linux.vnet.ibm.com>
8  */
9 
10 #define COMPONENT "zPCI hpc"
11 #define pr_fmt(fmt) COMPONENT ": " fmt
12 
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/slab.h>
16 #include <linux/pci.h>
17 #include <linux/pci_hotplug.h>
18 #include <linux/init.h>
19 #include <asm/sclp.h>
20 
21 #define SLOT_NAME_SIZE	10
22 static LIST_HEAD(s390_hotplug_slot_list);
23 
24 MODULE_AUTHOR("Jan Glauber <jang@linux.vnet.ibm.com");
25 MODULE_DESCRIPTION("Hot Plug PCI Controller for System z");
26 MODULE_LICENSE("GPL");
27 
28 static int zpci_fn_configured(enum zpci_state state)
29 {
30 	return state == ZPCI_FN_STATE_CONFIGURED ||
31 	       state == ZPCI_FN_STATE_ONLINE;
32 }
33 
34 /*
35  * struct slot - slot information for each *physical* slot
36  */
37 struct slot {
38 	struct list_head slot_list;
39 	struct hotplug_slot *hotplug_slot;
40 	struct zpci_dev *zdev;
41 };
42 
43 static int enable_slot(struct hotplug_slot *hotplug_slot)
44 {
45 	struct slot *slot = hotplug_slot->private;
46 	int rc;
47 
48 	if (slot->zdev->state != ZPCI_FN_STATE_STANDBY)
49 		return -EIO;
50 
51 	rc = sclp_pci_configure(slot->zdev->fid);
52 	if (!rc) {
53 		slot->zdev->state = ZPCI_FN_STATE_CONFIGURED;
54 		/* automatically scan the device after is was configured */
55 		zpci_enable_device(slot->zdev);
56 		zpci_scan_device(slot->zdev);
57 	}
58 	return rc;
59 }
60 
61 static int disable_slot(struct hotplug_slot *hotplug_slot)
62 {
63 	struct slot *slot = hotplug_slot->private;
64 	int rc;
65 
66 	if (!zpci_fn_configured(slot->zdev->state))
67 		return -EIO;
68 
69 	/* TODO: we rely on the user to unbind/remove the device, is that plausible
70 	 *	 or do we need to trigger that here?
71 	 */
72 	rc = sclp_pci_deconfigure(slot->zdev->fid);
73 	if (!rc) {
74 		/* Fixme: better call List-PCI to find the disabled FH
75 		   for the FID since the FH should be opaque... */
76 		slot->zdev->fh &= 0x7fffffff;
77 		slot->zdev->state = ZPCI_FN_STATE_STANDBY;
78 	}
79 	return rc;
80 }
81 
82 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
83 {
84 	struct slot *slot = hotplug_slot->private;
85 
86 	switch (slot->zdev->state) {
87 	case ZPCI_FN_STATE_STANDBY:
88 		*value = 0;
89 		break;
90 	default:
91 		*value = 1;
92 		break;
93 	}
94 	return 0;
95 }
96 
97 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
98 {
99 	/* if the slot exits it always contains a function */
100 	*value = 1;
101 	return 0;
102 }
103 
104 static void release_slot(struct hotplug_slot *hotplug_slot)
105 {
106 	struct slot *slot = hotplug_slot->private;
107 
108 	pr_debug("%s - physical_slot = %s\n", __func__, hotplug_slot_name(hotplug_slot));
109 	kfree(slot->hotplug_slot->info);
110 	kfree(slot->hotplug_slot);
111 	kfree(slot);
112 }
113 
114 static struct hotplug_slot_ops s390_hotplug_slot_ops = {
115 	.enable_slot =		enable_slot,
116 	.disable_slot =		disable_slot,
117 	.get_power_status =	get_power_status,
118 	.get_adapter_status =	get_adapter_status,
119 };
120 
121 static int init_pci_slot(struct zpci_dev *zdev)
122 {
123 	struct hotplug_slot *hotplug_slot;
124 	struct hotplug_slot_info *info;
125 	char name[SLOT_NAME_SIZE];
126 	struct slot *slot;
127 	int rc;
128 
129 	if (!zdev)
130 		return 0;
131 
132 	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
133 	if (!slot)
134 		goto error;
135 
136 	hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
137 	if (!hotplug_slot)
138 		goto error_hp;
139 	hotplug_slot->private = slot;
140 
141 	slot->hotplug_slot = hotplug_slot;
142 	slot->zdev = zdev;
143 
144 	info = kzalloc(sizeof(*info), GFP_KERNEL);
145 	if (!info)
146 		goto error_info;
147 	hotplug_slot->info = info;
148 
149 	hotplug_slot->ops = &s390_hotplug_slot_ops;
150 	hotplug_slot->release = &release_slot;
151 
152 	get_power_status(hotplug_slot, &info->power_status);
153 	get_adapter_status(hotplug_slot, &info->adapter_status);
154 
155 	snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid);
156 	rc = pci_hp_register(slot->hotplug_slot, zdev->bus,
157 			     ZPCI_DEVFN, name);
158 	if (rc) {
159 		pr_err("pci_hp_register failed with error %d\n", rc);
160 		goto error_reg;
161 	}
162 	list_add(&slot->slot_list, &s390_hotplug_slot_list);
163 	return 0;
164 
165 error_reg:
166 	kfree(info);
167 error_info:
168 	kfree(hotplug_slot);
169 error_hp:
170 	kfree(slot);
171 error:
172 	return -ENOMEM;
173 }
174 
175 static int __init init_pci_slots(void)
176 {
177 	struct zpci_dev *zdev;
178 	int device = 0;
179 
180 	/*
181 	 * Create a structure for each slot, and register that slot
182 	 * with the pci_hotplug subsystem.
183 	 */
184 	mutex_lock(&zpci_list_lock);
185 	list_for_each_entry(zdev, &zpci_list, entry) {
186 		init_pci_slot(zdev);
187 		device++;
188 	}
189 
190 	mutex_unlock(&zpci_list_lock);
191 	return (device) ? 0 : -ENODEV;
192 }
193 
194 static void exit_pci_slot(struct zpci_dev *zdev)
195 {
196 	struct list_head *tmp, *n;
197 	struct slot *slot;
198 
199 	list_for_each_safe(tmp, n, &s390_hotplug_slot_list) {
200 		slot = list_entry(tmp, struct slot, slot_list);
201 		if (slot->zdev != zdev)
202 			continue;
203 		list_del(&slot->slot_list);
204 		pci_hp_deregister(slot->hotplug_slot);
205 	}
206 }
207 
208 static void __exit exit_pci_slots(void)
209 {
210 	struct list_head *tmp, *n;
211 	struct slot *slot;
212 
213 	/*
214 	 * Unregister all of our slots with the pci_hotplug subsystem.
215 	 * Memory will be freed in release_slot() callback after slot's
216 	 * lifespan is finished.
217 	 */
218 	list_for_each_safe(tmp, n, &s390_hotplug_slot_list) {
219 		slot = list_entry(tmp, struct slot, slot_list);
220 		list_del(&slot->slot_list);
221 		pci_hp_deregister(slot->hotplug_slot);
222 	}
223 }
224 
225 static int __init pci_hotplug_s390_init(void)
226 {
227 	/*
228 	 * Do specific initialization stuff for your driver here
229 	 * like initializing your controller hardware (if any) and
230 	 * determining the number of slots you have in the system
231 	 * right now.
232 	 */
233 
234 	if (!pci_probe)
235 		return -EOPNOTSUPP;
236 
237 	/* register callbacks for slot handling from arch code */
238 	mutex_lock(&zpci_list_lock);
239 	hotplug_ops.create_slot = init_pci_slot;
240 	hotplug_ops.remove_slot = exit_pci_slot;
241 	mutex_unlock(&zpci_list_lock);
242 	pr_info("registered hotplug slot callbacks\n");
243 	return init_pci_slots();
244 }
245 
246 static void __exit pci_hotplug_s390_exit(void)
247 {
248 	exit_pci_slots();
249 }
250 
251 module_init(pci_hotplug_s390_init);
252 module_exit(pci_hotplug_s390_exit);
253