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 | ||
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> | |
a2ab8333 | 19 | #include <asm/pci_debug.h> |
7441b062 JG |
20 | #include <asm/sclp.h> |
21 | ||
22 | #define SLOT_NAME_SIZE 10 | |
23 | static LIST_HEAD(s390_hotplug_slot_list); | |
24 | ||
25 | MODULE_AUTHOR("Jan Glauber <jang@linux.vnet.ibm.com"); | |
26 | MODULE_DESCRIPTION("Hot Plug PCI Controller for System z"); | |
27 | MODULE_LICENSE("GPL"); | |
28 | ||
29 | static int zpci_fn_configured(enum zpci_state state) | |
30 | { | |
31 | return state == ZPCI_FN_STATE_CONFIGURED || | |
32 | state == ZPCI_FN_STATE_ONLINE; | |
33 | } | |
34 | ||
35 | /* | |
36 | * struct slot - slot information for each *physical* slot | |
37 | */ | |
38 | struct slot { | |
39 | struct list_head slot_list; | |
40 | struct hotplug_slot *hotplug_slot; | |
41 | struct zpci_dev *zdev; | |
42 | }; | |
43 | ||
4bee2a5d SO |
44 | static inline int slot_configure(struct slot *slot) |
45 | { | |
46 | int ret = sclp_pci_configure(slot->zdev->fid); | |
47 | ||
48 | zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, ret); | |
49 | if (!ret) | |
50 | slot->zdev->state = ZPCI_FN_STATE_CONFIGURED; | |
51 | ||
52 | return ret; | |
53 | } | |
54 | ||
55 | static inline int slot_deconfigure(struct slot *slot) | |
56 | { | |
57 | int ret = sclp_pci_deconfigure(slot->zdev->fid); | |
58 | ||
59 | zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, ret); | |
60 | if (!ret) | |
61 | slot->zdev->state = ZPCI_FN_STATE_STANDBY; | |
62 | ||
63 | return ret; | |
64 | } | |
65 | ||
7441b062 JG |
66 | static int enable_slot(struct hotplug_slot *hotplug_slot) |
67 | { | |
68 | struct slot *slot = hotplug_slot->private; | |
69 | int rc; | |
70 | ||
71 | if (slot->zdev->state != ZPCI_FN_STATE_STANDBY) | |
72 | return -EIO; | |
73 | ||
4bee2a5d SO |
74 | rc = slot_configure(slot); |
75 | if (rc) | |
76 | return rc; | |
77 | ||
78 | rc = zpci_enable_device(slot->zdev); | |
79 | if (rc) | |
80 | goto out_deconfigure; | |
81 | ||
4bee2a5d SO |
82 | pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN); |
83 | pci_bus_add_devices(slot->zdev->bus); | |
84 | ||
85 | return rc; | |
86 | ||
87 | out_deconfigure: | |
88 | slot_deconfigure(slot); | |
7441b062 JG |
89 | return rc; |
90 | } | |
91 | ||
92 | static int disable_slot(struct hotplug_slot *hotplug_slot) | |
93 | { | |
94 | struct slot *slot = hotplug_slot->private; | |
95 | int rc; | |
96 | ||
97 | if (!zpci_fn_configured(slot->zdev->state)) | |
98 | return -EIO; | |
99 | ||
8b2a7e60 SO |
100 | if (slot->zdev->pdev) |
101 | pci_stop_and_remove_bus_device(slot->zdev->pdev); | |
102 | ||
cb65a669 SO |
103 | rc = zpci_disable_device(slot->zdev); |
104 | if (rc) | |
105 | return rc; | |
8b2a7e60 | 106 | |
4bee2a5d | 107 | return slot_deconfigure(slot); |
7441b062 JG |
108 | } |
109 | ||
110 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | |
111 | { | |
112 | struct slot *slot = hotplug_slot->private; | |
113 | ||
114 | switch (slot->zdev->state) { | |
115 | case ZPCI_FN_STATE_STANDBY: | |
116 | *value = 0; | |
117 | break; | |
118 | default: | |
119 | *value = 1; | |
120 | break; | |
121 | } | |
122 | return 0; | |
123 | } | |
124 | ||
125 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | |
126 | { | |
127 | /* if the slot exits it always contains a function */ | |
128 | *value = 1; | |
129 | return 0; | |
130 | } | |
131 | ||
132 | static void release_slot(struct hotplug_slot *hotplug_slot) | |
133 | { | |
134 | struct slot *slot = hotplug_slot->private; | |
135 | ||
7441b062 JG |
136 | kfree(slot->hotplug_slot->info); |
137 | kfree(slot->hotplug_slot); | |
138 | kfree(slot); | |
139 | } | |
140 | ||
141 | static struct hotplug_slot_ops s390_hotplug_slot_ops = { | |
142 | .enable_slot = enable_slot, | |
143 | .disable_slot = disable_slot, | |
144 | .get_power_status = get_power_status, | |
145 | .get_adapter_status = get_adapter_status, | |
146 | }; | |
147 | ||
67f43f38 | 148 | int zpci_init_slot(struct zpci_dev *zdev) |
7441b062 JG |
149 | { |
150 | struct hotplug_slot *hotplug_slot; | |
151 | struct hotplug_slot_info *info; | |
152 | char name[SLOT_NAME_SIZE]; | |
153 | struct slot *slot; | |
154 | int rc; | |
155 | ||
156 | if (!zdev) | |
157 | return 0; | |
158 | ||
159 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); | |
160 | if (!slot) | |
161 | goto error; | |
162 | ||
163 | hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); | |
164 | if (!hotplug_slot) | |
165 | goto error_hp; | |
166 | hotplug_slot->private = slot; | |
167 | ||
168 | slot->hotplug_slot = hotplug_slot; | |
169 | slot->zdev = zdev; | |
170 | ||
171 | info = kzalloc(sizeof(*info), GFP_KERNEL); | |
172 | if (!info) | |
173 | goto error_info; | |
174 | hotplug_slot->info = info; | |
175 | ||
176 | hotplug_slot->ops = &s390_hotplug_slot_ops; | |
177 | hotplug_slot->release = &release_slot; | |
178 | ||
179 | get_power_status(hotplug_slot, &info->power_status); | |
180 | get_adapter_status(hotplug_slot, &info->adapter_status); | |
181 | ||
182 | snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid); | |
183 | rc = pci_hp_register(slot->hotplug_slot, zdev->bus, | |
184 | ZPCI_DEVFN, name); | |
1f1dcbd4 | 185 | if (rc) |
7441b062 | 186 | goto error_reg; |
1f1dcbd4 | 187 | |
7441b062 JG |
188 | list_add(&slot->slot_list, &s390_hotplug_slot_list); |
189 | return 0; | |
190 | ||
191 | error_reg: | |
192 | kfree(info); | |
193 | error_info: | |
194 | kfree(hotplug_slot); | |
195 | error_hp: | |
196 | kfree(slot); | |
197 | error: | |
198 | return -ENOMEM; | |
199 | } | |
200 | ||
67f43f38 | 201 | void zpci_exit_slot(struct zpci_dev *zdev) |
7441b062 JG |
202 | { |
203 | struct list_head *tmp, *n; | |
204 | struct slot *slot; | |
205 | ||
206 | list_for_each_safe(tmp, n, &s390_hotplug_slot_list) { | |
207 | slot = list_entry(tmp, struct slot, slot_list); | |
208 | if (slot->zdev != zdev) | |
209 | continue; | |
210 | list_del(&slot->slot_list); | |
211 | pci_hp_deregister(slot->hotplug_slot); | |
212 | } | |
213 | } |