Add PCI resource allocation section for (BARs, IRQs, DMA)

This commit is contained in:
Murray Stokely 2001-04-09 08:26:38 +00:00
parent c19dfc27c0
commit 53f5f0487c
Notes: svn2git 2020-12-08 03:00:23 +00:00
svn path=/head/; revision=9152
3 changed files with 525 additions and 57 deletions
en_US.ISO8859-1/books
arch-handbook/pci
developers-handbook/pci
en_US.ISO_8859-1/books/developers-handbook/pci

View file

@ -1,20 +1,22 @@
<!--
The FreeBSD Documentation Project
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.1 2000/11/28 19:07:46 asmodai Exp $
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.2 2001/04/09 00:33:42 dd Exp $
-->
<chapter id="pci">
<title>PCI Devices</title>
<chapter id="pci">
<title>PCI Devices</title>
<para>This chapter will talk about the FreeBSD mechanisms for
writing a device driver for a device on a PCI bus.</para>
<para>This chapter will talk about the FreeBSD mechanisms for
writing a device driver for a device on a PCI bus.</para>
<sect1><title>Probe and Attach</title>
<sect1>
<title>Probe and Attach</title>
<para>Information here about how the PCI bus code iterates through
the unattached devices and see if a newly loaded kld will attach
to any of them.</para>
<para>Information here about how the PCI bus code iterates
through the unattached devices and see if a newly loaded kld
will attach to any of them.</para>
<programlisting>/*
* Simple KLD to play with the PCI functions.
*
@ -201,16 +203,170 @@ static devclass_t mypci_devclass;
DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);</programlisting>
<para>Additional Resources
<itemizedlist>
<listitem><simpara><ulink
url="http://www.pcisig.org">PCI Special Interest
Group</ulink></simpara></listitem>
<listitem><simpara>PCI System Architecture, Fourth Edition by
<para>Additional Resources
<itemizedlist>
<listitem><simpara><ulink url="http://www.pcisig.org">PCI
Special Interest Group</ulink></simpara></listitem>
<listitem><simpara>PCI System Architecture, Fourth Edition by
Tom Shanley, et al.</simpara></listitem>
</itemizedlist>
</para>
</sect1>
</chapter>
</itemizedlist>
</para>
</sect1>
<sect1>
<title>Bus Resources</title>
<para>FreeBSD provides an object-oriented mechanism for requesting
resources from a parent bus. Almost all devices will be a child
member of some sort of bus (PCI, ISA, USB, SCSI, etc) and these
devices need to acquire resources from their parent bus (such as
memory segments, interrupt lines, or DMA channels).</para>
<sect2>
<title>Base Address Registers</title>
<para>To do anything particularly useful with a PCI device you
will need to obtain the <emphasis>Base Address
Registers</emphasis> (BARs) from the PCI Configuration space.
The PCI-specific details of obtaining the BAR is abstracted in
the <function>bus_alloc_resource()</function> function.</para>
<para>For example, a typical driver might have something similar
to this in the <function>attach()</function> function. : </para>
<programlisting> sc->bar0id = 0x10;
sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;(sc->bar0id),
0, ~0, 1, RF_ACTIVE);
if (sc->bar0res == NULL) {
uprintf("Memory allocation of PCI base register 0 failed!\n");
error = ENXIO;
goto fail1;
}
sc->bar1id = 0x14;
sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;(sc->bar1id),
0, ~0, 1, RF_ACTIVE);
if (sc->bar1res == NULL) {
uprintf("Memory allocation of PCI base register 1 failed!\n");
error = ENXIO;
goto fail2;
}
sc->bar0_bt = rman_get_bustag(sc->bar0res);
sc->bar0_bh = rman_get_bushandle(sc->bar0res);
sc->bar1_bt = rman_get_bustag(sc->bar1res);
sc->bar1_bh = rman_get_bushandle(sc->bar1res);
</programlisting>
<para>Handles for each base address register are kept in the
<structname>softc</structname> structure so that they can be
used to write to the device later.</para>
<para>These handles can then be used to read or write from the
device registers with the <function>bus_space_*</function>
functions. For example, a driver might contain a shorthand
function to read from a board specific register like this :
</para>
<programlisting>uint16_t
board_read(struct ni_softc *sc, uint16_t address) {
return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
}
</programlisting>
<para>Similarly, one could write to the registers with : </para>
<programlisting>void
board_write(struct ni_softc *sc, uint16_t address, uint16_t value) {
bus_space_write_2(sc->bar1_bt, sc->bar1_bh, address, value);
}
</programlisting>
<para>These functions exist in 8bit, 16bit, and 32bit versions
and you should use
<function>bus_space_{read|write}_{1|2|4}</function>
accordingly.</para>
</sect2>
<sect2>
<title>Interrupts</title>
<para>Interrupts are allocated from the object-oriented bus code
in a way similar to the memory resources. First an IRQ
resource must be allocated from the parent bus, and then the
interrupt handler must be setup to deal with this IRQ.</para>
<para>Again, a sample from a device
<function>attach()</function> function says more than
words.</para>
<programlisting>/* Get the IRQ resource */
sc->irqid = 0x0;
sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &amp;(sc->irqid),
0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
if (sc->irqres == NULL) {
uprintf("IRQ allocation failed!\n");
error = ENXIO;
goto fail3;
}
/* Now we should setup the interrupt handler */
error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_MISC,
my_handler, sc, &amp;(sc->handler));
if (error) {
printf("Couldn't set up irq\n");
goto fail4;
}
sc->irq_bt = rman_get_bustag(sc->irqres);
sc->irq_bh = rman_get_bushandle(sc->irqres);
</programlisting>
</sect2>
<sect2>
<title>DMA</title>
<para>On the PC, peripherals that want to do bus-mastering DMA
must deal with physical addresses. This is a problem since
FreeBSD uses virtual memory and deals almost exclusively with
virtual addresses. Fortunately, there is a function,
<function>vtophys()</function> to help.</para>
<programlisting>#include &lt;vm/vm.h&gt;
#include &lt;vm/pmap.h&gt;
#define vtophys(virtual_address) (...)
</programlisting>
<para>The solution is a bit different on the alpha however, and
what we really want is a function called
<function>vtobus()</function>.</para>
<programlisting>#if defined(__alpha__)
#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
#else
#define vtobus(va) vtophys(va)
#endif
</programlisting>
</sect2>
<sect2>
<title>Deallocating Resources</title>
<para>It's very important to deallocate all of the resources
that were allocated during <function>attach()</function>.
Care must be taken to deallocate the correct stuff even on a
failure condition so that the system will remain useable while
your driver dies.</para>
</sect2>
</sect1>
</chapter>

