Added PCI DMA functions

DMA transfers can only happen within a dedicated DMA window, so we need
special functions for allocating buffers and for mapping these buffers in and
out again.

Signed-off-by: Thomas Huth <thuth@linux.vnet.ibm.com>
This commit is contained in:
Thomas Huth 2011-08-05 09:31:12 +02:00
parent 904abb4a8f
commit 4f0a051873
5 changed files with 142 additions and 1 deletions

View File

@ -81,6 +81,7 @@ OF_FFS_FILES = \
$(SLOFCMNDIR)/fs/xmodem.fs \
$(SLOFBRDDIR)/default-font.bin \
$(SLOFBRDDIR)/pci-phb.fs \
$(SLOFBRDDIR)/pci-device-dma.fs \
$(SLOFBRDDIR)/rtas.fs \
$(FCODE_FFS_FILES)

View File

@ -0,0 +1,95 @@
\ *****************************************************************************
\ * Copyright (c) 2011 IBM Corporation
\ * All rights reserved.
\ * This program and the accompanying materials
\ * are made available under the terms of the BSD License
\ * which accompanies this distribution, and is available at
\ * http://www.opensource.org/licenses/bsd-license.php
\ *
\ * Contributors:
\ * IBM Corporation - initial implementation
\ ****************************************************************************/
\ * Open Firmware 1275 DMA transfer mapping functions
\ *****************************************************************************
0 VALUE dma-debug?
0 VALUE dma-window-liobn \ Logical I/O bus number
0 VALUE dma-window-base \ Start address of window
0 VALUE dma-window-size \ Size of the window
\ Read DMA window base and size from ibm,dma-window property
: update-dma-window-vars ( -- )
s" ibm,dma-window" get-node get-property 0= IF
decode-int TO dma-window-liobn
decode-64 TO dma-window-base
decode-64 TO dma-window-size
2drop
THEN
;
update-dma-window-vars
: dma-alloc ( size -- virt )
dma-debug? IF cr ." dma-alloc called: " .s cr THEN
fff + fff not and \ Align size to next 4k boundary
alloc-mem
\ alloc-mem always returns aligned memory - double check just to be sure
dup fff and IF
." Warning: dma-alloc got unaligned memory!" cr
THEN
;
: dma-free ( virt size -- )
dma-debug? IF cr ." dma-free called: " .s cr THEN
fff + fff not and \ Align size to next 4k boundary
free-mem
;
\ We assume that firmware never maps more than the whole dma-window-size
\ so we cheat by calculating the remainder of addr/windowsize instead
\ of taking care to maintain a list of assigned device addresses
: dma-virt2dev ( virt -- devaddr )
dma-window-size mod dma-window-base +
;
: dma-map-in ( virt size cachable? -- devaddr )
dma-debug? IF cr ." dma-map-in called: " .s cr THEN
drop ( virt size )
bounds dup >r ( v+s virt R: virt )
swap fff + fff not and \ Align end to next 4k boundary
swap fff not and ( v+s' virt' R: virt )
?DO
\ ." mapping " i . cr
dma-window-liobn \ liobn
i dma-virt2dev \ ioba
i 3 OR \ Make a read- & writeable TCE
( liobn ioba tce R: virt )
hv-put-tce ABORT" H_PUT_TCE failed"
1000 +LOOP
r> dma-virt2dev
;
: dma-map-out ( virt devaddr size -- )
dma-debug? IF cr ." dma-map-out called: " .s cr THEN
nip ( virt size )
bounds ( v+s virt )
swap fff + fff not and \ Align end to next 4k boundary
swap fff not and ( v+s' virt' )
?DO
\ ." unmapping " i . cr
dma-window-liobn \ liobn
i dma-virt2dev \ ioba
i \ Lowest bits not set => invalid TCE
( liobn ioba tce )
hv-put-tce ABORT" H_PUT_TCE failed"
1000 +LOOP
;
: dma-sync ( virt devaddr size -- )
dma-debug? IF cr ." dma-sync called: " .s cr THEN
\ TODO: Call flush-cache or sync here?
3drop
;

View File

@ -75,6 +75,7 @@ setup-puid
dup to my-self
dup ihandle>phandle node>instance-size @ \ Remember instance size
\ Include the PCI device functions:
s" pci-device-dma.fs" included
s" pci-device.fs" included
\ Clean up the temporary instance. Note that we can not use close-node
\ or destroy-instance here since node>instance-size might have changed.

View File

@ -102,6 +102,9 @@ typedef int (*k_ioctl_t) (int, int, void *);
typedef void (*modules_remove_t) (int);
typedef snk_module_t *(*modules_load_t) (int);
typedef long (*dma_map_in_t)(void *address, long size, int cachable);
typedef void (*dma_map_out_t)(void *address, long devaddr, long size);
typedef struct {
int version;
print_t print;
@ -129,6 +132,8 @@ typedef struct {
k_ioctl_t k_ioctl;
modules_remove_t modules_remove;
modules_load_t modules_load;
dma_map_in_t dma_map_in;
dma_map_out_t dma_map_out;
} snk_kernel_t;
/* Entry of module */

View File

@ -220,6 +220,12 @@ of_4_1(const char *serv, int arg0, int arg1, int arg2, int arg3)
return arg.args[4];
}
int
of_test(const char *name)
{
return (int) of_1_1("test", p32cast name);
}
int
of_interpret_1(void *s, void *ret)
{
@ -373,6 +379,7 @@ bootmsg_error(short id, const char *str)
(void) of_2_0("bootmsg-error", id, p32cast str);
}
/*
void
bootmsg_debugcp(short id, const char *str, short lvl)
{
@ -384,7 +391,7 @@ bootmsg_cp(short id)
{
(void) of_1_0("bootmsg-cp", id);
}
*/
static long
of_fileio_read(snk_fileio_t *fileio, char *buf, long len)
@ -413,6 +420,34 @@ of_fileio_close(snk_fileio_t *fileio)
return 0;
}
static long
dma_map_in(void *address, long size, int cachable)
{
unsigned int ret;
/* Is dma-map-in available? */
if (of_test("dma-map-in") != 0) {
/* No dma-map-in available ==> Assume we can use 1:1 addresses */
return (long)address;
}
ret = of_3_1("dma-map-in", p32cast address, (int)size, cachable);
return ret;
}
static void
dma_map_out(void *address, long devaddr, long size)
{
/* Is dma-map-out available? */
if (of_test("dma-map-out") != 0) {
/* No dma-map-out available */
return;
}
of_3_0("dma-map-out", p32cast address, (int)devaddr, (int)size);
}
#define CONFIG_SPACE 0
#define IO_SPACE 1
@ -791,6 +826,7 @@ get_timebase(unsigned int *timebase)
of_getprop(cpu, "timebase-frequency", timebase, 4);
}
int
glue_init(snk_kernel_t * snk_kernel_interface, unsigned int * timebase,
size_t _client_start, size_t _client_size)
@ -834,6 +870,9 @@ glue_init(snk_kernel_t * snk_kernel_interface, unsigned int * timebase,
snk_kernel_interface->translate_addr = translate_address;
snk_kernel_interface->pci_config_read = rtas_pci_config_read;
snk_kernel_interface->pci_config_write = rtas_pci_config_write;
snk_kernel_interface->dma_map_in = dma_map_in;
snk_kernel_interface->dma_map_out = dma_map_out;
claim_rc=(int)(long)of_claim(client_start, client_size, 0);
return 0;