1ae115bc7Smrj /*
2ae115bc7Smrj * CDDL HEADER START
3ae115bc7Smrj *
4ae115bc7Smrj * The contents of this file are subject to the terms of the
5ae115bc7Smrj * Common Development and Distribution License (the "License").
6ae115bc7Smrj * You may not use this file except in compliance with the License.
7ae115bc7Smrj *
8ae115bc7Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ae115bc7Smrj * or http://www.opensolaris.org/os/licensing.
10ae115bc7Smrj * See the License for the specific language governing permissions
11ae115bc7Smrj * and limitations under the License.
12ae115bc7Smrj *
13ae115bc7Smrj * When distributing Covered Code, include this CDDL HEADER in each
14ae115bc7Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ae115bc7Smrj * If applicable, add the following below this CDDL HEADER, with the
16ae115bc7Smrj * fields enclosed by brackets "[]" replaced with your own identifying
17ae115bc7Smrj * information: Portions Copyright [yyyy] [name of copyright owner]
18ae115bc7Smrj *
19ae115bc7Smrj * CDDL HEADER END
20ae115bc7Smrj */
21ae115bc7Smrj /*
229757e35cSStephen Hanson * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23ae115bc7Smrj * Use is subject to license terms.
24ae115bc7Smrj */
25*cd21e7c5SGarrett D'Amore /*
26*cd21e7c5SGarrett D'Amore * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
27*cd21e7c5SGarrett D'Amore */
28ae115bc7Smrj
29ae115bc7Smrj /*
30ae115bc7Smrj * PCI to PCI bus bridge nexus driver
31ae115bc7Smrj */
32ae115bc7Smrj
33ae115bc7Smrj #include <sys/conf.h>
34ae115bc7Smrj #include <sys/kmem.h>
35ae115bc7Smrj #include <sys/debug.h>
36ae115bc7Smrj #include <sys/modctl.h>
37ae115bc7Smrj #include <sys/autoconf.h>
38ae115bc7Smrj #include <sys/ddi_impldefs.h>
39ae115bc7Smrj #include <sys/pci.h>
4026947304SEvan Yan #include <sys/pci_impl.h>
41eae2e508Skrishnae #include <sys/pcie_impl.h>
42ae115bc7Smrj #include <sys/ddi.h>
43ae115bc7Smrj #include <sys/sunddi.h>
44ae115bc7Smrj #include <sys/sunndi.h>
45ae115bc7Smrj #include <sys/ddifm.h>
46ae115bc7Smrj #include <sys/ndifm.h>
47ae115bc7Smrj #include <sys/fm/protocol.h>
4826947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h>
49ae115bc7Smrj #include <sys/hotplug/pci/pcihp.h>
50ae115bc7Smrj #include <sys/pci_intr_lib.h>
51ae115bc7Smrj #include <sys/psm.h>
52cb7ea99dSJimmy Vetayases #include <sys/pci_cap.h>
53ae115bc7Smrj
54ae115bc7Smrj /*
55ae115bc7Smrj * The variable controls the default setting of the command register
56ae115bc7Smrj * for pci devices. See ppb_initchild() for details.
57ae115bc7Smrj */
58ae115bc7Smrj static ushort_t ppb_command_default = PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_IO;
59ae115bc7Smrj
60ae115bc7Smrj
61ae115bc7Smrj static int ppb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *,
62ae115bc7Smrj off_t, off_t, caddr_t *);
63ae115bc7Smrj static int ppb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
64ae115bc7Smrj void *, void *);
65ae115bc7Smrj static int ppb_fm_init(dev_info_t *, dev_info_t *, int,
66ae115bc7Smrj ddi_iblock_cookie_t *);
67ae115bc7Smrj static int ppb_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *);
68ae115bc7Smrj static int ppb_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
69ae115bc7Smrj ddi_intr_handle_impl_t *, void *);
70ae115bc7Smrj
71ae115bc7Smrj /*
72ae115bc7Smrj * ppb_support_msi: Flag that controls MSI support across P2P Bridges.
73ae115bc7Smrj * By default, MSI is not supported except for special cases like HT
74ae115bc7Smrj * bridges/tunnels that have HT MSI mapping enabled.
75ae115bc7Smrj *
76ae115bc7Smrj * However, MSI support behavior can be patched on a system by changing
77ae115bc7Smrj * the value of this flag as shown below:-
78ae115bc7Smrj * 0 = default value, MSI is allowed by this driver for special cases
79ae115bc7Smrj * 1 = MSI supported without any checks for this driver
80ae115bc7Smrj * -1 = MSI not supported at all
81ae115bc7Smrj */
82ae115bc7Smrj int ppb_support_msi = 0;
83ae115bc7Smrj
84ae115bc7Smrj /*
85ae115bc7Smrj * Controls the usage of the Hypertransport MSI mapping capability
86ae115bc7Smrj * 0 = default value, leave hardware function as it is
87ae115bc7Smrj * 1 = always enable HT MSI mapping
88ae115bc7Smrj * -1 = always disable HT MSI mapping
89ae115bc7Smrj */
90ae115bc7Smrj int ppb_support_ht_msimap = 0;
91ae115bc7Smrj
92ae115bc7Smrj struct bus_ops ppb_bus_ops = {
93ae115bc7Smrj BUSO_REV,
94ae115bc7Smrj ppb_bus_map,
95ae115bc7Smrj 0,
96ae115bc7Smrj 0,
97ae115bc7Smrj 0,
98ae115bc7Smrj i_ddi_map_fault,
99*cd21e7c5SGarrett D'Amore 0,
100ae115bc7Smrj ddi_dma_allochdl,
101ae115bc7Smrj ddi_dma_freehdl,
102ae115bc7Smrj ddi_dma_bindhdl,
103ae115bc7Smrj ddi_dma_unbindhdl,
104ae115bc7Smrj ddi_dma_flush,
105ae115bc7Smrj ddi_dma_win,
106ae115bc7Smrj ddi_dma_mctl,
107ae115bc7Smrj ppb_ctlops,
108ae115bc7Smrj ddi_bus_prop_op,
109ae115bc7Smrj 0, /* (*bus_get_eventcookie)(); */
110ae115bc7Smrj 0, /* (*bus_add_eventcall)(); */
111ae115bc7Smrj 0, /* (*bus_remove_eventcall)(); */
112ae115bc7Smrj 0, /* (*bus_post_event)(); */
113ae115bc7Smrj 0, /* (*bus_intr_ctl)(); */
114ae115bc7Smrj 0, /* (*bus_config)(); */
115ae115bc7Smrj 0, /* (*bus_unconfig)(); */
116ae115bc7Smrj ppb_fm_init, /* (*bus_fm_init)(); */
117ae115bc7Smrj NULL, /* (*bus_fm_fini)(); */
118ae115bc7Smrj NULL, /* (*bus_fm_access_enter)(); */
119ae115bc7Smrj NULL, /* (*bus_fm_access_exit)(); */
120ae115bc7Smrj NULL, /* (*bus_power)(); */
12126947304SEvan Yan ppb_intr_ops, /* (*bus_intr_op)(); */
12226947304SEvan Yan pcie_hp_common_ops /* (*bus_hp_op)(); */
123ae115bc7Smrj };
124ae115bc7Smrj
125ae115bc7Smrj /*
126ae115bc7Smrj * The goal here is to leverage off of the pcihp.c source without making
127ae115bc7Smrj * changes to it. Call into it's cb_ops directly if needed.
128ae115bc7Smrj */
129ae115bc7Smrj static int ppb_open(dev_t *, int, int, cred_t *);
130ae115bc7Smrj static int ppb_close(dev_t, int, int, cred_t *);
131ae115bc7Smrj static int ppb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
132ae115bc7Smrj static int ppb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
133ae115bc7Smrj caddr_t, int *);
134ae115bc7Smrj static int ppb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
135eae2e508Skrishnae static void ppb_peekpoke_cb(dev_info_t *, ddi_fm_error_t *);
136ae115bc7Smrj
137ae115bc7Smrj struct cb_ops ppb_cb_ops = {
138ae115bc7Smrj ppb_open, /* open */
139ae115bc7Smrj ppb_close, /* close */
140ae115bc7Smrj nodev, /* strategy */
141ae115bc7Smrj nodev, /* print */
142ae115bc7Smrj nodev, /* dump */
143ae115bc7Smrj nodev, /* read */
144ae115bc7Smrj nodev, /* write */
145ae115bc7Smrj ppb_ioctl, /* ioctl */
146ae115bc7Smrj nodev, /* devmap */
147ae115bc7Smrj nodev, /* mmap */
148ae115bc7Smrj nodev, /* segmap */
149ae115bc7Smrj nochpoll, /* poll */
150ae115bc7Smrj ppb_prop_op, /* cb_prop_op */
151ae115bc7Smrj NULL, /* streamtab */
152ae115bc7Smrj D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
153ae115bc7Smrj CB_REV, /* rev */
154ae115bc7Smrj nodev, /* int (*cb_aread)() */
155ae115bc7Smrj nodev /* int (*cb_awrite)() */
156ae115bc7Smrj };
157ae115bc7Smrj
158ae115bc7Smrj
159ae115bc7Smrj static int ppb_probe(dev_info_t *);
160ae115bc7Smrj static int ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
161ae115bc7Smrj static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
162ae115bc7Smrj
163ae115bc7Smrj struct dev_ops ppb_ops = {
164ae115bc7Smrj DEVO_REV, /* devo_rev */
165ae115bc7Smrj 0, /* refcnt */
166ae115bc7Smrj ppb_info, /* info */
167ae115bc7Smrj nulldev, /* identify */
168ae115bc7Smrj ppb_probe, /* probe */
169ae115bc7Smrj ppb_attach, /* attach */
170ae115bc7Smrj ppb_detach, /* detach */
171ae115bc7Smrj nulldev, /* reset */
172ae115bc7Smrj &ppb_cb_ops, /* driver operations */
17319397407SSherry Moore &ppb_bus_ops, /* bus operations */
17419397407SSherry Moore NULL, /* power */
17519397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */
176ae115bc7Smrj };
177ae115bc7Smrj
178ae115bc7Smrj /*
179ae115bc7Smrj * Module linkage information for the kernel.
180ae115bc7Smrj */
181ae115bc7Smrj
182ae115bc7Smrj static struct modldrv modldrv = {
183ae115bc7Smrj &mod_driverops, /* Type of module */
18426947304SEvan Yan "Standard PCI to PCI bridge nexus driver",
185ae115bc7Smrj &ppb_ops, /* driver ops */
186ae115bc7Smrj };
187ae115bc7Smrj
188ae115bc7Smrj static struct modlinkage modlinkage = {
189ae115bc7Smrj MODREV_1,
190ae115bc7Smrj (void *)&modldrv,
191ae115bc7Smrj NULL
192ae115bc7Smrj };
193ae115bc7Smrj
194ae115bc7Smrj /*
195ae115bc7Smrj * soft state pointer and structure template:
196ae115bc7Smrj */
197ae115bc7Smrj static void *ppb_state;
198ae115bc7Smrj
199ae115bc7Smrj typedef struct {
200ae115bc7Smrj dev_info_t *dip;
201ae115bc7Smrj int ppb_fmcap;
202ae115bc7Smrj ddi_iblock_cookie_t ppb_fm_ibc;
20326947304SEvan Yan kmutex_t ppb_mutex;
204ae115bc7Smrj kmutex_t ppb_peek_poke_mutex;
205ae115bc7Smrj kmutex_t ppb_err_mutex;
206ae115bc7Smrj
207ae115bc7Smrj /*
208ae115bc7Smrj * cpr support:
209ae115bc7Smrj */
210ae115bc7Smrj uint_t config_state_index;
211ae115bc7Smrj struct {
212ae115bc7Smrj dev_info_t *dip;
213ae115bc7Smrj ushort_t command;
214ae115bc7Smrj uchar_t cache_line_size;
215ae115bc7Smrj uchar_t latency_timer;
216ae115bc7Smrj uchar_t header_type;
217ae115bc7Smrj uchar_t sec_latency_timer;
218ae115bc7Smrj ushort_t bridge_control;
219ae115bc7Smrj } config_state[PCI_MAX_CHILDREN];
220eae2e508Skrishnae
221c85864d8SKrishna Elango uint16_t parent_bus;
222ae115bc7Smrj } ppb_devstate_t;
223ae115bc7Smrj
224ae115bc7Smrj
225ae115bc7Smrj /*
226ae115bc7Smrj * forward function declarations:
227ae115bc7Smrj */
228ae115bc7Smrj static void ppb_removechild(dev_info_t *);
229ae115bc7Smrj static int ppb_initchild(dev_info_t *child);
230ae115bc7Smrj static void ppb_save_config_regs(ppb_devstate_t *ppb_p);
231ae115bc7Smrj static void ppb_restore_config_regs(ppb_devstate_t *ppb_p);
232ae115bc7Smrj static boolean_t ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl);
233ae115bc7Smrj static int ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd);
234ae115bc7Smrj
235ae115bc7Smrj /*
236ae115bc7Smrj * for <cmd> in ppb_ht_msimap_set
237ae115bc7Smrj */
238ae115bc7Smrj #define HT_MSIMAP_ENABLE 1
239ae115bc7Smrj #define HT_MSIMAP_DISABLE 0
240ae115bc7Smrj
241ae115bc7Smrj
242ae115bc7Smrj int
_init(void)243ae115bc7Smrj _init(void)
244ae115bc7Smrj {
245ae115bc7Smrj int e;
246ae115bc7Smrj
247ae115bc7Smrj if ((e = ddi_soft_state_init(&ppb_state, sizeof (ppb_devstate_t),
248ae115bc7Smrj 1)) == 0 && (e = mod_install(&modlinkage)) != 0)
249ae115bc7Smrj ddi_soft_state_fini(&ppb_state);
250ae115bc7Smrj return (e);
251ae115bc7Smrj }
252ae115bc7Smrj
253ae115bc7Smrj int
_fini(void)254ae115bc7Smrj _fini(void)
255ae115bc7Smrj {
256ae115bc7Smrj int e;
257ae115bc7Smrj
258ae115bc7Smrj if ((e = mod_remove(&modlinkage)) == 0)
259ae115bc7Smrj ddi_soft_state_fini(&ppb_state);
260ae115bc7Smrj return (e);
261ae115bc7Smrj }
262ae115bc7Smrj
263ae115bc7Smrj int
_info(struct modinfo * modinfop)264ae115bc7Smrj _info(struct modinfo *modinfop)
265ae115bc7Smrj {
266ae115bc7Smrj return (mod_info(&modlinkage, modinfop));
267ae115bc7Smrj }
268ae115bc7Smrj
269ae115bc7Smrj /*ARGSUSED*/
270ae115bc7Smrj static int
ppb_probe(dev_info_t * devi)271ae115bc7Smrj ppb_probe(dev_info_t *devi)
272ae115bc7Smrj {
273ae115bc7Smrj return (DDI_PROBE_SUCCESS);
274ae115bc7Smrj }
275ae115bc7Smrj
276ae115bc7Smrj /*ARGSUSED*/
277ae115bc7Smrj static int
ppb_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)278ae115bc7Smrj ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
279ae115bc7Smrj {
280eae2e508Skrishnae dev_info_t *root = ddi_root_node();
281ae115bc7Smrj int instance;
282ae115bc7Smrj ppb_devstate_t *ppb;
283eae2e508Skrishnae dev_info_t *pdip;
284ae115bc7Smrj ddi_acc_handle_t config_handle;
285eae2e508Skrishnae char *bus;
28626947304SEvan Yan int ret;
287ae115bc7Smrj
288ae115bc7Smrj switch (cmd) {
289ae115bc7Smrj case DDI_ATTACH:
290ae115bc7Smrj
291ae115bc7Smrj /*
292ae115bc7Smrj * Make sure the "device_type" property exists.
293ae115bc7Smrj */
294ae115bc7Smrj (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
295ae115bc7Smrj "device_type", "pci");
296ae115bc7Smrj
297ae115bc7Smrj /*
298ae115bc7Smrj * Allocate and get soft state structure.
299ae115bc7Smrj */
300ae115bc7Smrj instance = ddi_get_instance(devi);
301ae115bc7Smrj if (ddi_soft_state_zalloc(ppb_state, instance) != DDI_SUCCESS)
302ae115bc7Smrj return (DDI_FAILURE);
303ae115bc7Smrj ppb = ddi_get_soft_state(ppb_state, instance);
304ae115bc7Smrj ppb->dip = devi;
305ae115bc7Smrj
306ae115bc7Smrj /*
307ae115bc7Smrj * don't enable ereports if immediate child of npe
308ae115bc7Smrj */
309ae115bc7Smrj if (strcmp(ddi_driver_name(ddi_get_parent(devi)), "npe") == 0)
310ae115bc7Smrj ppb->ppb_fmcap = DDI_FM_ERRCB_CAPABLE |
311ae115bc7Smrj DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
312ae115bc7Smrj else
313ae115bc7Smrj ppb->ppb_fmcap = DDI_FM_EREPORT_CAPABLE |
314ae115bc7Smrj DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
315ae115bc7Smrj DDI_FM_DMACHK_CAPABLE;
316ae115bc7Smrj
317ae115bc7Smrj ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc);
31826947304SEvan Yan mutex_init(&ppb->ppb_mutex, NULL, MUTEX_DRIVER, NULL);
319ae115bc7Smrj mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER,
320ae115bc7Smrj (void *)ppb->ppb_fm_ibc);
321ae115bc7Smrj mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER,
322ae115bc7Smrj (void *)ppb->ppb_fm_ibc);
323ae115bc7Smrj
324ae115bc7Smrj if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
325ae115bc7Smrj DDI_FM_EREPORT_CAPABLE))
326ae115bc7Smrj pci_ereport_setup(devi);
327ae115bc7Smrj if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
328ae115bc7Smrj ddi_fm_handler_register(devi, ppb_fm_callback, NULL);
329ae115bc7Smrj
330ae115bc7Smrj if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) {
331ae115bc7Smrj if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
332ae115bc7Smrj ddi_fm_handler_unregister(devi);
333ae115bc7Smrj if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
334ae115bc7Smrj DDI_FM_EREPORT_CAPABLE))
335ae115bc7Smrj pci_ereport_teardown(devi);
336ae115bc7Smrj ddi_fm_fini(devi);
337ae115bc7Smrj ddi_soft_state_free(ppb_state, instance);
338ae115bc7Smrj return (DDI_FAILURE);
339ae115bc7Smrj }
340ae115bc7Smrj
341c85864d8SKrishna Elango ppb->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO;
342eae2e508Skrishnae for (pdip = ddi_get_parent(devi); pdip && (pdip != root) &&
343eae2e508Skrishnae (ppb->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV);
344eae2e508Skrishnae pdip = ddi_get_parent(pdip)) {
345eae2e508Skrishnae if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
346eae2e508Skrishnae DDI_PROP_DONTPASS, "device_type", &bus) !=
347eae2e508Skrishnae DDI_PROP_SUCCESS)
348eae2e508Skrishnae break;
349eae2e508Skrishnae
350eae2e508Skrishnae if (strcmp(bus, "pciex") == 0)
351eae2e508Skrishnae ppb->parent_bus =
352eae2e508Skrishnae PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
353eae2e508Skrishnae
354eae2e508Skrishnae ddi_prop_free(bus);
355eae2e508Skrishnae }
356eae2e508Skrishnae
357ae115bc7Smrj if (ppb_support_ht_msimap == 1)
358ae115bc7Smrj (void) ppb_ht_msimap_set(config_handle,
359ae115bc7Smrj HT_MSIMAP_ENABLE);
360ae115bc7Smrj else if (ppb_support_ht_msimap == -1)
361ae115bc7Smrj (void) ppb_ht_msimap_set(config_handle,
362ae115bc7Smrj HT_MSIMAP_DISABLE);
363ae115bc7Smrj
364ae115bc7Smrj pci_config_teardown(&config_handle);
365ae115bc7Smrj
366ae115bc7Smrj /*
36726947304SEvan Yan * Initialize hotplug support on this bus.
368ae115bc7Smrj */
36926947304SEvan Yan if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
37026947304SEvan Yan ret = pcie_init(devi, NULL);
37126947304SEvan Yan else
37226947304SEvan Yan ret = pcihp_init(devi);
37326947304SEvan Yan
37426947304SEvan Yan if (ret != DDI_SUCCESS) {
3752df1fe9cSrandyf cmn_err(CE_WARN,
3762df1fe9cSrandyf "pci: Failed to setup hotplug framework");
37726947304SEvan Yan (void) ppb_detach(devi, DDI_DETACH);
37826947304SEvan Yan return (ret);
37926947304SEvan Yan }
380ae115bc7Smrj
381ae115bc7Smrj ddi_report_dev(devi);
382ae115bc7Smrj return (DDI_SUCCESS);
383ae115bc7Smrj
384ae115bc7Smrj case DDI_RESUME:
385ae115bc7Smrj
386ae115bc7Smrj /*
387ae115bc7Smrj * Get the soft state structure for the bridge.
388ae115bc7Smrj */
389ae115bc7Smrj ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
390ae115bc7Smrj ppb_restore_config_regs(ppb);
391ae115bc7Smrj return (DDI_SUCCESS);
392ae115bc7Smrj
393ae115bc7Smrj default:
394ae115bc7Smrj break;
395ae115bc7Smrj }
396ae115bc7Smrj return (DDI_FAILURE);
397ae115bc7Smrj }
398ae115bc7Smrj
399ae115bc7Smrj /*ARGSUSED*/
400ae115bc7Smrj static int
ppb_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)401ae115bc7Smrj ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
402ae115bc7Smrj {
403ae115bc7Smrj ppb_devstate_t *ppb;
40426947304SEvan Yan int ret;
405ae115bc7Smrj
406ae115bc7Smrj switch (cmd) {
407ae115bc7Smrj case DDI_DETACH:
408ae115bc7Smrj (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
409ae115bc7Smrj
410ae115bc7Smrj ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
411ae115bc7Smrj if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE)
412ae115bc7Smrj ddi_fm_handler_unregister(devi);
413ae115bc7Smrj if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE |
414ae115bc7Smrj DDI_FM_EREPORT_CAPABLE))
415ae115bc7Smrj pci_ereport_teardown(devi);
41626947304SEvan Yan
41726947304SEvan Yan /*
41826947304SEvan Yan * Uninitialize hotplug support on this bus.
41926947304SEvan Yan */
42026947304SEvan Yan ret = (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) ?
42126947304SEvan Yan pcie_uninit(devi) : pcihp_uninit(devi);
42226947304SEvan Yan if (ret != DDI_SUCCESS)
42326947304SEvan Yan return (DDI_FAILURE);
42426947304SEvan Yan
425ae115bc7Smrj mutex_destroy(&ppb->ppb_peek_poke_mutex);
426ae115bc7Smrj mutex_destroy(&ppb->ppb_err_mutex);
42726947304SEvan Yan mutex_destroy(&ppb->ppb_mutex);
428ae115bc7Smrj ddi_fm_fini(devi);
429ae115bc7Smrj
430ae115bc7Smrj /*
431ae115bc7Smrj * And finally free the per-pci soft state.
432ae115bc7Smrj */
433ae115bc7Smrj ddi_soft_state_free(ppb_state, ddi_get_instance(devi));
434ae115bc7Smrj
435ae115bc7Smrj return (DDI_SUCCESS);
436ae115bc7Smrj
437ae115bc7Smrj case DDI_SUSPEND:
438ae115bc7Smrj ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi));
439ae115bc7Smrj ppb_save_config_regs(ppb);
440ae115bc7Smrj return (DDI_SUCCESS);
441ae115bc7Smrj
442ae115bc7Smrj default:
443ae115bc7Smrj break;
444ae115bc7Smrj }
445ae115bc7Smrj return (DDI_FAILURE);
446ae115bc7Smrj }
447ae115bc7Smrj
448ae115bc7Smrj /*ARGSUSED*/
449ae115bc7Smrj static int
ppb_bus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t offset,off_t len,caddr_t * vaddrp)450ae115bc7Smrj ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
451ae115bc7Smrj off_t offset, off_t len, caddr_t *vaddrp)
452ae115bc7Smrj {
453ae115bc7Smrj dev_info_t *pdip;
454837c1ac4SStephen Hanson ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
455837c1ac4SStephen Hanson ddi_get_instance(dip));
456ae115bc7Smrj
4579757e35cSStephen Hanson if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") == 0 &&
4589757e35cSStephen Hanson mp->map_handlep != NULL) {
459837c1ac4SStephen Hanson ddi_acc_impl_t *hdlp =
460837c1ac4SStephen Hanson (ddi_acc_impl_t *)(mp->map_handlep)->ah_platform_private;
461837c1ac4SStephen Hanson hdlp->ahi_err_mutexp = &ppb->ppb_err_mutex;
462837c1ac4SStephen Hanson hdlp->ahi_peekpoke_mutexp = &ppb->ppb_peek_poke_mutex;
463837c1ac4SStephen Hanson hdlp->ahi_scan_dip = dip;
464837c1ac4SStephen Hanson hdlp->ahi_scan = ppb_peekpoke_cb;
465837c1ac4SStephen Hanson }
466ae115bc7Smrj pdip = (dev_info_t *)DEVI(dip)->devi_parent;
467ae115bc7Smrj return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
468ae115bc7Smrj rdip, mp, offset, len, vaddrp));
469ae115bc7Smrj }
470ae115bc7Smrj
471ae115bc7Smrj /*ARGSUSED*/
472ae115bc7Smrj static int
ppb_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)473ae115bc7Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
474ae115bc7Smrj ddi_ctl_enum_t ctlop, void *arg, void *result)
475ae115bc7Smrj {
476ae115bc7Smrj pci_regspec_t *drv_regp;
477ae115bc7Smrj int reglen;
478ae115bc7Smrj int rn;
479ae115bc7Smrj int totreg;
480eae2e508Skrishnae ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
481eae2e508Skrishnae ddi_get_instance(dip));
482eae2e508Skrishnae struct detachspec *dsp;
4832df1fe9cSrandyf struct attachspec *asp;
484ae115bc7Smrj
485ae115bc7Smrj switch (ctlop) {
486ae115bc7Smrj case DDI_CTLOPS_REPORTDEV:
487ae115bc7Smrj if (rdip == (dev_info_t *)0)
488ae115bc7Smrj return (DDI_FAILURE);
489ae115bc7Smrj cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
490ae115bc7Smrj ddi_node_name(rdip), ddi_get_name_addr(rdip),
491ae115bc7Smrj ddi_driver_name(rdip),
492ae115bc7Smrj ddi_get_instance(rdip));
493ae115bc7Smrj return (DDI_SUCCESS);
494ae115bc7Smrj
495ae115bc7Smrj case DDI_CTLOPS_INITCHILD:
496ae115bc7Smrj return (ppb_initchild((dev_info_t *)arg));
497ae115bc7Smrj
498ae115bc7Smrj case DDI_CTLOPS_UNINITCHILD:
499ae115bc7Smrj ppb_removechild((dev_info_t *)arg);
500ae115bc7Smrj return (DDI_SUCCESS);
501ae115bc7Smrj
502ae115bc7Smrj case DDI_CTLOPS_SIDDEV:
503ae115bc7Smrj return (DDI_SUCCESS);
504ae115bc7Smrj
505ae115bc7Smrj case DDI_CTLOPS_REGSIZE:
506ae115bc7Smrj case DDI_CTLOPS_NREGS:
507ae115bc7Smrj if (rdip == (dev_info_t *)0)
508ae115bc7Smrj return (DDI_FAILURE);
509ae115bc7Smrj break;
510ae115bc7Smrj
5112df1fe9cSrandyf /* X86 systems support PME wakeup from suspend */
5122df1fe9cSrandyf case DDI_CTLOPS_ATTACH:
513eae2e508Skrishnae if (!pcie_is_child(dip, rdip))
514eae2e508Skrishnae return (DDI_SUCCESS);
515eae2e508Skrishnae
5162df1fe9cSrandyf asp = (struct attachspec *)arg;
517eae2e508Skrishnae if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
518eae2e508Skrishnae (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS))
519eae2e508Skrishnae pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd);
520eae2e508Skrishnae
5212df1fe9cSrandyf if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
5222df1fe9cSrandyf if (pci_pre_resume(rdip) != DDI_SUCCESS)
5232df1fe9cSrandyf return (DDI_FAILURE);
5242df1fe9cSrandyf
525eae2e508Skrishnae return (DDI_SUCCESS);
5262df1fe9cSrandyf
5272df1fe9cSrandyf case DDI_CTLOPS_DETACH:
528eae2e508Skrishnae if (!pcie_is_child(dip, rdip))
529eae2e508Skrishnae return (DDI_SUCCESS);
530eae2e508Skrishnae
531eae2e508Skrishnae dsp = (struct detachspec *)arg;
532eae2e508Skrishnae if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
533eae2e508Skrishnae (dsp->when == DDI_PRE))
534eae2e508Skrishnae pf_fini(rdip, dsp->cmd);
535eae2e508Skrishnae
536eae2e508Skrishnae if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST)
5372df1fe9cSrandyf if (pci_post_suspend(rdip) != DDI_SUCCESS)
5382df1fe9cSrandyf return (DDI_FAILURE);
539eae2e508Skrishnae
540eae2e508Skrishnae return (DDI_SUCCESS);
5412df1fe9cSrandyf
542ae115bc7Smrj case DDI_CTLOPS_PEEK:
543ae115bc7Smrj case DDI_CTLOPS_POKE:
544ae115bc7Smrj if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0)
545ae115bc7Smrj return (ddi_ctlops(dip, rdip, ctlop, arg, result));
546ae115bc7Smrj return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
547ae115bc7Smrj ddi_ctlops, &ppb->ppb_err_mutex,
548eae2e508Skrishnae &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb));
549ae115bc7Smrj
550ae115bc7Smrj default:
551ae115bc7Smrj return (ddi_ctlops(dip, rdip, ctlop, arg, result));
552ae115bc7Smrj }
553ae115bc7Smrj
554ae115bc7Smrj *(int *)result = 0;
555ae115bc7Smrj if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
556ae115bc7Smrj DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
557ae115bc7Smrj (caddr_t)&drv_regp, ®len) != DDI_SUCCESS)
558ae115bc7Smrj return (DDI_FAILURE);
559ae115bc7Smrj
560ae115bc7Smrj totreg = reglen / sizeof (pci_regspec_t);
561ae115bc7Smrj if (ctlop == DDI_CTLOPS_NREGS)
562ae115bc7Smrj *(int *)result = totreg;
563ae115bc7Smrj else if (ctlop == DDI_CTLOPS_REGSIZE) {
564ae115bc7Smrj rn = *(int *)arg;
565ae115bc7Smrj if (rn >= totreg) {
566ae115bc7Smrj kmem_free(drv_regp, reglen);
567ae115bc7Smrj return (DDI_FAILURE);
568ae115bc7Smrj }
569ae115bc7Smrj *(off_t *)result = drv_regp[rn].pci_size_low;
570ae115bc7Smrj }
571ae115bc7Smrj
572ae115bc7Smrj kmem_free(drv_regp, reglen);
573ae115bc7Smrj return (DDI_SUCCESS);
574ae115bc7Smrj }
575ae115bc7Smrj
576ae115bc7Smrj static int
ppb_name_child(dev_info_t * child,char * name,int namelen)577ae115bc7Smrj ppb_name_child(dev_info_t *child, char *name, int namelen)
578ae115bc7Smrj {
579ae115bc7Smrj pci_regspec_t *pci_rp;
580ae115bc7Smrj uint_t slot, func;
581ae115bc7Smrj char **unit_addr;
582ae115bc7Smrj uint_t n;
583ae115bc7Smrj
584ae115bc7Smrj /*
585ae115bc7Smrj * For .conf nodes, use unit-address property as name
586ae115bc7Smrj */
587ae115bc7Smrj if (ndi_dev_is_persistent_node(child) == 0) {
588ae115bc7Smrj if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
589ae115bc7Smrj DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
590ae115bc7Smrj DDI_PROP_SUCCESS) {
591ae115bc7Smrj cmn_err(CE_WARN,
592ae115bc7Smrj "cannot find unit-address in %s.conf",
593ae115bc7Smrj ddi_driver_name(child));
594ae115bc7Smrj return (DDI_FAILURE);
595ae115bc7Smrj }
596ae115bc7Smrj if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
597ae115bc7Smrj cmn_err(CE_WARN, "unit-address property in %s.conf"
598ae115bc7Smrj " not well-formed", ddi_driver_name(child));
599ae115bc7Smrj ddi_prop_free(unit_addr);
600ae115bc7Smrj return (DDI_SUCCESS);
601ae115bc7Smrj }
602ae115bc7Smrj (void) snprintf(name, namelen, "%s", *unit_addr);
603ae115bc7Smrj ddi_prop_free(unit_addr);
604ae115bc7Smrj return (DDI_SUCCESS);
605ae115bc7Smrj }
606ae115bc7Smrj
607ae115bc7Smrj /* get child "reg" property */
608ae115bc7Smrj if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
609ae115bc7Smrj DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
610ae115bc7Smrj return (DDI_FAILURE);
611ae115bc7Smrj }
612ae115bc7Smrj
613ae115bc7Smrj /* copy the device identifications */
614ae115bc7Smrj slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
615ae115bc7Smrj func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
616ae115bc7Smrj
617ae115bc7Smrj if (func != 0)
618ae115bc7Smrj (void) snprintf(name, namelen, "%x,%x", slot, func);
619ae115bc7Smrj else
620ae115bc7Smrj (void) snprintf(name, namelen, "%x", slot);
621ae115bc7Smrj
622ae115bc7Smrj ddi_prop_free(pci_rp);
623ae115bc7Smrj return (DDI_SUCCESS);
624ae115bc7Smrj }
625ae115bc7Smrj
626ae115bc7Smrj static int
ppb_initchild(dev_info_t * child)627ae115bc7Smrj ppb_initchild(dev_info_t *child)
628ae115bc7Smrj {
629ae115bc7Smrj struct ddi_parent_private_data *pdptr;
630eae2e508Skrishnae ppb_devstate_t *ppb;
631ae115bc7Smrj char name[MAXNAMELEN];
632ae115bc7Smrj ddi_acc_handle_t config_handle;
633ae115bc7Smrj ushort_t command_preserve, command;
634ae115bc7Smrj
635eae2e508Skrishnae ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
636eae2e508Skrishnae ddi_get_instance(ddi_get_parent(child)));
637eae2e508Skrishnae
638ae115bc7Smrj if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
639ae115bc7Smrj return (DDI_FAILURE);
640ae115bc7Smrj ddi_set_name_addr(child, name);
641ae115bc7Smrj
642ae115bc7Smrj /*
643ae115bc7Smrj * Pseudo nodes indicate a prototype node with per-instance
644ae115bc7Smrj * properties to be merged into the real h/w device node.
645ae115bc7Smrj * The interpretation of the unit-address is DD[,F]
646ae115bc7Smrj * where DD is the device id and F is the function.
647ae115bc7Smrj */
648ae115bc7Smrj if (ndi_dev_is_persistent_node(child) == 0) {
649ae115bc7Smrj extern int pci_allow_pseudo_children;
650ae115bc7Smrj
651ae115bc7Smrj ddi_set_parent_data(child, NULL);
652ae115bc7Smrj
653ae115bc7Smrj /*
654ae115bc7Smrj * Try to merge the properties from this prototype
655ae115bc7Smrj * node into real h/w nodes.
656ae115bc7Smrj */
657ae115bc7Smrj if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) {
658ae115bc7Smrj /*
659ae115bc7Smrj * Merged ok - return failure to remove the node.
660ae115bc7Smrj */
661ae115bc7Smrj ddi_set_name_addr(child, NULL);
662ae115bc7Smrj return (DDI_FAILURE);
663ae115bc7Smrj }
664ae115bc7Smrj
665ae115bc7Smrj /* workaround for ddivs to run under PCI */
666ae115bc7Smrj if (pci_allow_pseudo_children)
667ae115bc7Smrj return (DDI_SUCCESS);
668ae115bc7Smrj
669ae115bc7Smrj /*
670ae115bc7Smrj * The child was not merged into a h/w node,
671ae115bc7Smrj * but there's not much we can do with it other
672ae115bc7Smrj * than return failure to cause the node to be removed.
673ae115bc7Smrj */
674ae115bc7Smrj cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
675ae115bc7Smrj ddi_driver_name(child), ddi_get_name_addr(child),
676ae115bc7Smrj ddi_driver_name(child));
677ae115bc7Smrj ddi_set_name_addr(child, NULL);
678ae115bc7Smrj return (DDI_NOT_WELL_FORMED);
679ae115bc7Smrj }
680ae115bc7Smrj
681eae2e508Skrishnae ddi_set_parent_data(child, NULL);
682eae2e508Skrishnae
683eae2e508Skrishnae /*
684eae2e508Skrishnae * PCIe FMA specific
685eae2e508Skrishnae *
686eae2e508Skrishnae * Note: parent_data for parent is created only if this is PCI-E
687eae2e508Skrishnae * platform, for which, SG take a different route to handle device
688eae2e508Skrishnae * errors.
689eae2e508Skrishnae */
690eae2e508Skrishnae if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
691c0da6274SZhi-Jun Robin Fu if (pcie_init_cfghdl(child) != DDI_SUCCESS)
692eae2e508Skrishnae return (DDI_FAILURE);
693fc256490SJason Beloro pcie_init_dom(child);
694eae2e508Skrishnae }
695eae2e508Skrishnae
696ae115bc7Smrj /* transfer select properties from PROM to kernel */
6972df1fe9cSrandyf if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
6982df1fe9cSrandyf "interrupts", -1) != -1) {
699ae115bc7Smrj pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
700ae115bc7Smrj sizeof (struct intrspec)), KM_SLEEP);
701ae115bc7Smrj pdptr->par_intr = (struct intrspec *)(pdptr + 1);
702ae115bc7Smrj pdptr->par_nintr = 1;
703ae115bc7Smrj ddi_set_parent_data(child, pdptr);
704ae115bc7Smrj } else
705ae115bc7Smrj ddi_set_parent_data(child, NULL);
706ae115bc7Smrj
707fc256490SJason Beloro if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
708fc256490SJason Beloro pcie_fini_dom(child);
709ae115bc7Smrj return (DDI_FAILURE);
710fc256490SJason Beloro }
711ae115bc7Smrj
712ae115bc7Smrj /*
713ae115bc7Smrj * Support for the "command-preserve" property.
714ae115bc7Smrj */
715ae115bc7Smrj command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
716ae115bc7Smrj DDI_PROP_DONTPASS, "command-preserve", 0);
717ae115bc7Smrj command = pci_config_get16(config_handle, PCI_CONF_COMM);
718ae115bc7Smrj command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
719ae115bc7Smrj command |= (ppb_command_default & ~command_preserve);
720ae115bc7Smrj pci_config_put16(config_handle, PCI_CONF_COMM, command);
721ae115bc7Smrj
722ae115bc7Smrj pci_config_teardown(&config_handle);
723ae115bc7Smrj return (DDI_SUCCESS);
724ae115bc7Smrj }
725ae115bc7Smrj
726ae115bc7Smrj static void
ppb_removechild(dev_info_t * dip)727ae115bc7Smrj ppb_removechild(dev_info_t *dip)
728ae115bc7Smrj {
729ae115bc7Smrj struct ddi_parent_private_data *pdptr;
730eae2e508Skrishnae ppb_devstate_t *ppb;
731ae115bc7Smrj
732eae2e508Skrishnae ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
733eae2e508Skrishnae ddi_get_instance(ddi_get_parent(dip)));
734eae2e508Skrishnae
735fc256490SJason Beloro if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
736fc256490SJason Beloro pcie_fini_dom(dip);
737c0da6274SZhi-Jun Robin Fu pcie_fini_cfghdl(dip);
738fc256490SJason Beloro } else if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
739ae115bc7Smrj kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
740ae115bc7Smrj ddi_set_parent_data(dip, NULL);
741ae115bc7Smrj }
742ae115bc7Smrj ddi_set_name_addr(dip, NULL);
743ae115bc7Smrj
744ae115bc7Smrj /*
745ae115bc7Smrj * Strip the node to properly convert it back to prototype form
746ae115bc7Smrj */
747ae115bc7Smrj ddi_remove_minor_node(dip, NULL);
748ae115bc7Smrj
749ae115bc7Smrj impl_rem_dev_props(dip);
750ae115bc7Smrj }
751ae115bc7Smrj
752ae115bc7Smrj /*
753ae115bc7Smrj * ppb_save_config_regs
754ae115bc7Smrj *
755ae115bc7Smrj * This routine saves the state of the configuration registers of all
756ae115bc7Smrj * the child nodes of each PBM.
757ae115bc7Smrj *
758ae115bc7Smrj * used by: ppb_detach() on suspends
759ae115bc7Smrj *
760ae115bc7Smrj * return value: none
761ae115bc7Smrj */
762ae115bc7Smrj static void
ppb_save_config_regs(ppb_devstate_t * ppb_p)763ae115bc7Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p)
764ae115bc7Smrj {
765ae115bc7Smrj int i;
766ae115bc7Smrj dev_info_t *dip;
767ae115bc7Smrj ddi_acc_handle_t config_handle;
768ae115bc7Smrj
769ae115bc7Smrj for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL;
770ae115bc7Smrj i++, dip = ddi_get_next_sibling(dip)) {
771ae115bc7Smrj
772ae115bc7Smrj if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
773ae115bc7Smrj cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
774ae115bc7Smrj ddi_driver_name(ppb_p->dip),
775ae115bc7Smrj ddi_get_instance(ppb_p->dip),
776ae115bc7Smrj ddi_driver_name(dip),
777ae115bc7Smrj ddi_get_instance(dip));
778ae115bc7Smrj continue;
779ae115bc7Smrj }
780ae115bc7Smrj
781ae115bc7Smrj ppb_p->config_state[i].dip = dip;
782ae115bc7Smrj ppb_p->config_state[i].command =
783ae115bc7Smrj pci_config_get16(config_handle, PCI_CONF_COMM);
784ae115bc7Smrj pci_config_teardown(&config_handle);
785ae115bc7Smrj }
786ae115bc7Smrj ppb_p->config_state_index = i;
787ae115bc7Smrj }
788ae115bc7Smrj
789ae115bc7Smrj
790ae115bc7Smrj /*
791ae115bc7Smrj * ppb_restore_config_regs
792ae115bc7Smrj *
793ae115bc7Smrj * This routine restores the state of the configuration registers of all
794ae115bc7Smrj * the child nodes of each PBM.
795ae115bc7Smrj *
796ae115bc7Smrj * used by: ppb_attach() on resume
797ae115bc7Smrj *
798ae115bc7Smrj * return value: none
799ae115bc7Smrj */
800ae115bc7Smrj static void
ppb_restore_config_regs(ppb_devstate_t * ppb_p)801ae115bc7Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p)
802ae115bc7Smrj {
803ae115bc7Smrj int i;
804ae115bc7Smrj dev_info_t *dip;
805ae115bc7Smrj ddi_acc_handle_t config_handle;
806ae115bc7Smrj
807ae115bc7Smrj for (i = 0; i < ppb_p->config_state_index; i++) {
808ae115bc7Smrj dip = ppb_p->config_state[i].dip;
809ae115bc7Smrj if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
810ae115bc7Smrj cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
811ae115bc7Smrj ddi_driver_name(ppb_p->dip),
812ae115bc7Smrj ddi_get_instance(ppb_p->dip),
813ae115bc7Smrj ddi_driver_name(dip),
814ae115bc7Smrj ddi_get_instance(dip));
815ae115bc7Smrj continue;
816ae115bc7Smrj }
817ae115bc7Smrj pci_config_put16(config_handle, PCI_CONF_COMM,
818ae115bc7Smrj ppb_p->config_state[i].command);
819ae115bc7Smrj pci_config_teardown(&config_handle);
820ae115bc7Smrj }
821ae115bc7Smrj }
822ae115bc7Smrj
823ae115bc7Smrj
824ae115bc7Smrj static boolean_t
ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)825ae115bc7Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)
826ae115bc7Smrj {
827cb7ea99dSJimmy Vetayases uint16_t ptr;
828ae115bc7Smrj
829cb7ea99dSJimmy Vetayases if (pci_htcap_locate(cfg_hdl,
830cb7ea99dSJimmy Vetayases PCI_HTCAP_TYPE_MASK | PCI_HTCAP_MSIMAP_ENABLE_MASK,
831cb7ea99dSJimmy Vetayases PCI_HTCAP_MSIMAP_TYPE | PCI_HTCAP_MSIMAP_ENABLE, &ptr) !=
832cb7ea99dSJimmy Vetayases DDI_SUCCESS)
833ae115bc7Smrj return (B_FALSE);
834ae115bc7Smrj
835ae115bc7Smrj return (B_TRUE);
836ae115bc7Smrj }
837ae115bc7Smrj
838ae115bc7Smrj
839ae115bc7Smrj static int
ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl,int cmd)840ae115bc7Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd)
841ae115bc7Smrj {
842cb7ea99dSJimmy Vetayases uint16_t ptr;
843ae115bc7Smrj uint16_t reg;
844ae115bc7Smrj
845cb7ea99dSJimmy Vetayases if (pci_htcap_locate(cfg_hdl, PCI_HTCAP_TYPE_MASK,
846cb7ea99dSJimmy Vetayases PCI_HTCAP_MSIMAP_TYPE, &ptr) != DDI_SUCCESS)
847ae115bc7Smrj return (0);
848ae115bc7Smrj
849ae115bc7Smrj reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
850ae115bc7Smrj switch (cmd) {
851ae115bc7Smrj case HT_MSIMAP_ENABLE:
852cb7ea99dSJimmy Vetayases reg |= PCI_HTCAP_MSIMAP_ENABLE;
853ae115bc7Smrj break;
854ae115bc7Smrj case HT_MSIMAP_DISABLE:
855ae115bc7Smrj default:
856cb7ea99dSJimmy Vetayases reg &= ~(uint16_t)PCI_HTCAP_MSIMAP_ENABLE;
857ae115bc7Smrj }
858ae115bc7Smrj
859ae115bc7Smrj pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
860ae115bc7Smrj return (1);
861ae115bc7Smrj }
862ae115bc7Smrj
863ae115bc7Smrj
864ae115bc7Smrj /*
865ae115bc7Smrj * intercept certain interrupt services to handle special cases
866ae115bc7Smrj */
867ae115bc7Smrj static int
ppb_intr_ops(dev_info_t * pdip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)868ae115bc7Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
869ae115bc7Smrj ddi_intr_handle_impl_t *hdlp, void *result)
870ae115bc7Smrj {
871ae115bc7Smrj ddi_acc_handle_t cfg_hdl;
872ae115bc7Smrj int rv = DDI_SUCCESS;
873ae115bc7Smrj
874ae115bc7Smrj if (intr_op != DDI_INTROP_SUPPORTED_TYPES)
875ae115bc7Smrj return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
876ae115bc7Smrj
877ae115bc7Smrj DDI_INTR_NEXDBG((CE_CONT,
878ae115bc7Smrj "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
879ae115bc7Smrj (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
880ae115bc7Smrj
881ae115bc7Smrj /* Fixed interrupt is supported by default */
882ae115bc7Smrj *(int *)result = DDI_INTR_TYPE_FIXED;
883ae115bc7Smrj
884ae115bc7Smrj if (ppb_support_msi == -1) {
885ae115bc7Smrj DDI_INTR_NEXDBG((CE_CONT,
886ae115bc7Smrj "ppb_intr_ops: MSI is not allowed\n"));
887ae115bc7Smrj goto OUT;
888ae115bc7Smrj }
889ae115bc7Smrj
890ae115bc7Smrj if (ppb_support_msi == 1) {
891ae115bc7Smrj DDI_INTR_NEXDBG((CE_CONT,
892ae115bc7Smrj "ppb_intr_ops: MSI is always allowed\n"));
893ae115bc7Smrj rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
894ae115bc7Smrj goto OUT;
895ae115bc7Smrj }
896ae115bc7Smrj
897ae115bc7Smrj if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) {
898ae115bc7Smrj DDI_INTR_NEXDBG((CE_CONT,
899ae115bc7Smrj "ppb_intr_ops: pci_config_setup() failed\n"));
900ae115bc7Smrj goto OUT;
901ae115bc7Smrj }
902ae115bc7Smrj
903ae115bc7Smrj /*
904ae115bc7Smrj * check for hypertransport msi mapping capability
905ae115bc7Smrj */
906ae115bc7Smrj if (ppb_ht_msimap_check(cfg_hdl)) {
907ae115bc7Smrj DDI_INTR_NEXDBG((CE_CONT,
908ae115bc7Smrj "ppb_intr_ops: HT MSI mapping enabled\n"));
909ae115bc7Smrj rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
910ae115bc7Smrj }
911ae115bc7Smrj
912ae115bc7Smrj /*
913ae115bc7Smrj * if we add failure conditions after pci_config_setup, move this to
914ae115bc7Smrj * OUT and use an extra flag to indicate the need to teardown cfg_hdl
915ae115bc7Smrj */
916ae115bc7Smrj pci_config_teardown(&cfg_hdl);
917ae115bc7Smrj
918ae115bc7Smrj OUT:
919ae115bc7Smrj DDI_INTR_NEXDBG((CE_CONT,
920ae115bc7Smrj "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n",
921ae115bc7Smrj (void *)rdip, *(int *)result));
922ae115bc7Smrj return (rv);
923ae115bc7Smrj }
924ae115bc7Smrj
92526947304SEvan Yan /* ARGSUSED */
926ae115bc7Smrj static int
ppb_open(dev_t * devp,int flags,int otyp,cred_t * credp)927ae115bc7Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
928ae115bc7Smrj {
92926947304SEvan Yan int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp));
93026947304SEvan Yan ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
93126947304SEvan Yan int rv;
93226947304SEvan Yan
93326947304SEvan Yan if (ppb_p == NULL)
93426947304SEvan Yan return (ENXIO);
93526947304SEvan Yan
93626947304SEvan Yan /*
93726947304SEvan Yan * Ioctls will be handled by PCI Express framework for all
93826947304SEvan Yan * PCIe platforms
93926947304SEvan Yan */
94026947304SEvan Yan if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
94126947304SEvan Yan mutex_enter(&ppb_p->ppb_mutex);
94226947304SEvan Yan rv = pcie_open(ppb_p->dip, devp, flags, otyp, credp);
94326947304SEvan Yan mutex_exit(&ppb_p->ppb_mutex);
94426947304SEvan Yan return (rv);
94526947304SEvan Yan }
94626947304SEvan Yan
947ae115bc7Smrj return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
948ae115bc7Smrj }
949ae115bc7Smrj
95026947304SEvan Yan /* ARGSUSED */
951ae115bc7Smrj static int
ppb_close(dev_t dev,int flags,int otyp,cred_t * credp)952ae115bc7Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
953ae115bc7Smrj {
95426947304SEvan Yan int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
95526947304SEvan Yan ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
95626947304SEvan Yan int rv;
95726947304SEvan Yan
95826947304SEvan Yan if (ppb_p == NULL)
95926947304SEvan Yan return (ENXIO);
96026947304SEvan Yan
96126947304SEvan Yan mutex_enter(&ppb_p->ppb_mutex);
96226947304SEvan Yan /*
96326947304SEvan Yan * Ioctls will be handled by PCI Express framework for all
96426947304SEvan Yan * PCIe platforms
96526947304SEvan Yan */
96626947304SEvan Yan if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
96726947304SEvan Yan rv = pcie_close(ppb_p->dip, dev, flags, otyp, credp);
96826947304SEvan Yan mutex_exit(&ppb_p->ppb_mutex);
96926947304SEvan Yan return (rv);
97026947304SEvan Yan }
97126947304SEvan Yan
97226947304SEvan Yan mutex_exit(&ppb_p->ppb_mutex);
973ae115bc7Smrj return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
974ae115bc7Smrj }
975ae115bc7Smrj
97626947304SEvan Yan /*
97726947304SEvan Yan * ppb_ioctl: devctl hotplug controls
97826947304SEvan Yan */
97926947304SEvan Yan /* ARGSUSED */
980ae115bc7Smrj static int
ppb_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)98126947304SEvan Yan ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
98226947304SEvan Yan int *rvalp)
983ae115bc7Smrj {
98426947304SEvan Yan int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
98526947304SEvan Yan ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
98626947304SEvan Yan
98726947304SEvan Yan if (ppb_p == NULL)
98826947304SEvan Yan return (ENXIO);
98926947304SEvan Yan
99026947304SEvan Yan /*
99126947304SEvan Yan * Ioctls will be handled by PCI Express framework for all
99226947304SEvan Yan * PCIe platforms
99326947304SEvan Yan */
99426947304SEvan Yan if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
99526947304SEvan Yan return (pcie_ioctl(ppb_p->dip, dev, cmd, arg, mode, credp,
99626947304SEvan Yan rvalp));
99726947304SEvan Yan
998ae115bc7Smrj return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
999ae115bc7Smrj rvalp));
1000ae115bc7Smrj }
1001ae115bc7Smrj
1002ae115bc7Smrj static int
ppb_prop_op(dev_t dev,dev_info_t * dip,ddi_prop_op_t prop_op,int flags,char * name,caddr_t valuep,int * lengthp)100326947304SEvan Yan ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int flags,
100426947304SEvan Yan char *name, caddr_t valuep, int *lengthp)
1005ae115bc7Smrj {
100626947304SEvan Yan int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
100726947304SEvan Yan ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
100826947304SEvan Yan
100926947304SEvan Yan if (ppb_p == NULL)
101026947304SEvan Yan return (ENXIO);
101126947304SEvan Yan
101226947304SEvan Yan if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
101326947304SEvan Yan return (pcie_prop_op(dev, dip, prop_op, flags, name,
101426947304SEvan Yan valuep, lengthp));
101526947304SEvan Yan
1016ae115bc7Smrj return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
1017ae115bc7Smrj name, valuep, lengthp));
1018ae115bc7Smrj }
1019ae115bc7Smrj
1020ae115bc7Smrj static int
ppb_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)1021ae115bc7Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
1022ae115bc7Smrj {
102326947304SEvan Yan minor_t minor = getminor((dev_t)arg);
102426947304SEvan Yan int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
102526947304SEvan Yan ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
102626947304SEvan Yan
102726947304SEvan Yan if (ppb_p == NULL)
102826947304SEvan Yan return (DDI_FAILURE);
102926947304SEvan Yan
103026947304SEvan Yan if (ppb_p->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
1031ae115bc7Smrj return (pcihp_info(dip, cmd, arg, result));
103226947304SEvan Yan
103326947304SEvan Yan switch (cmd) {
103426947304SEvan Yan default:
103526947304SEvan Yan return (DDI_FAILURE);
103626947304SEvan Yan
103726947304SEvan Yan case DDI_INFO_DEVT2INSTANCE:
103826947304SEvan Yan *result = (void *)(uintptr_t)instance;
103926947304SEvan Yan return (DDI_SUCCESS);
104026947304SEvan Yan
104126947304SEvan Yan case DDI_INFO_DEVT2DEVINFO:
104226947304SEvan Yan if (ppb_p == NULL)
104326947304SEvan Yan return (DDI_FAILURE);
104426947304SEvan Yan *result = (void *)ppb_p->dip;
104526947304SEvan Yan return (DDI_SUCCESS);
104626947304SEvan Yan }
1047ae115bc7Smrj }
1048ae115bc7Smrj
ppb_peekpoke_cb(dev_info_t * dip,ddi_fm_error_t * derr)1049eae2e508Skrishnae void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) {
1050eae2e508Skrishnae (void) pci_ereport_post(dip, derr, NULL);
1051eae2e508Skrishnae }
1052eae2e508Skrishnae
1053ae115bc7Smrj /*ARGSUSED*/
1054ae115bc7Smrj static int
ppb_fm_init(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)1055ae115bc7Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
1056ae115bc7Smrj ddi_iblock_cookie_t *ibc)
1057ae115bc7Smrj {
1058ae115bc7Smrj ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
1059ae115bc7Smrj ddi_get_instance(dip));
1060ae115bc7Smrj
1061ae115bc7Smrj ASSERT(ibc != NULL);
1062ae115bc7Smrj *ibc = ppb->ppb_fm_ibc;
1063ae115bc7Smrj
1064ae115bc7Smrj return (ppb->ppb_fmcap);
1065ae115bc7Smrj }
1066ae115bc7Smrj
1067ae115bc7Smrj /*ARGSUSED*/
1068ae115bc7Smrj static int
ppb_fm_callback(dev_info_t * dip,ddi_fm_error_t * derr,const void * no_used)1069ae115bc7Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
1070ae115bc7Smrj {
1071ae115bc7Smrj ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
1072ae115bc7Smrj ddi_get_instance(dip));
1073ae115bc7Smrj
1074ae115bc7Smrj mutex_enter(&ppb->ppb_err_mutex);
1075ae115bc7Smrj pci_ereport_post(dip, derr, NULL);
1076ae115bc7Smrj mutex_exit(&ppb->ppb_err_mutex);
1077ae115bc7Smrj return (derr->fme_status);
1078ae115bc7Smrj }
1079