patch-2.3.99-pre4 linux/arch/sparc64/kernel/pci_common.c
Next file: linux/arch/sparc64/kernel/pci_impl.h
Previous file: linux/arch/sparc64/kernel/itlb_base.S
Back to the patch index
Back to the overall index
- Lines: 172
- Date:
Mon Mar 27 10:35:56 2000
- Orig file:
v2.3.99-pre3/linux/arch/sparc64/kernel/pci_common.c
- Orig date:
Fri Jan 7 19:13:21 2000
diff -u --recursive --new-file v2.3.99-pre3/linux/arch/sparc64/kernel/pci_common.c linux/arch/sparc64/kernel/pci_common.c
@@ -1,4 +1,4 @@
-/* $Id: pci_common.c,v 1.6 2000/01/06 23:51:49 davem Exp $
+/* $Id: pci_common.c,v 1.7 2000/03/25 05:18:11 davem Exp $
* pci_common.c: PCI controller common support.
*
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -563,6 +563,165 @@
walk = &pbus->children;
for (walk = walk->next; walk != &pbus->children; walk = walk->next)
pci_fixup_irq(pbm, pci_bus_b(walk));
+}
+
+#undef DEBUG_BUSMASTERING
+
+static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz)
+{
+ u16 cmd;
+ u8 hdr_type, min_gnt, ltimer;
+
+#ifdef DEBUG_BUSMASTERING
+ printk("PCI: Checking DEV(%s), ", pdev->name);
+#endif
+
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ cmd |= PCI_COMMAND_MASTER;
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+ /* Read it back, if the mastering bit did not
+ * get set, the device does not support bus
+ * mastering so we have nothing to do here.
+ */
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if ((cmd & PCI_COMMAND_MASTER) == 0) {
+#ifdef DEBUG_BUSMASTERING
+ printk("no bus mastering...\n");
+#endif
+ return;
+ }
+
+ /* Set correct cache line size, 64-byte on all
+ * Sparc64 PCI systems. Note that the value is
+ * measured in 32-bit words.
+ */
+#ifdef DEBUG_BUSMASTERING
+ printk("set cachelinesize, ");
+#endif
+ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
+ 64 / sizeof(u32));
+
+ pci_read_config_byte(pdev, PCI_HEADER_TYPE, &hdr_type);
+ hdr_type &= ~0x80;
+ if (hdr_type != PCI_HEADER_TYPE_NORMAL) {
+#ifdef DEBUG_BUSMASTERING
+ printk("hdr_type=%x, exit\n", hdr_type);
+#endif
+ return;
+ }
+
+ /* If the latency timer is already programmed with a non-zero
+ * value, assume whoever set it (OBP or whoever) knows what
+ * they are doing.
+ */
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, <imer);
+ if (ltimer != 0) {
+#ifdef DEBUG_BUSMASTERING
+ printk("ltimer was %x, exit\n", ltimer);
+#endif
+ return;
+ }
+
+ /* XXX Since I'm tipping off the min grant value to
+ * XXX choose a suitable latency timer value, I also
+ * XXX considered making use of the max latency value
+ * XXX as well. Unfortunately I've seen too many bogusly
+ * XXX low settings for it to the point where it lacks
+ * XXX any usefulness. In one case, an ethernet card
+ * XXX claimed a min grant of 10 and a max latency of 5.
+ * XXX Now, if I had two such cards on the same bus I
+ * XXX could not set the desired burst period (calculated
+ * XXX from min grant) without violating the max latency
+ * XXX bound. Duh...
+ * XXX
+ * XXX I blame dumb PC bios implementors for stuff like
+ * XXX this, most of them don't even try to do something
+ * XXX sensible with latency timer values and just set some
+ * XXX default value (usually 32) into every device.
+ */
+
+ pci_read_config_byte(pdev, PCI_MIN_GNT, &min_gnt);
+
+ if (min_gnt == 0) {
+ /* If no min_gnt setting then use a default
+ * value.
+ */
+ if (is_66mhz)
+ ltimer = 16;
+ else
+ ltimer = 32;
+ } else {
+ int shift_factor;
+
+ if (is_66mhz)
+ shift_factor = 2;
+ else
+ shift_factor = 3;
+
+ /* Use a default value when the min_gnt value
+ * is erroneously high.
+ */
+ if (((unsigned int) min_gnt << shift_factor) > 512 ||
+ ((min_gnt << shift_factor) & 0xff) == 0) {
+ ltimer = 8 << shift_factor;
+ } else {
+ ltimer = min_gnt << shift_factor;
+ }
+ }
+
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, ltimer);
+#ifdef DEBUG_BUSMASTERING
+ printk("set ltimer to %x\n", ltimer);
+#endif
+}
+
+void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct list_head *walk;
+ int all_are_66mhz;
+ u16 status;
+
+ if (pbm->is_66mhz_capable == 0) {
+ all_are_66mhz = 0;
+ goto out;
+ }
+
+ walk = &pbus->devices;
+ all_are_66mhz = 1;
+ for (walk = walk->next; walk != &pbus->devices; walk = walk->next) {
+ struct pci_dev *pdev = pci_dev_b(walk);
+
+ pci_read_config_word(pdev, PCI_STATUS, &status);
+ if (!(status & PCI_STATUS_66MHZ)) {
+ all_are_66mhz = 0;
+ break;
+ }
+ }
+out:
+ pbm->all_devs_66mhz = all_are_66mhz;
+
+ printk("PCI%d(PBM%c): Bus running at %dMHz\n",
+ pbm->parent->index,
+ (pbm == &pbm->parent->pbm_A) ? 'A' : 'B',
+ (all_are_66mhz ? 66 : 33));
+}
+
+void pci_setup_busmastering(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct list_head *walk = &pbus->devices;
+ int is_66mhz;
+
+ is_66mhz = pbm->is_66mhz_capable && pbm->all_devs_66mhz;
+
+ for (walk = walk->next; walk != &pbus->devices; walk = walk->next)
+ pdev_setup_busmastering(pci_dev_b(walk), is_66mhz);
+
+ walk = &pbus->children;
+ for (walk = walk->next; walk != &pbus->children; walk = walk->next)
+ pci_setup_busmastering(pbm, pci_bus_b(walk));
}
/* Generic helper routines for PCI error reporting. */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)