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; | |
96 | int rc; | |
97 | ||
98 | if (!zpci_fn_configured(slot->zdev->state)) | |
99 | return -EIO; | |
100 | ||
8b2a7e60 | 101 | if (slot->zdev->pdev) |
c4ec84c7 | 102 | pci_stop_and_remove_bus_device_locked(slot->zdev->pdev); |
8b2a7e60 | 103 | |
cb65a669 SO |
104 | rc = zpci_disable_device(slot->zdev); |
105 | if (rc) | |
106 | return rc; | |
8b2a7e60 | 107 | |
4bee2a5d | 108 | return slot_deconfigure(slot); |
7441b062 JG |
109 | } |
110 | ||
111 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | |
112 | { | |
113 | struct slot *slot = hotplug_slot->private; | |
114 | ||
115 | switch (slot->zdev->state) { | |
116 | case ZPCI_FN_STATE_STANDBY: | |
117 | *value = 0; | |
118 | break; | |
119 | default: | |
120 | *value = 1; | |
121 | break; | |
122 | } | |
123 | return 0; | |
124 | } | |
125 | ||
126 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | |
127 | { | |
128 | /* if the slot exits it always contains a function */ | |
129 | *value = 1; | |
130 | return 0; | |
131 | } | |
132 | ||
133 | static void release_slot(struct hotplug_slot *hotplug_slot) | |
134 | { | |
135 | struct slot *slot = hotplug_slot->private; | |
136 | ||
7441b062 JG |
137 | kfree(slot->hotplug_slot->info); |
138 | kfree(slot->hotplug_slot); | |
139 | kfree(slot); | |
140 | } | |
141 | ||
142 | static struct hotplug_slot_ops s390_hotplug_slot_ops = { | |
143 | .enable_slot = enable_slot, | |
144 | .disable_slot = disable_slot, | |
145 | .get_power_status = get_power_status, | |
146 | .get_adapter_status = get_adapter_status, | |
147 | }; | |
148 | ||
67f43f38 | 149 | int zpci_init_slot(struct zpci_dev *zdev) |
7441b062 JG |
150 | { |
151 | struct hotplug_slot *hotplug_slot; | |
152 | struct hotplug_slot_info *info; | |
153 | char name[SLOT_NAME_SIZE]; | |
154 | struct slot *slot; | |
155 | int rc; | |
156 | ||
157 | if (!zdev) | |
158 | return 0; | |
159 | ||
160 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); | |
161 | if (!slot) | |
162 | goto error; | |
163 | ||
164 | hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); | |
165 | if (!hotplug_slot) | |
166 | goto error_hp; | |
167 | hotplug_slot->private = slot; | |
168 | ||
169 | slot->hotplug_slot = hotplug_slot; | |
170 | slot->zdev = zdev; | |
171 | ||
172 | info = kzalloc(sizeof(*info), GFP_KERNEL); | |
173 | if (!info) | |
174 | goto error_info; | |
175 | hotplug_slot->info = info; | |
176 | ||
177 | hotplug_slot->ops = &s390_hotplug_slot_ops; | |
178 | hotplug_slot->release = &release_slot; | |
179 | ||
180 | get_power_status(hotplug_slot, &info->power_status); | |
181 | get_adapter_status(hotplug_slot, &info->adapter_status); | |
182 | ||
183 | snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid); | |
184 | rc = pci_hp_register(slot->hotplug_slot, zdev->bus, | |
185 | ZPCI_DEVFN, name); | |
1f1dcbd4 | 186 | if (rc) |
7441b062 | 187 | goto error_reg; |
1f1dcbd4 | 188 | |
7441b062 JG |
189 | list_add(&slot->slot_list, &s390_hotplug_slot_list); |
190 | return 0; | |
191 | ||
192 | error_reg: | |
193 | kfree(info); | |
194 | error_info: | |
195 | kfree(hotplug_slot); | |
196 | error_hp: | |
197 | kfree(slot); | |
198 | error: | |
199 | return -ENOMEM; | |
200 | } | |
201 | ||
67f43f38 | 202 | void zpci_exit_slot(struct zpci_dev *zdev) |
7441b062 JG |
203 | { |
204 | struct list_head *tmp, *n; | |
205 | struct slot *slot; | |
206 | ||
207 | list_for_each_safe(tmp, n, &s390_hotplug_slot_list) { | |
208 | slot = list_entry(tmp, struct slot, slot_list); | |
209 | if (slot->zdev != zdev) | |
210 | continue; | |
211 | list_del(&slot->slot_list); | |
212 | pci_hp_deregister(slot->hotplug_slot); | |
213 | } | |
214 | } |