View file

@ -1,20 +1,22 @@
<!--
The FreeBSD Documentation Project
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.1 2000/11/28 19:07:46 asmodai Exp $
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.2 2001/04/09 00:33:42 dd Exp $
-->
<chapter id="pci">
<title>PCI Devices</title>
<chapter id="pci">
<title>PCI Devices</title>
<para>This chapter will talk about the FreeBSD mechanisms for
writing a device driver for a device on a PCI bus.</para>
<para>This chapter will talk about the FreeBSD mechanisms for
writing a device driver for a device on a PCI bus.</para>
<sect1><title>Probe and Attach</title>
<sect1>
<title>Probe and Attach</title>
<para>Information here about how the PCI bus code iterates through
the unattached devices and see if a newly loaded kld will attach
to any of them.</para>
<para>Information here about how the PCI bus code iterates
through the unattached devices and see if a newly loaded kld
will attach to any of them.</para>
<programlisting>/*
* Simple KLD to play with the PCI functions.
*
@ -201,16 +203,170 @@ static devclass_t mypci_devclass;
DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);</programlisting>
<para>Additional Resources
<itemizedlist>
<listitem><simpara><ulink
url="http://www.pcisig.org">PCI Special Interest
Group</ulink></simpara></listitem>
<listitem><simpara>PCI System Architecture, Fourth Edition by
<para>Additional Resources
<itemizedlist>
<listitem><simpara><ulink url="http://www.pcisig.org">PCI
Special Interest Group</ulink></simpara></listitem>
<listitem><simpara>PCI System Architecture, Fourth Edition by
Tom Shanley, et al.</simpara></listitem>
</itemizedlist>
</para>
</sect1>
</chapter>
</itemizedlist>
</para>
</sect1>
<sect1>
<title>Bus Resources</title>
<para>FreeBSD provides an object-oriented mechanism for requesting
resources from a parent bus. Almost all devices will be a child
member of some sort of bus (PCI, ISA, USB, SCSI, etc) and these
devices need to acquire resources from their parent bus (such as
memory segments, interrupt lines, or DMA channels).</para>
<sect2>
<title>Base Address Registers</title>
<para>To do anything particularly useful with a PCI device you
will need to obtain the <emphasis>Base Address
Registers</emphasis> (BARs) from the PCI Configuration space.
The PCI-specific details of obtaining the BAR is abstracted in
the <function>bus_alloc_resource()</function> function.</para>
<para>For example, a typical driver might have something similar
to this in the <function>attach()</function> function. : </para>
<programlisting> sc->bar0id = 0x10;
sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;(sc->bar0id),
0, ~0, 1, RF_ACTIVE);
if (sc->bar0res == NULL) {
uprintf("Memory allocation of PCI base register 0 failed!\n");
error = ENXIO;
goto fail1;
}
sc->bar1id = 0x14;
sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;(sc->bar1id),
0, ~0, 1, RF_ACTIVE);
if (sc->bar1res == NULL) {
uprintf("Memory allocation of PCI base register 1 failed!\n");
error = ENXIO;
goto fail2;
}
sc->bar0_bt = rman_get_bustag(sc->bar0res);
sc->bar0_bh = rman_get_bushandle(sc->bar0res);
sc->bar1_bt = rman_get_bustag(sc->bar1res);
sc->bar1_bh = rman_get_bushandle(sc->bar1res);
</programlisting>
<para>Handles for each base address register are kept in the
<structname>softc</structname> structure so that they can be
used to write to the device later.</para>
<para>These handles can then be used to read or write from the
device registers with the <function>bus_space_*</function>
functions. For example, a driver might contain a shorthand
function to read from a board specific register like this :
</para>
<programlisting>uint16_t
board_read(struct ni_softc *sc, uint16_t address) {
return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
}
</programlisting>
<para>Similarly, one could write to the registers with : </para>
<programlisting>void
board_write(struct ni_softc *sc, uint16_t address, uint16_t value) {
bus_space_write_2(sc->bar1_bt, sc->bar1_bh, address, value);
}
</programlisting>
<para>These functions exist in 8bit, 16bit, and 32bit versions
and you should use
<function>bus_space_{read|write}_{1|2|4}</function>
accordingly.</para>
</sect2>
<sect2>
<title>Interrupts</title>
<para>Interrupts are allocated from the object-oriented bus code
in a way similar to the memory resources. First an IRQ
resource must be allocated from the parent bus, and then the
interrupt handler must be setup to deal with this IRQ.</para>
<para>Again, a sample from a device
<function>attach()</function> function says more than
words.</para>
<programlisting>/* Get the IRQ resource */
sc->irqid = 0x0;
sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &amp;(sc->irqid),
0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
if (sc->irqres == NULL) {
uprintf("IRQ allocation failed!\n");
error = ENXIO;
goto fail3;
}
/* Now we should setup the interrupt handler */
error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_MISC,
my_handler, sc, &amp;(sc->handler));
if (error) {
printf("Couldn't set up irq\n");
goto fail4;
}
sc->irq_bt = rman_get_bustag(sc->irqres);
sc->irq_bh = rman_get_bushandle(sc->irqres);
</programlisting>
</sect2>
<sect2>
<title>DMA</title>
<para>On the PC, peripherals that want to do bus-mastering DMA
must deal with physical addresses. This is a problem since
FreeBSD uses virtual memory and deals almost exclusively with
virtual addresses. Fortunately, there is a function,
<function>vtophys()</function> to help.</para>
<programlisting>#include &lt;vm/vm.h&gt;
#include &lt;vm/pmap.h&gt;
#define vtophys(virtual_address) (...)
</programlisting>
<para>The solution is a bit different on the alpha however, and
what we really want is a function called
<function>vtobus()</function>.</para>
<programlisting>#if defined(__alpha__)
#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
#else
#define vtobus(va) vtophys(va)
#endif
</programlisting>
</sect2>
<sect2>
<title>Deallocating Resources</title>
<para>It's very important to deallocate all of the resources
that were allocated during <function>attach()</function>.
Care must be taken to deallocate the correct stuff even on a
failure condition so that the system will remain useable while
your driver dies.</para>
</sect2>
</sect1>
</chapter>

