<!-- @page { margin: 0.79in } P { margin-bottom: 0.08in } -->
This time we will look into pci_pci driver module. It's PCI to PCI bus bridge nexus driver. As usual, we can find the source files which implement this module. I tried to type Chinese in OpenOffice, but the font looks so ugly.
[allen@blu-xvm-osol:uts]find . -name Makefile\* | xargs grep PCI_PCINEXUS_OBJS
./intel/Makefile.files:PCI_PCINEXUS_OBJS += pci_pci.o
./intel/pci_pci/Makefile:OBJECTS = $(PCI_PCINEXUS_OBJS:%=$(OBJS_DIR)/%)
./intel/pci_pci/Makefile:LINTS = $(PCI_PCINEXUS_OBJS:%.o=$(LINTS_DIR)/%.ln)
[allen@blu-xvm-osol:uts]find . -name pci_pci.c
./sun4u/io/pci/pci_pci.c
./intel/io/pci/pci_pci.c
As usual, if no further notification, all source code is from intel/io/pci/pci_pci.c. Module linkage information:
175 /*
176 * Module linkage information for the kernel.
177 */
178
179 static struct modldrv modldrv = {
180 |_______&mod_driverops, /* Type of module */
181 |_______"Standard PCI to PCI bridge nexus driver",
182 |_______&ppb_ops,|______/* driver ops */
183 };
184
185 static struct modlinkage modlinkage = {
186 |_______MODREV_1,
187 |_______(void *)&modldrv,
188 |_______NULL
189 };
Soft state structure is created in _init() and will be allocated attach(). The state structure is defined as below.
191 /*
192 * soft state pointer and structure template:
193 */
194 static void *ppb_state;
195
196 typedef struct {
197 |_______dev_info_t *dip;
198 |_______int ppb_fmcap;
199 |_______ddi_iblock_cookie_t ppb_fm_ibc;
200 |_______kmutex_t ppb_mutex;
201 |_______kmutex_t ppb_peek_poke_mutex;
202 |_______kmutex_t ppb_err_mutex;
203
204 |_______/*
205 |_______ * cpr support:
206 |_______ */
207 |_______uint_t config_state_index;
208 |_______struct {
209 |_______|_______dev_info_t *dip;
210 |_______|_______ushort_t command;
211 |_______|_______uchar_t cache_line_size;
212 |_______|_______uchar_t latency_timer;
213 |_______|_______uchar_t header_type;
214 |_______|_______uchar_t sec_latency_timer;
215 |_______|_______ushort_t bridge_control;
216 |_______} config_state[PCI_MAX_CHILDREN];
217
218 |_______uint16_t parent_bus;
219 } ppb_devstate_t;
attach() interface first and ignore the DDI_RESUME command.
-
Set property of "device_type" to "pci".
-
Allocate and get soft state structure.
-
FM setup, don't enable ereports if immediate child of npe.
-
Initialize the mutex locks in soft state structure.
-
Check all the ancestor of the bridge, if there is a PCIe device, set parent_bus of soft state to PCIE_PCIECAP_DEV_TYPE_PCIE_DEV; otherwise, it is set to PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO.
-
Initialize hotplug support on this bus. If itś a PCI-E Endpont Device, pcie_init() is called; otherwise, pcihp_init() is called.
-
Report the bridge device.
Question: What is the differnce between “PCI-E to PCI bus bridge” and “PCI to PCI bus bridge”? Why bus_parent set to PCIE_PCIECAP_DEV_TYPE_PCIE_DEV when the ancestor of a PCI_PCI bridge is PCI-E device?
Answer: with bus_parent set to PCIE_PCIECAP_DEV_TYPE_PCIE_DEV, the platform is a PCI-E platform; otherwise, it is a lagecy PCI platform.
Picture 1 Tylersburg IOH block diagram
Dirver Operations:
-
ppb_open()
-
ppb_close()
-
ppb_ioctl()
-
ppb_prop_op()
Call pcie framework interface if it is a PCIe platform; otherwise, call interfaces in misc/pcihp. All hotplug, power management, fault management interfaces of a PCI-e device are exported via misc/pcie, which is called PCI Express framework. For a legacy PCI platform, hotplug is supported by misc/pcihp.
972 |_______/*
973 |_______ * Ioctls will be handled by PCI Express framework for all
974 |_______ * PCIe platforms
975 |_______ */
976 |_______if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
977 |_______|_______return (pcie_ioctl(ppb_p->dip, dev, cmd, arg, mode, credp,
978 |_______|_______ rvalp));
979
980 |_______return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
981 |_______ rvalp));
Bus Operation:
-
ppb_bus_map()
Escalate this request to parent.
-
ppb_ctlops()
ppb_initchild()/ppb_removechild() are used to initialize a child node under this pci-pci bridge. It first merges the pseudo .conf node property. This kind of pseudo nodes are created in init_node() when DDI implementation tries to put the devinfo node at DS_INITIALIZED state. Initialize the PCI-e private data if this bridge is on a PCIE platform. PCIe Bus Private Data contains commonly used PCI/PCIe information and offsets to key registers. Create parent private data and the interrupt vectore data if the node contains “interrupt” property. The last step of initchild() is the support for the "command-preserve" property. ppb_removechild() will be called in init_node() if initchild() returns error.
Device suspend and resume support is implemented via DDI_CTLOPS_ATTACH/DDI_CTLOPS_DETACH bus ctl operations.
Device configure space peek/poke is supported via DDI_CTLOPS_PEEK/DDI_CTLOPS_POKE bus ctl operations.
-
ppb_fm_init()
-
ppb_intr_ops()
This function is defined to intercept certain interrupt services to handle special cases, such as check for hypertransport msi mapping capability.