Commit | Line | Data |
---|---|---|
7441b062 JG |
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 | ||
896cb7e6 GS |
10 | #define KMSG_COMPONENT "zpci" |
11 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | |
7441b062 JG |
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> | |
a2ab8333 | 18 | #include <asm/pci_debug.h> |
7441b062 JG |
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 | ||
4bee2a5d SO |
43 | static inline int slot_configure(struct slot *slot) |
44 | { | |
45 | int ret = sclp_pci_configure(slot->zdev->fid); | |
46 | ||
47 | zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, ret); | |
48 | if (!ret) | |
49 | slot->zdev->state = ZPCI_FN_STATE_CONFIGURED; | |
50 | ||
51 | return ret; | |
52 | } | |
53 | ||
54 | static inline int slot_deconfigure(struct slot *slot) | |
55 | { | |
56 | int ret = sclp_pci_deconfigure(slot->zdev->fid); | |
57 | ||
58 | zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, ret); | |
59 | if (!ret) | |
60 | slot->zdev->state = ZPCI_FN_STATE_STANDBY; | |
61 | ||
62 | return ret; | |
63 | } | |
64 | ||
7441b062 JG |
65 | static int enable_slot(struct hotplug_slot *hotplug_slot) |
66 | { | |
67 | struct slot *slot = hotplug_slot->private; | |
68 | int rc; | |
69 | ||
70 | if (slot->zdev->state != ZPCI_FN_STATE_STANDBY) | |
71 | return -EIO; | |
72 | ||
4bee2a5d SO |
73 | rc = slot_configure(slot); |
74 | if (rc) | |
75 | return rc; | |
76 | ||
77 | rc = zpci_enable_device(slot->zdev); | |
78 | if (rc) | |
79 | goto out_deconfigure; | |
80 | ||
4bee2a5d | 81 | pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN); |
c4ec84c7 | 82 | pci_lock_rescan_remove(); |
4bee2a5d | 83 | pci_bus_add_devices(slot->zdev->bus); |
c4ec84c7 | 84 | pci_unlock_rescan_remove(); |
4bee2a5d SO |
85 | |
86 | return rc; | |
87 | ||
88 | out_deconfigure: | |
89 | slot_deconfigure(slot); | |
7441b062 JG |
90 | return rc; |
91 | } | |
92 | ||
93 | static int disable_slot(struct hotplug_slot *hotplug_slot) | |
94 | { | |
95 | struct slot *slot = hotplug_slot->private; | |
9a99649f | 96 | struct pci_dev *pdev; |
7441b062 JG |
97 | int rc; |
98 | ||
99 | if (!zpci_fn_configured(slot->zdev->state)) | |
100 | return -EIO; | |
101 | ||
9a99649f SO |
102 | pdev = pci_get_slot(slot->zdev->bus, ZPCI_DEVFN); |
103 | if (pdev) { | |
104 | pci_stop_and_remove_bus_device_locked(pdev); | |
105 | pci_dev_put(pdev); | |
106 | } | |
8b2a7e60 | 107 | |
cb65a669 SO |
108 | rc = zpci_disable_device(slot->zdev); |
109 | if (rc) | |
110 | return rc; | |
8b2a7e60 | 111 | |
4bee2a5d | 112 | return slot_deconfigure(slot); |
7441b062 JG |
113 | } |
114 | ||
115 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | |
116 | { | |
117 | struct slot *slot = hotplug_slot->private; | |
118 | ||
119 | switch (slot->zdev->state) { | |
120 | case ZPCI_FN_STATE_STANDBY: | |
121 | *value = 0; | |
122 | break; | |
123 | default: | |
124 | *value = 1; | |
125 | break; | |
126 | } | |
127 | return 0; | |
128 | } | |
129 | ||
130 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | |
131 | { | |
132 | /* if the slot exits it always contains a function */ | |
133 | *value = 1; | |
134 | return 0; | |
135 | } | |
136 | ||
137 | static void release_slot(struct hotplug_slot *hotplug_slot) | |
138 | { | |
139 | struct slot *slot = hotplug_slot->private; | |
140 | ||
7441b062 JG |
141 | kfree(slot->hotplug_slot->info); |
142 | kfree(slot->hotplug_slot); | |
143 | kfree(slot); | |
144 | } | |
145 | ||
146 | static struct hotplug_slot_ops s390_hotplug_slot_ops = { | |
147 | .enable_slot = enable_slot, | |
148 | .disable_slot = disable_slot, | |
149 | .get_power_status = get_power_status, | |
150 | .get_adapter_status = get_adapter_status, | |
151 | }; | |
152 | ||
67f43f38 | 153 | int zpci_init_slot(struct zpci_dev *zdev) |
7441b062 JG |
154 | { |
155 | struct hotplug_slot *hotplug_slot; | |
156 | struct hotplug_slot_info *info; | |
157 | char name[SLOT_NAME_SIZE]; | |
158 | struct slot *slot; | |
159 | int rc; | |
160 | ||
161 | if (!zdev) | |
162 | return 0; | |
163 | ||
164 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); | |
165 | if (!slot) | |
166 | goto error; | |
167 | ||
168 | hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); | |
169 | if (!hotplug_slot) | |
170 | goto error_hp; | |
171 | hotplug_slot->private = slot; | |
172 | ||
173 | slot->hotplug_slot = hotplug_slot; | |
174 | slot->zdev = zdev; | |
175 | ||
176 | info = kzalloc(sizeof(*info), GFP_KERNEL); | |
177 | if (!info) | |
178 | goto error_info; | |
179 | hotplug_slot->info = info; | |
180 | ||
181 | hotplug_slot->ops = &s390_hotplug_slot_ops; | |
182 | hotplug_slot->release = &release_slot; | |
183 | ||
184 | get_power_status(hotplug_slot, &info->power_status); | |
185 | get_adapter_status(hotplug_slot, &info->adapter_status); | |
186 | ||
187 | snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid); | |
188 | rc = pci_hp_register(slot->hotplug_slot, zdev->bus, | |
189 | ZPCI_DEVFN, name); | |
1f1dcbd4 | 190 | if (rc) |
7441b062 | 191 | goto error_reg; |
1f1dcbd4 | 192 | |
7441b062 JG |
193 | list_add(&slot->slot_list, &s390_hotplug_slot_list); |
194 | return 0; | |
195 | ||
196 | error_reg: | |
197 | kfree(info); | |
198 | error_info: | |
199 | kfree(hotplug_slot); | |
200 | error_hp: | |
201 | kfree(slot); | |
202 | error: | |
203 | return -ENOMEM; | |
204 | } | |
205 | ||
67f43f38 | 206 | void zpci_exit_slot(struct zpci_dev *zdev) |
7441b062 | 207 | { |
2ac83ccc | 208 | struct slot *slot, *next; |
7441b062 | 209 | |
2ac83ccc GT |
210 | list_for_each_entry_safe(slot, next, &s390_hotplug_slot_list, |
211 | slot_list) { | |
7441b062 JG |
212 | if (slot->zdev != zdev) |
213 | continue; | |
214 | list_del(&slot->slot_list); | |
215 | pci_hp_deregister(slot->hotplug_slot); | |
216 | } | |
217 | } |