View file

@ -1,20 +1,22 @@
<!--
The FreeBSD Documentation Project
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.1 2000/11/28 19:07:46 asmodai Exp $
$FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.2 2001/04/09 00:33:42 dd Exp $
-->
<chapter id="pci">
<title>PCI Devices</title>
<chapter id="pci">
<title>PCI Devices</title>
<para>This chapter will talk about the FreeBSD mechanisms for
writing a device driver for a device on a PCI bus.</para>
<para>This chapter will talk about the FreeBSD mechanisms for
writing a device driver for a device on a PCI bus.</para>
<sect1><title>Probe and Attach</title>
<sect1>
<title>Probe and Attach</title>
<para>Information here about how the PCI bus code iterates through
the unattached devices and see if a newly loaded kld will attach
to any of them.</para>
<para>Information here about how the PCI bus code iterates
through the unattached devices and see if a newly loaded kld
will attach to any of them.</para>
<programlisting>/*
* Simple KLD to play with the PCI functions.
*
@ -201,16 +203,170 @@ static devclass_t mypci_devclass;
DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);</programlisting>
<para>Additional Resources
<itemizedlist>
<listitem><simpara><ulink
url="http://www.pcisig.org">PCI Special Interest
Group</ulink></simpara></listitem>
<listitem><simpara>PCI System Architecture, Fourth Edition by
<para>Additional Resources
<itemizedlist>
<listitem><simpara><ulink url="http://www.pcisig.org">PCI
Special Interest Group</ulink></simpara></listitem>
<listitem><simpara>PCI System Architecture, Fourth Edition by
Tom Shanley, et al.</simpara></listitem>
</itemizedlist>
</para>
</sect1>
</chapter>
</itemizedlist>
</para>
</sect1>
<sect1>
<title>Bus Resources</title>
<para>FreeBSD provides an object-oriented mechanism for requesting
resources from a parent bus. Almost all devices will be a child
member of some sort of bus (PCI, ISA, USB, SCSI, etc) and these
devices need to acquire resources from their parent bus (such as
memory segments, interrupt lines, or DMA channels).</para>
<sect2>
<title>Base Address Registers</title>
<para>To do anything particularly useful with a PCI device you
will need to obtain the <emphasis>Base Address
Registers</emphasis> (BARs) from the PCI Configuration space.
The PCI-specific details of obtaining the BAR is abstracted in
the <function>bus_alloc_resource()</function> function.</para>
<para>For example, a typical driver might have something similar
to this in the <function>attach()</function> function. : </para>
<programlisting> sc->bar0id = 0x10;
sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;(sc->bar0id),
0, ~0, 1, RF_ACTIVE);
if (sc->bar0res == NULL) {
uprintf("Memory allocation of PCI base register 0 failed!\n");
error = ENXIO;
goto fail1;
}
sc->bar1id = 0x14;
sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;(sc->bar1id),
0, ~0, 1, RF_ACTIVE);
if (sc->bar1res == NULL) {
uprintf("Memory allocation of PCI base register 1 failed!\n");
error = ENXIO;
goto fail2;
}
sc->bar0_bt = rman_get_bustag(sc->bar0res);
sc->bar0_bh = rman_get_bushandle(sc->bar0res);
sc->bar1_bt = rman_get_bustag(sc->bar1res);
sc->bar1_bh = rman_get_bushandle(sc->bar1res);
</programlisting>
<para>Handles for each base address register are kept in the
<structname>softc</structname> structure so that they can be
used to write to the device later.</para>
<para>These handles can then be used to read or write from the
device registers with the <function>bus_space_*</function>
functions. For example, a driver might contain a shorthand
function to read from a board specific register like this :
</para>
<programlisting>uint16_t
board_read(struct ni_softc *sc, uint16_t address) {
return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
}
</programlisting>
<para>Similarly, one could write to the registers with : </para>
<programlisting>void
board_write(struct ni_softc *sc, uint16_t address, uint16_t value) {
bus_space_write_2(sc->bar1_bt, sc->bar1_bh, address, value);
}
</programlisting>
<para>These functions exist in 8bit, 16bit, and 32bit versions
and you should use
<function>bus_space_{read|write}_{1|2|4}</function>
accordingly.</para>
</sect2>
<sect2>
<title>Interrupts</title>
<para>Interrupts are allocated from the object-oriented bus code
in a way similar to the memory resources. First an IRQ
resource must be allocated from the parent bus, and then the
interrupt handler must be setup to deal with this IRQ.</para>
<para>Again, a sample from a device
<function>attach()</function> function says more than
words.</para>
<programlisting>/* Get the IRQ resource */
sc->irqid = 0x0;
sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &amp;(sc->irqid),
0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
if (sc->irqres == NULL) {
uprintf("IRQ allocation failed!\n");
error = ENXIO;
goto fail3;
}
/* Now we should setup the interrupt handler */
error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_MISC,
my_handler, sc, &amp;(sc->handler));
if (error) {
printf("Couldn't set up irq\n");
goto fail4;
}
sc->irq_bt = rman_get_bustag(sc->irqres);
sc->irq_bh = rman_get_bushandle(sc->irqres);
</programlisting>
</sect2>
<sect2>
<title>DMA</title>
<para>On the PC, peripherals that want to do bus-mastering DMA
must deal with physical addresses. This is a problem since
FreeBSD uses virtual memory and deals almost exclusively with
virtual addresses. Fortunately, there is a function,
<function>vtophys()</function> to help.</para>
<programlisting>#include &lt;vm/vm.h&gt;
#include &lt;vm/pmap.h&gt;
#define vtophys(virtual_address) (...)
</programlisting>
<para>The solution is a bit different on the alpha however, and
what we really want is a function called
<function>vtobus()</function>.</para>
<programlisting>#if defined(__alpha__)
#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
#else
#define vtobus(va) vtophys(va)
#endif
</programlisting>
</sect2>
<sect2>
<title>Deallocating Resources</title>
<para>It's very important to deallocate all of the resources
that were allocated during <function>attach()</function>.
Care must be taken to deallocate the correct stuff even on a
failure condition so that the system will remain useable while
your driver dies.</para>
</sect2>
</sect1>
</chapter>