All of lore.kernel.org
 help / color / mirror / Atom feed
* How to access DCR registers in powerpc440gx? Got err when use macro def in Linux kernel
@ 2009-09-10 17:24 g r1x
  2009-09-11  4:14 ` g r1x
  0 siblings, 1 reply; 4+ messages in thread
From: g r1x @ 2009-09-10 17:24 UTC (permalink / raw)
  To: Linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 2797 bytes --]

Now, I'm writing a DMA driver on powerpc
440gx platform(2.6.26.5), as the only way to set up DMA Controller is
to access it's dcr registers with 'mfdcr' and 'mtdcr'.

I've found some dma code in Linux kernel 2.6.26.5, so I copy the code
u wrote to my driver module directory, and include them, but when I
compile my driver, gcc complains following err messages:
--------------------------------------------------------
{standard input}: Assembler messages:
{standard input}:83: Error: unsupported relocation against dmanr
---------------------------------------------------------


code I copy from kernel 2.6.26.5 (arch/ppc/syslib/ppc4xx_dma.c)
--------------------------------------------
#include "dcr.h"
/* #inlcude <asm/dcr-native.h> */
ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];

int ppc4xx_get_dma_status(void)
{
       return (mfdcr(DCRN_DMASR));
}


void ppc4xx_enable_dma(unsigned int dmanr)
{
      unsigned int control;
      ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

      .........

      / * for other xfer modes, the addresses are already set */
      control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); <----------------err
}
void ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
       if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
               printk("set_src_addr: bad channel: %d\n", dmanr);
               return;
       }

#ifdef PPC4xx_DMA_64BIT
       mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
       mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}




--------------------------------------------------

DCR access micro I copied:
----------------------------------
#define mfdcr(rn) \
      ({      \
              unsigned long rval; \
              asm volatile("mfdcr %0,%1" : "=r"(rval) : "i"(rn)); \
              rval; \
      })
#define mtdcr(rn, val) \
      asm volatile("mtdcr %0,%1" : : "i"(rn), "r"(val))
-----------------------------------------------------

Makefile I worte
-------------------------
obj-m := dmatest.o
dmatest-objs += dma.o core.o
KBUILD_CFLAGS += -m440 -mregnames -Wa, -booke
KBUILD_ASFAGS += -m440 -mregnames -Wa, -booke
---------

KBUILD_CFLAGS += -m440 -mregnames -Wa, -booke
KBUILD_ASFAGS += -m440 -mregnames -Wa, -booke
I add these two sentences when I read these
http://sourceware.org/ml/binutils/2004-09/msg00161.html.

When I change it to mfdcr(DCRN_DMACR0); then everything is fine. but
from the result I got when I insmod my module driver, dcr reg is not
changed.

It seems that it's the problem with gnu assembler, my GNU assembler ver is 2.17

The attachments are the c source file I copy from linux 2.6.26.5, and
I use these dma function to directly manipulate dcr registers, but
when I  make my driver module outside kernel source tree, it complains
the error I mentioned above.

Thanks!

[-- Attachment #2: ppc4xx_dma.c --]
[-- Type: application/octet-stream, Size: 18648 bytes --]

/*
 * IBM PPC4xx DMA engine core library
 *
 * Copyright 2000-2004 MontaVista Software Inc.
 *
 * Cleaned up and converted to new DCR access
 * Matt Porter <mporter@kernel.crashing.org>
 *
 * Original code by Armin Kuster <akuster@mvista.com>
 * and Pete Popov <ppopov@mvista.com>
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 *
 * You should have received a copy of the  GNU General Public License along
 * with this program; if not, write  to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/module.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/ppc4xx_dma.h>

ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];

int
ppc4xx_get_dma_status(void)
{
	return (mfdcr(DCRN_DMASR));
}

void
ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("set_src_addr: bad channel: %d\n", dmanr);
		return;
	}

#ifdef PPC4xx_DMA_64BIT
	mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
	mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}

void
ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("set_dst_addr: bad channel: %d\n", dmanr);
		return;
	}

#ifdef PPC4xx_DMA_64BIT
	mtdcr(DCRN_DMADAH0 + dmanr*2, (u32)(dst_addr >> 32));
#else
	mtdcr(DCRN_DMADA0 + dmanr*2, (u32)dst_addr);
#endif
}

void
ppc4xx_enable_dma(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
	unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR,
				       DMA_CS1 | DMA_TS1 | DMA_CH1_ERR,
				       DMA_CS2 | DMA_TS2 | DMA_CH2_ERR,
				       DMA_CS3 | DMA_TS3 | DMA_CH3_ERR};

	if (p_dma_ch->in_use) {
		printk("enable_dma: channel %d in use\n", dmanr);
		return;
	}

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("enable_dma: bad channel: %d\n", dmanr);
		return;
	}

	if (p_dma_ch->mode == DMA_MODE_READ) {
		/* peripheral to memory */
		ppc4xx_set_src_addr(dmanr, 0);
		ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr);
	} else if (p_dma_ch->mode == DMA_MODE_WRITE) {
		/* memory to peripheral */
		ppc4xx_set_src_addr(dmanr, p_dma_ch->addr);
		ppc4xx_set_dst_addr(dmanr, 0);
	}

	/* for other xfer modes, the addresses are already set */
	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));

	control &= ~(DMA_TM_MASK | DMA_TD);	/* clear all mode bits */
	if (p_dma_ch->mode == DMA_MODE_MM) {
		/* software initiated memory to memory */
		control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
	}

	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	/*
	 * Clear the CS, TS, RI bits for the channel from DMASR.  This
	 * has been observed to happen correctly only after the mode and
	 * ETD/DCE bits in DMACRx are set above.  Must do this before
	 * enabling the channel.
	 */

	mtdcr(DCRN_DMASR, status_bits[dmanr]);

	/*
	 * For device-paced transfers, Terminal Count Enable apparently
	 * must be on, and this must be turned on after the mode, etc.
	 * bits are cleared above (at least on Redwood-6).
	 */

	if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) ||
	    (p_dma_ch->mode == DMA_MODE_MM_DEVATSRC))
		control |= DMA_TCE_ENABLE;

	/*
	 * Now enable the channel.
	 */

	control |= (p_dma_ch->mode | DMA_CE_ENABLE);

	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	p_dma_ch->in_use = 1;
}

void
ppc4xx_disable_dma(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (!p_dma_ch->in_use) {
		printk("disable_dma: channel %d not in use\n", dmanr);
		return;
	}

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("disable_dma: bad channel: %d\n", dmanr);
		return;
	}

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control &= ~DMA_CE_ENABLE;
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	p_dma_ch->in_use = 0;
}

/*
 * Sets the dma mode for single DMA transfers only.
 * For scatter/gather transfers, the mode is passed to the
 * alloc_dma_handle() function as one of the parameters.
 *
 * The mode is simply saved and used later.  This allows
 * the driver to call set_dma_mode() and set_dma_addr() in
 * any order.
 *
 * Valid mode values are:
 *
 * DMA_MODE_READ          peripheral to memory
 * DMA_MODE_WRITE         memory to peripheral
 * DMA_MODE_MM            memory to memory
 * DMA_MODE_MM_DEVATSRC   device-paced memory to memory, device at src
 * DMA_MODE_MM_DEVATDST   device-paced memory to memory, device at dst
 */
int
ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode)
{
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("set_dma_mode: bad channel 0x%x\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	p_dma_ch->mode = mode;

	return DMA_STATUS_GOOD;
}

/*
 * Sets the DMA Count register. Note that 'count' is in bytes.
 * However, the DMA Count register counts the number of "transfers",
 * where each transfer is equal to the bus width.  Thus, count
 * MUST be a multiple of the bus width.
 */
void
ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count)
{
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

#ifdef DEBUG_4xxDMA
	{
		int error = 0;
		switch (p_dma_ch->pwidth) {
		case PW_8:
			break;
		case PW_16:
			if (count & 0x1)
				error = 1;
			break;
		case PW_32:
			if (count & 0x3)
				error = 1;
			break;
		case PW_64:
			if (count & 0x7)
				error = 1;
			break;
		default:
			printk("set_dma_count: invalid bus width: 0x%x\n",
			       p_dma_ch->pwidth);
			return;
		}
		if (error)
			printk
			    ("Warning: set_dma_count count 0x%x bus width %d\n",
			     count, p_dma_ch->pwidth);
	}
#endif

	count = count >> p_dma_ch->shift;

	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), count);
}

/*
 *   Returns the number of bytes left to be transferred.
 *   After a DMA transfer, this should return zero.
 *   Reading this while a DMA transfer is still in progress will return
 *   unpredictable results.
 */
int
ppc4xx_get_dma_residue(unsigned int dmanr)
{
	unsigned int count;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_get_dma_residue: bad channel 0x%x\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	count = mfdcr(DCRN_DMACT0 + (dmanr * 0x8));

	return (count << p_dma_ch->shift);
}

/*
 * Sets the DMA address for a memory to peripheral or peripheral
 * to memory transfer.  The address is just saved in the channel
 * structure for now and used later in enable_dma().
 */
void
ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr)
{
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr);
		return;
	}

#ifdef DEBUG_4xxDMA
	{
		int error = 0;
		switch (p_dma_ch->pwidth) {
		case PW_8:
			break;
		case PW_16:
			if ((unsigned) addr & 0x1)
				error = 1;
			break;
		case PW_32:
			if ((unsigned) addr & 0x3)
				error = 1;
			break;
		case PW_64:
			if ((unsigned) addr & 0x7)
				error = 1;
			break;
		default:
			printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n",
			       p_dma_ch->pwidth);
			return;
		}
		if (error)
			printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n",
			       addr, p_dma_ch->pwidth);
	}
#endif

	/* save dma address and program it later after we know the xfer mode */
	p_dma_ch->addr = addr;
}

/*
 * Sets both DMA addresses for a memory to memory transfer.
 * For memory to peripheral or peripheral to memory transfers
 * the function set_dma_addr() should be used instead.
 */
void
ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
		     phys_addr_t dst_dma_addr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr);
		return;
	}

#ifdef DEBUG_4xxDMA
	{
		ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
		int error = 0;
		switch (p_dma_ch->pwidth) {
			case PW_8:
				break;
			case PW_16:
				if (((unsigned) src_dma_addr & 0x1) ||
						((unsigned) dst_dma_addr & 0x1)
				   )
					error = 1;
				break;
			case PW_32:
				if (((unsigned) src_dma_addr & 0x3) ||
						((unsigned) dst_dma_addr & 0x3)
				   )
					error = 1;
				break;
			case PW_64:
				if (((unsigned) src_dma_addr & 0x7) ||
						((unsigned) dst_dma_addr & 0x7)
				   )
					error = 1;
				break;
			default:
				printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n",
						p_dma_ch->pwidth);
				return;
		}
		if (error)
			printk
				("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n",
				 src_dma_addr, dst_dma_addr, p_dma_ch->pwidth);
	}
#endif

	ppc4xx_set_src_addr(dmanr, src_dma_addr);
	ppc4xx_set_dst_addr(dmanr, dst_dma_addr);
}

/*
 * Enables the channel interrupt.
 *
 * If performing a scatter/gatter transfer, this function
 * MUST be called before calling alloc_dma_handle() and building
 * the sgl list.  Otherwise, interrupts will not be enabled, if
 * they were previously disabled.
 */
int
ppc4xx_enable_dma_interrupt(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	p_dma_ch->int_enable = 1;

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control |= DMA_CIE_ENABLE;	/* Channel Interrupt Enable */
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	return DMA_STATUS_GOOD;
}

/*
 * Disables the channel interrupt.
 *
 * If performing a scatter/gatter transfer, this function
 * MUST be called before calling alloc_dma_handle() and building
 * the sgl list.  Otherwise, interrupts will not be disabled, if
 * they were previously enabled.
 */
int
ppc4xx_disable_dma_interrupt(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	p_dma_ch->int_enable = 0;

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control &= ~DMA_CIE_ENABLE;	/* Channel Interrupt Enable */
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	return DMA_STATUS_GOOD;
}

/*
 * Configures a DMA channel, including the peripheral bus width, if a
 * peripheral is attached to the channel, the polarity of the DMAReq and
 * DMAAck signals, etc.  This information should really be setup by the boot
 * code, since most likely the configuration won't change dynamically.
 * If the kernel has to call this function, it's recommended that it's
 * called from platform specific init code.  The driver should not need to
 * call this function.
 */
int
ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init)
{
	unsigned int polarity;
	uint32_t control = 0;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	DMA_MODE_READ = (unsigned long) DMA_TD;	/* Peripheral to Memory */
	DMA_MODE_WRITE = 0;	/* Memory to Peripheral */

	if (!p_init) {
		printk("ppc4xx_init_dma_channel: NULL p_init\n");
		return DMA_STATUS_NULL_POINTER;
	}

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

#if DCRN_POL > 0
	polarity = mfdcr(DCRN_POL);
#else
	polarity = 0;
#endif

	/* Setup the control register based on the values passed to
	 * us in p_init.  Then, over-write the control register with this
	 * new value.
	 */
	control |= SET_DMA_CONTROL;

	/* clear all polarity signals and then "or" in new signal levels */
	polarity &= ~GET_DMA_POLARITY(dmanr);
	polarity |= p_init->polarity;
#if DCRN_POL > 0
	mtdcr(DCRN_POL, polarity);
#endif
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	/* save these values in our dma channel structure */
	memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t));

	/*
	 * The peripheral width values written in the control register are:
	 *   PW_8                 0
	 *   PW_16                1
	 *   PW_32                2
	 *   PW_64                3
	 *
	 *   Since the DMA count register takes the number of "transfers",
	 *   we need to divide the count sent to us in certain
	 *   functions by the appropriate number.  It so happens that our
	 *   right shift value is equal to the peripheral width value.
	 */
	p_dma_ch->shift = p_init->pwidth;

	/*
	 * Save the control word for easy access.
	 */
	p_dma_ch->control = control;

	mtdcr(DCRN_DMASR, 0xffffffff);	/* clear status register */
	return DMA_STATUS_GOOD;
}

/*
 * This function returns the channel configuration.
 */
int
ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch)
{
	unsigned int polarity;
	unsigned int control;

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_get_channel_config: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	memcpy(p_dma_ch, &dma_channels[dmanr], sizeof (ppc_dma_ch_t));

#if DCRN_POL > 0
	polarity = mfdcr(DCRN_POL);
#else
	polarity = 0;
#endif

	p_dma_ch->polarity = polarity & GET_DMA_POLARITY(dmanr);
	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));

	p_dma_ch->cp = GET_DMA_PRIORITY(control);
	p_dma_ch->pwidth = GET_DMA_PW(control);
	p_dma_ch->psc = GET_DMA_PSC(control);
	p_dma_ch->pwc = GET_DMA_PWC(control);
	p_dma_ch->phc = GET_DMA_PHC(control);
	p_dma_ch->ce = GET_DMA_CE_ENABLE(control);
	p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control);
	p_dma_ch->shift = GET_DMA_PW(control);

#ifdef CONFIG_PPC4xx_EDMA
	p_dma_ch->pf = GET_DMA_PREFETCH(control);
#else
	p_dma_ch->ch_enable = GET_DMA_CH(control);
	p_dma_ch->ece_enable = GET_DMA_ECE(control);
	p_dma_ch->tcd_disable = GET_DMA_TCD(control);
#endif
	return DMA_STATUS_GOOD;
}

/*
 * Sets the priority for the DMA channel dmanr.
 * Since this is setup by the hardware init function, this function
 * can be used to dynamically change the priority of a channel.
 *
 * Acceptable priorities:
 *
 * PRIORITY_LOW
 * PRIORITY_MID_LOW
 * PRIORITY_MID_HIGH
 * PRIORITY_HIGH
 *
 */
int
ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority)
{
	unsigned int control;

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_set_channel_priority: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	if ((priority != PRIORITY_LOW) &&
	    (priority != PRIORITY_MID_LOW) &&
	    (priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) {
		printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority);
	}

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control |= SET_DMA_PRIORITY(priority);
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	return DMA_STATUS_GOOD;
}

/*
 * Returns the width of the peripheral attached to this channel. This assumes
 * that someone who knows the hardware configuration, boot code or some other
 * init code, already set the width.
 *
 * The return value is one of:
 *   PW_8
 *   PW_16
 *   PW_32
 *   PW_64
 *
 *   The function returns 0 on error.
 */
unsigned int
ppc4xx_get_peripheral_width(unsigned int dmanr)
{
	unsigned int control;

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_get_peripheral_width: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));

	return (GET_DMA_PW(control));
}

/*
 * Clears the channel status bits
 */
int
ppc4xx_clr_dma_status(unsigned int dmanr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_clr_dma_status: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
	mtdcr(DCRN_DMASR, ((u32)DMA_CH0_ERR | (u32)DMA_CS0 | (u32)DMA_TS0) >> dmanr);
	return DMA_STATUS_GOOD;
}

#ifdef CONFIG_PPC4xx_EDMA
/*
 * Enables the burst on the channel (BTEN bit in the control/count register)
 * Note:
 * For scatter/gather dma, this function MUST be called before the
 * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
 * sgl list and used as each sgl element is added.
 */
int
ppc4xx_enable_burst(unsigned int dmanr)
{
	unsigned int ctc;
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_enable_burst: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
        ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) | DMA_CTC_BTEN;
	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
	return DMA_STATUS_GOOD;
}
/*
 * Disables the burst on the channel (BTEN bit in the control/count register)
 * Note:
 * For scatter/gather dma, this function MUST be called before the
 * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
 * sgl list and used as each sgl element is added.
 */
int
ppc4xx_disable_burst(unsigned int dmanr)
{
	unsigned int ctc;
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_disable_burst: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
	ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BTEN;
	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
	return DMA_STATUS_GOOD;
}
/*
 * Sets the burst size (number of peripheral widths) for the channel
 * (BSIZ bits in the control/count register))
 * must be one of:
 *    DMA_CTC_BSIZ_2
 *    DMA_CTC_BSIZ_4
 *    DMA_CTC_BSIZ_8
 *    DMA_CTC_BSIZ_16
 * Note:
 * For scatter/gather dma, this function MUST be called before the
 * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
 * sgl list and used as each sgl element is added.
 */
int
ppc4xx_set_burst_size(unsigned int dmanr, unsigned int bsize)
{
	unsigned int ctc;
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_set_burst_size: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
	ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BSIZ_MSK;
	ctc |= (bsize & DMA_CTC_BSIZ_MSK);
	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
	return DMA_STATUS_GOOD;
}

EXPORT_SYMBOL(ppc4xx_enable_burst);
EXPORT_SYMBOL(ppc4xx_disable_burst);
EXPORT_SYMBOL(ppc4xx_set_burst_size);
#endif /* CONFIG_PPC4xx_EDMA */

EXPORT_SYMBOL(ppc4xx_init_dma_channel);
EXPORT_SYMBOL(ppc4xx_get_channel_config);
EXPORT_SYMBOL(ppc4xx_set_channel_priority);
EXPORT_SYMBOL(ppc4xx_get_peripheral_width);
EXPORT_SYMBOL(dma_channels);
EXPORT_SYMBOL(ppc4xx_set_src_addr);
EXPORT_SYMBOL(ppc4xx_set_dst_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr2);
EXPORT_SYMBOL(ppc4xx_enable_dma);
EXPORT_SYMBOL(ppc4xx_disable_dma);
EXPORT_SYMBOL(ppc4xx_set_dma_mode);
EXPORT_SYMBOL(ppc4xx_set_dma_count);
EXPORT_SYMBOL(ppc4xx_get_dma_residue);
EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_get_dma_status);
EXPORT_SYMBOL(ppc4xx_clr_dma_status);


[-- Attachment #3: dcr-native.h --]
[-- Type: application/octet-stream, Size: 3320 bytes --]

/*
 * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
 *                    <benh@kernel.crashing.org>
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef _ASM_POWERPC_DCR_NATIVE_H
#define _ASM_POWERPC_DCR_NATIVE_H
#ifdef __KERNEL__
#ifndef __ASSEMBLY__

#include <linux/spinlock.h>

typedef struct {
	unsigned int base;
} dcr_host_t;

#define DCR_MAP_OK(host)	(1)

#define dcr_map(dev, dcr_n, dcr_c)	((dcr_host_t){ .base = (dcr_n) })
#define dcr_unmap(host, dcr_c)		do {} while (0)
#define dcr_read(host, dcr_n)		mfdcr(dcr_n + host.base)
#define dcr_write(host, dcr_n, value)	mtdcr(dcr_n + host.base, value)

/* Device Control Registers */
void __mtdcr(int reg, unsigned int val);
unsigned int __mfdcr(int reg);
#define mfdcr(rn)						\
	({unsigned int rval;					\
	if (__builtin_constant_p(rn))				\
		asm volatile("mfdcr %0," __stringify(rn)	\
		              : "=r" (rval));			\
	else							\
		rval = __mfdcr(rn);				\
	rval;})

#define mtdcr(rn, v)						\
do {								\
	if (__builtin_constant_p(rn))				\
		asm volatile("mtdcr " __stringify(rn) ",%0"	\
			      : : "r" (v)); 			\
	else							\
		__mtdcr(rn, v);					\
} while (0)

/* R/W of indirect DCRs make use of standard naming conventions for DCRs */
extern spinlock_t dcr_ind_lock;

static inline unsigned __mfdcri(int base_addr, int base_data, int reg)
{
	unsigned long flags;
	unsigned int val;

	spin_lock_irqsave(&dcr_ind_lock, flags);
	__mtdcr(base_addr, reg);
	val = __mfdcr(base_data);
	spin_unlock_irqrestore(&dcr_ind_lock, flags);
	return val;
}

static inline void __mtdcri(int base_addr, int base_data, int reg,
			    unsigned val)
{
	unsigned long flags;

	spin_lock_irqsave(&dcr_ind_lock, flags);
	__mtdcr(base_addr, reg);
	__mtdcr(base_data, val);
	spin_unlock_irqrestore(&dcr_ind_lock, flags);
}

static inline void __dcri_clrset(int base_addr, int base_data, int reg,
				 unsigned clr, unsigned set)
{
	unsigned long flags;
	unsigned int val;

	spin_lock_irqsave(&dcr_ind_lock, flags);
	__mtdcr(base_addr, reg);
	val = (__mfdcr(base_data) & ~clr) | set;
	__mtdcr(base_data, val);
	spin_unlock_irqrestore(&dcr_ind_lock, flags);
}

#define mfdcri(base, reg)	__mfdcri(DCRN_ ## base ## _CONFIG_ADDR,	\
					 DCRN_ ## base ## _CONFIG_DATA,	\
					 reg)

#define mtdcri(base, reg, data)	__mtdcri(DCRN_ ## base ## _CONFIG_ADDR,	\
					 DCRN_ ## base ## _CONFIG_DATA,	\
					 reg, data)

#define dcri_clrset(base, reg, clr, set)	__dcri_clrset(DCRN_ ## base ## _CONFIG_ADDR,	\
							      DCRN_ ## base ## _CONFIG_DATA,	\
							      reg, clr, set)

#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_DCR_NATIVE_H */

[-- Attachment #4: dcr.h --]
[-- Type: application/octet-stream, Size: 6308 bytes --]

#ifndef _PPC_BOOT_DCR_H_
#define _PPC_BOOT_DCR_H_

#define mfdcr(rn) \
	({	\
		unsigned long rval; \
		asm volatile("mfdcr %0,%1" : "=r"(rval) : "i"(rn)); \
		rval; \
	})
#define mtdcr(rn, val) \
	asm volatile("mtdcr %0,%1" : : "i"(rn), "r"(val))

/* 440GP/440GX SDRAM controller DCRs */
#define DCRN_SDRAM0_CFGADDR				0x010
#define DCRN_SDRAM0_CFGDATA				0x011

#define SDRAM0_READ(offset) ({\
	mtdcr(DCRN_SDRAM0_CFGADDR, offset); \
	mfdcr(DCRN_SDRAM0_CFGDATA); })
#define SDRAM0_WRITE(offset, data) ({\
	mtdcr(DCRN_SDRAM0_CFGADDR, offset); \
	mtdcr(DCRN_SDRAM0_CFGDATA, data); })

#define 	SDRAM0_B0CR				0x40
#define 	SDRAM0_B1CR				0x44
#define 	SDRAM0_B2CR				0x48
#define 	SDRAM0_B3CR				0x4c

static const unsigned long sdram_bxcr[] = { SDRAM0_B0CR, SDRAM0_B1CR,
					    SDRAM0_B2CR, SDRAM0_B3CR };

#define			SDRAM_CONFIG_BANK_ENABLE        0x00000001
#define			SDRAM_CONFIG_SIZE_MASK          0x000e0000
#define			SDRAM_CONFIG_BANK_SIZE(reg)	\
	(0x00400000 << ((reg & SDRAM_CONFIG_SIZE_MASK) >> 17))

/* 440GP External Bus Controller (EBC) */
#define DCRN_EBC0_CFGADDR				0x012
#define DCRN_EBC0_CFGDATA				0x013
#define   EBC_NUM_BANKS					  8
#define   EBC_B0CR					  0x00
#define   EBC_B1CR					  0x01
#define   EBC_B2CR					  0x02
#define   EBC_B3CR					  0x03
#define   EBC_B4CR					  0x04
#define   EBC_B5CR					  0x05
#define   EBC_B6CR					  0x06
#define   EBC_B7CR					  0x07
#define   EBC_BXCR(n)					  (n)
#define	    EBC_BXCR_BAS				    0xfff00000
#define	    EBC_BXCR_BS				  	    0x000e0000
#define	    EBC_BXCR_BANK_SIZE(reg) \
	(0x100000 << (((reg) & EBC_BXCR_BS) >> 17))
#define	    EBC_BXCR_BU				  	    0x00018000
#define	      EBC_BXCR_BU_OFF			  	      0x00000000
#define	      EBC_BXCR_BU_RO			  	      0x00008000
#define	      EBC_BXCR_BU_WO			  	      0x00010000
#define	      EBC_BXCR_BU_RW			  	      0x00018000
#define	    EBC_BXCR_BW				  	    0x00006000
#define   EBC_B0AP					  0x10
#define   EBC_B1AP					  0x11
#define   EBC_B2AP					  0x12
#define   EBC_B3AP					  0x13
#define   EBC_B4AP					  0x14
#define   EBC_B5AP					  0x15
#define   EBC_B6AP					  0x16
#define   EBC_B7AP					  0x17
#define   EBC_BXAP(n)					  (0x10+(n))
#define   EBC_BEAR					  0x20
#define   EBC_BESR					  0x21
#define   EBC_CFG					  0x23
#define   EBC_CID					  0x24

/* 440GP Clock, PM, chip control */
#define DCRN_CPC0_SR					0x0b0
#define DCRN_CPC0_ER					0x0b1
#define DCRN_CPC0_FR					0x0b2
#define DCRN_CPC0_SYS0					0x0e0
#define	  CPC0_SYS0_TUNE				  0xffc00000
#define	  CPC0_SYS0_FBDV_MASK				  0x003c0000
#define	  CPC0_SYS0_FWDVA_MASK				  0x00038000
#define	  CPC0_SYS0_FWDVB_MASK				  0x00007000
#define	  CPC0_SYS0_OPDV_MASK				  0x00000c00
#define	  CPC0_SYS0_EPDV_MASK				  0x00000300
/* Helper macros to compute the actual clock divider values from the
 * encodings in the CPC0 register */
#define	  CPC0_SYS0_FBDV(reg) \
		((((((reg) & CPC0_SYS0_FBDV_MASK) >> 18) - 1) & 0xf) + 1)
#define	  CPC0_SYS0_FWDVA(reg) \
		(8 - (((reg) & CPC0_SYS0_FWDVA_MASK) >> 15))
#define	  CPC0_SYS0_FWDVB(reg) \
		(8 - (((reg) & CPC0_SYS0_FWDVB_MASK) >> 12))
#define	  CPC0_SYS0_OPDV(reg) \
		((((reg) & CPC0_SYS0_OPDV_MASK) >> 10) + 1)
#define	  CPC0_SYS0_EPDV(reg) \
		((((reg) & CPC0_SYS0_EPDV_MASK) >> 8) + 1)
#define	  CPC0_SYS0_EXTSL				  0x00000080
#define	  CPC0_SYS0_RW_MASK				  0x00000060
#define	  CPC0_SYS0_RL					  0x00000010
#define	  CPC0_SYS0_ZMIISL_MASK				  0x0000000c
#define	  CPC0_SYS0_BYPASS				  0x00000002
#define	  CPC0_SYS0_NTO1				  0x00000001
#define DCRN_CPC0_SYS1					0x0e1
#define DCRN_CPC0_CUST0					0x0e2
#define DCRN_CPC0_CUST1					0x0e3
#define DCRN_CPC0_STRP0					0x0e4
#define DCRN_CPC0_STRP1					0x0e5
#define DCRN_CPC0_STRP2					0x0e6
#define DCRN_CPC0_STRP3					0x0e7
#define DCRN_CPC0_GPIO					0x0e8
#define DCRN_CPC0_PLB					0x0e9
#define DCRN_CPC0_CR1					0x0ea
#define DCRN_CPC0_CR0					0x0eb
#define	  CPC0_CR0_SWE					  0x80000000
#define	  CPC0_CR0_CETE					  0x40000000
#define	  CPC0_CR0_U1FCS				  0x20000000
#define	  CPC0_CR0_U0DTE				  0x10000000
#define	  CPC0_CR0_U0DRE				  0x08000000
#define	  CPC0_CR0_U0DC					  0x04000000
#define	  CPC0_CR0_U1DTE				  0x02000000
#define	  CPC0_CR0_U1DRE				  0x01000000
#define	  CPC0_CR0_U1DC					  0x00800000
#define	  CPC0_CR0_U0EC					  0x00400000
#define	  CPC0_CR0_U1EC					  0x00200000
#define	  CPC0_CR0_UDIV_MASK				  0x001f0000
#define	  CPC0_CR0_UDIV(reg) \
		((((reg) & CPC0_CR0_UDIV_MASK) >> 16) + 1)
#define DCRN_CPC0_MIRQ0					0x0ec
#define DCRN_CPC0_MIRQ1					0x0ed
#define DCRN_CPC0_JTAGID				0x0ef

#define DCRN_MAL0_CFG					0x180
#define MAL_RESET 0x80000000

/* 440EP Clock/Power-on Reset regs */
#define DCRN_CPR0_ADDR	0xc
#define DCRN_CPR0_DATA	0xd
#define CPR0_PLLD0	0x60
#define CPR0_OPBD0	0xc0
#define CPR0_PERD0	0xe0
#define CPR0_PRIMBD0	0xa0
#define CPR0_SCPID	0x120
#define CPR0_PLLC0	0x40

/* 405GP Clocking/Power Management/Chip Control regs */
#define DCRN_CPC0_PLLMR 0xb0
#define DCRN_405_CPC0_CR0 0xb1
#define DCRN_405_CPC0_CR1 0xb2
#define DCRN_405_CPC0_PSR 0xb4

/* 405EP Clocking/Power Management/Chip Control regs */
#define DCRN_CPC0_PLLMR0  0xf0
#define DCRN_CPC0_PLLMR1  0xf4
#define DCRN_CPC0_UCR     0xf5

/* 440GX Clock control etc */


#define DCRN_CPR0_CLKUPD				0x020
#define DCRN_CPR0_PLLC					0x040
#define DCRN_CPR0_PLLD					0x060
#define DCRN_CPR0_PRIMAD				0x080
#define DCRN_CPR0_PRIMBD				0x0a0
#define DCRN_CPR0_OPBD					0x0c0
#define DCRN_CPR0_PERD					0x0e0
#define DCRN_CPR0_MALD					0x100

#define DCRN_SDR0_CONFIG_ADDR 	0xe
#define DCRN_SDR0_CONFIG_DATA	0xf

/* SDR read/write helper macros */
#define SDR0_READ(offset) ({\
	mtdcr(DCRN_SDR0_CONFIG_ADDR, offset); \
	mfdcr(DCRN_SDR0_CONFIG_DATA); })
#define SDR0_WRITE(offset, data) ({\
	mtdcr(DCRN_SDR0_CONFIG_ADDR, offset); \
	mtdcr(DCRN_SDR0_CONFIG_DATA, data); })

#define DCRN_SDR0_UART0		0x0120
#define DCRN_SDR0_UART1		0x0121
#define DCRN_SDR0_UART2		0x0122
#define DCRN_SDR0_UART3		0x0123


/* CPRs read/write helper macros - based off include/asm-ppc/ibm44x.h */

#define DCRN_CPR0_CFGADDR				0xc
#define DCRN_CPR0_CFGDATA				0xd

#define CPR0_READ(offset) ({\
	mtdcr(DCRN_CPR0_CFGADDR, offset); \
	mfdcr(DCRN_CPR0_CFGDATA); })
#define CPR0_WRITE(offset, data) ({\
	mtdcr(DCRN_CPR0_CFGADDR, offset); \
	mtdcr(DCRN_CPR0_CFGDATA, data); })



#endif	/* _PPC_BOOT_DCR_H_ */

^ permalink raw reply	[flat|nested] 4+ messages in thread

* How to access DCR registers in powerpc440gx? Got err when use macro def in Linux kernel
  2009-09-10 17:24 How to access DCR registers in powerpc440gx? Got err when use macro def in Linux kernel g r1x
@ 2009-09-11  4:14 ` g r1x
  2009-09-11  5:31   ` David Gibson
  0 siblings, 1 reply; 4+ messages in thread
From: g r1x @ 2009-09-11  4:14 UTC (permalink / raw)
  To: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 2870 bytes --]

Now, I'm writing a DMA driver on powerpc
440gx platform(2.6.26.5), as the only way to set up DMA Controller is
to access it's dcr registers with 'mfdcr' and 'mtdcr'.

I've found some dma code in Linux kernel 2.6.26.5, so I copy the code
u wrote to my driver module directory, and include them, but when I
compile my driver, gcc complains following err messages:
--------------------------------------------------------
{standard input}: Assembler messages:
{standard input}:83: Error: unsupported relocation against dmanr
---------------------------------------------------------


code I copy from kernel 2.6.26.5 (arch/ppc/syslib/ppc4xx_dma.c)
--------------------------------------------
#include "dcr.h"
/* #inlcude <asm/dcr-native.h> */
ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];

int ppc4xx_get_dma_status(void)
{
      return (mfdcr(DCRN_DMASR));
}


void ppc4xx_enable_dma(unsigned int dmanr)
{
     unsigned int control;
     ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

     .........

     / * for other xfer modes, the addresses are already set */
     control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); <----------------err
}
void ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
      if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
              printk("set_src_addr: bad channel: %d\n", dmanr);
              return;
      }

#ifdef PPC4xx_DMA_64BIT
      mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
      mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}




--------------------------------------------------

DCR access micro I copied:
----------------------------------
#define mfdcr(rn) \
     ({      \
             unsigned long rval; \
             asm volatile("mfdcr %0,%1" : "=r"(rval) : "i"(rn)); \
             rval; \
     })
#define mtdcr(rn, val) \
     asm volatile("mtdcr %0,%1" : : "i"(rn), "r"(val))
-----------------------------------------------------

Makefile I worte
-------------------------
obj-m := dmatest.o
dmatest-objs += dma.o core.o
KBUILD_CFLAGS += -m440 -mregnames -Wa, -booke
KBUILD_ASFAGS += -m440 -mregnames -Wa, -booke
---------

KBUILD_CFLAGS += -m440 -mregnames -Wa, -booke
KBUILD_ASFAGS += -m440 -mregnames -Wa, -booke
I add these two sentences when I read these
http://sourceware.org/ml/binutils/2004-09/msg00161.html.

When I change it to mfdcr(DCRN_DMACR0); then everything is fine. but
from the result I got when I insmod my module driver, dcr reg is not
changed.

It seems that it's the problem with gnu assembler, my GNU assembler ver is 2.17

The attachments are the c source file I copy from linux 2.6.26.5, and
I use these dma function to directly manipulate dcr registers, but
when I  make my driver module outside kernel source tree, it complains
the error I mentioned above.

Thanks!

[-- Attachment #2: ppc4xx_dma.c --]
[-- Type: application/octet-stream, Size: 18648 bytes --]

/*
 * IBM PPC4xx DMA engine core library
 *
 * Copyright 2000-2004 MontaVista Software Inc.
 *
 * Cleaned up and converted to new DCR access
 * Matt Porter <mporter@kernel.crashing.org>
 *
 * Original code by Armin Kuster <akuster@mvista.com>
 * and Pete Popov <ppopov@mvista.com>
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 *
 * You should have received a copy of the  GNU General Public License along
 * with this program; if not, write  to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/module.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/ppc4xx_dma.h>

ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];

int
ppc4xx_get_dma_status(void)
{
	return (mfdcr(DCRN_DMASR));
}

void
ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("set_src_addr: bad channel: %d\n", dmanr);
		return;
	}

#ifdef PPC4xx_DMA_64BIT
	mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
	mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}

void
ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("set_dst_addr: bad channel: %d\n", dmanr);
		return;
	}

#ifdef PPC4xx_DMA_64BIT
	mtdcr(DCRN_DMADAH0 + dmanr*2, (u32)(dst_addr >> 32));
#else
	mtdcr(DCRN_DMADA0 + dmanr*2, (u32)dst_addr);
#endif
}

void
ppc4xx_enable_dma(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
	unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR,
				       DMA_CS1 | DMA_TS1 | DMA_CH1_ERR,
				       DMA_CS2 | DMA_TS2 | DMA_CH2_ERR,
				       DMA_CS3 | DMA_TS3 | DMA_CH3_ERR};

	if (p_dma_ch->in_use) {
		printk("enable_dma: channel %d in use\n", dmanr);
		return;
	}

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("enable_dma: bad channel: %d\n", dmanr);
		return;
	}

	if (p_dma_ch->mode == DMA_MODE_READ) {
		/* peripheral to memory */
		ppc4xx_set_src_addr(dmanr, 0);
		ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr);
	} else if (p_dma_ch->mode == DMA_MODE_WRITE) {
		/* memory to peripheral */
		ppc4xx_set_src_addr(dmanr, p_dma_ch->addr);
		ppc4xx_set_dst_addr(dmanr, 0);
	}

	/* for other xfer modes, the addresses are already set */
	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));

	control &= ~(DMA_TM_MASK | DMA_TD);	/* clear all mode bits */
	if (p_dma_ch->mode == DMA_MODE_MM) {
		/* software initiated memory to memory */
		control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
	}

	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	/*
	 * Clear the CS, TS, RI bits for the channel from DMASR.  This
	 * has been observed to happen correctly only after the mode and
	 * ETD/DCE bits in DMACRx are set above.  Must do this before
	 * enabling the channel.
	 */

	mtdcr(DCRN_DMASR, status_bits[dmanr]);

	/*
	 * For device-paced transfers, Terminal Count Enable apparently
	 * must be on, and this must be turned on after the mode, etc.
	 * bits are cleared above (at least on Redwood-6).
	 */

	if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) ||
	    (p_dma_ch->mode == DMA_MODE_MM_DEVATSRC))
		control |= DMA_TCE_ENABLE;

	/*
	 * Now enable the channel.
	 */

	control |= (p_dma_ch->mode | DMA_CE_ENABLE);

	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	p_dma_ch->in_use = 1;
}

void
ppc4xx_disable_dma(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (!p_dma_ch->in_use) {
		printk("disable_dma: channel %d not in use\n", dmanr);
		return;
	}

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("disable_dma: bad channel: %d\n", dmanr);
		return;
	}

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control &= ~DMA_CE_ENABLE;
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	p_dma_ch->in_use = 0;
}

/*
 * Sets the dma mode for single DMA transfers only.
 * For scatter/gather transfers, the mode is passed to the
 * alloc_dma_handle() function as one of the parameters.
 *
 * The mode is simply saved and used later.  This allows
 * the driver to call set_dma_mode() and set_dma_addr() in
 * any order.
 *
 * Valid mode values are:
 *
 * DMA_MODE_READ          peripheral to memory
 * DMA_MODE_WRITE         memory to peripheral
 * DMA_MODE_MM            memory to memory
 * DMA_MODE_MM_DEVATSRC   device-paced memory to memory, device at src
 * DMA_MODE_MM_DEVATDST   device-paced memory to memory, device at dst
 */
int
ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode)
{
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("set_dma_mode: bad channel 0x%x\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	p_dma_ch->mode = mode;

	return DMA_STATUS_GOOD;
}

/*
 * Sets the DMA Count register. Note that 'count' is in bytes.
 * However, the DMA Count register counts the number of "transfers",
 * where each transfer is equal to the bus width.  Thus, count
 * MUST be a multiple of the bus width.
 */
void
ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count)
{
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

#ifdef DEBUG_4xxDMA
	{
		int error = 0;
		switch (p_dma_ch->pwidth) {
		case PW_8:
			break;
		case PW_16:
			if (count & 0x1)
				error = 1;
			break;
		case PW_32:
			if (count & 0x3)
				error = 1;
			break;
		case PW_64:
			if (count & 0x7)
				error = 1;
			break;
		default:
			printk("set_dma_count: invalid bus width: 0x%x\n",
			       p_dma_ch->pwidth);
			return;
		}
		if (error)
			printk
			    ("Warning: set_dma_count count 0x%x bus width %d\n",
			     count, p_dma_ch->pwidth);
	}
#endif

	count = count >> p_dma_ch->shift;

	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), count);
}

/*
 *   Returns the number of bytes left to be transferred.
 *   After a DMA transfer, this should return zero.
 *   Reading this while a DMA transfer is still in progress will return
 *   unpredictable results.
 */
int
ppc4xx_get_dma_residue(unsigned int dmanr)
{
	unsigned int count;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_get_dma_residue: bad channel 0x%x\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	count = mfdcr(DCRN_DMACT0 + (dmanr * 0x8));

	return (count << p_dma_ch->shift);
}

/*
 * Sets the DMA address for a memory to peripheral or peripheral
 * to memory transfer.  The address is just saved in the channel
 * structure for now and used later in enable_dma().
 */
void
ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr)
{
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr);
		return;
	}

#ifdef DEBUG_4xxDMA
	{
		int error = 0;
		switch (p_dma_ch->pwidth) {
		case PW_8:
			break;
		case PW_16:
			if ((unsigned) addr & 0x1)
				error = 1;
			break;
		case PW_32:
			if ((unsigned) addr & 0x3)
				error = 1;
			break;
		case PW_64:
			if ((unsigned) addr & 0x7)
				error = 1;
			break;
		default:
			printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n",
			       p_dma_ch->pwidth);
			return;
		}
		if (error)
			printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n",
			       addr, p_dma_ch->pwidth);
	}
#endif

	/* save dma address and program it later after we know the xfer mode */
	p_dma_ch->addr = addr;
}

/*
 * Sets both DMA addresses for a memory to memory transfer.
 * For memory to peripheral or peripheral to memory transfers
 * the function set_dma_addr() should be used instead.
 */
void
ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
		     phys_addr_t dst_dma_addr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr);
		return;
	}

#ifdef DEBUG_4xxDMA
	{
		ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
		int error = 0;
		switch (p_dma_ch->pwidth) {
			case PW_8:
				break;
			case PW_16:
				if (((unsigned) src_dma_addr & 0x1) ||
						((unsigned) dst_dma_addr & 0x1)
				   )
					error = 1;
				break;
			case PW_32:
				if (((unsigned) src_dma_addr & 0x3) ||
						((unsigned) dst_dma_addr & 0x3)
				   )
					error = 1;
				break;
			case PW_64:
				if (((unsigned) src_dma_addr & 0x7) ||
						((unsigned) dst_dma_addr & 0x7)
				   )
					error = 1;
				break;
			default:
				printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n",
						p_dma_ch->pwidth);
				return;
		}
		if (error)
			printk
				("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n",
				 src_dma_addr, dst_dma_addr, p_dma_ch->pwidth);
	}
#endif

	ppc4xx_set_src_addr(dmanr, src_dma_addr);
	ppc4xx_set_dst_addr(dmanr, dst_dma_addr);
}

/*
 * Enables the channel interrupt.
 *
 * If performing a scatter/gatter transfer, this function
 * MUST be called before calling alloc_dma_handle() and building
 * the sgl list.  Otherwise, interrupts will not be enabled, if
 * they were previously disabled.
 */
int
ppc4xx_enable_dma_interrupt(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	p_dma_ch->int_enable = 1;

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control |= DMA_CIE_ENABLE;	/* Channel Interrupt Enable */
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	return DMA_STATUS_GOOD;
}

/*
 * Disables the channel interrupt.
 *
 * If performing a scatter/gatter transfer, this function
 * MUST be called before calling alloc_dma_handle() and building
 * the sgl list.  Otherwise, interrupts will not be disabled, if
 * they were previously enabled.
 */
int
ppc4xx_disable_dma_interrupt(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	p_dma_ch->int_enable = 0;

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control &= ~DMA_CIE_ENABLE;	/* Channel Interrupt Enable */
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	return DMA_STATUS_GOOD;
}

/*
 * Configures a DMA channel, including the peripheral bus width, if a
 * peripheral is attached to the channel, the polarity of the DMAReq and
 * DMAAck signals, etc.  This information should really be setup by the boot
 * code, since most likely the configuration won't change dynamically.
 * If the kernel has to call this function, it's recommended that it's
 * called from platform specific init code.  The driver should not need to
 * call this function.
 */
int
ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init)
{
	unsigned int polarity;
	uint32_t control = 0;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	DMA_MODE_READ = (unsigned long) DMA_TD;	/* Peripheral to Memory */
	DMA_MODE_WRITE = 0;	/* Memory to Peripheral */

	if (!p_init) {
		printk("ppc4xx_init_dma_channel: NULL p_init\n");
		return DMA_STATUS_NULL_POINTER;
	}

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

#if DCRN_POL > 0
	polarity = mfdcr(DCRN_POL);
#else
	polarity = 0;
#endif

	/* Setup the control register based on the values passed to
	 * us in p_init.  Then, over-write the control register with this
	 * new value.
	 */
	control |= SET_DMA_CONTROL;

	/* clear all polarity signals and then "or" in new signal levels */
	polarity &= ~GET_DMA_POLARITY(dmanr);
	polarity |= p_init->polarity;
#if DCRN_POL > 0
	mtdcr(DCRN_POL, polarity);
#endif
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	/* save these values in our dma channel structure */
	memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t));

	/*
	 * The peripheral width values written in the control register are:
	 *   PW_8                 0
	 *   PW_16                1
	 *   PW_32                2
	 *   PW_64                3
	 *
	 *   Since the DMA count register takes the number of "transfers",
	 *   we need to divide the count sent to us in certain
	 *   functions by the appropriate number.  It so happens that our
	 *   right shift value is equal to the peripheral width value.
	 */
	p_dma_ch->shift = p_init->pwidth;

	/*
	 * Save the control word for easy access.
	 */
	p_dma_ch->control = control;

	mtdcr(DCRN_DMASR, 0xffffffff);	/* clear status register */
	return DMA_STATUS_GOOD;
}

/*
 * This function returns the channel configuration.
 */
int
ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch)
{
	unsigned int polarity;
	unsigned int control;

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_get_channel_config: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	memcpy(p_dma_ch, &dma_channels[dmanr], sizeof (ppc_dma_ch_t));

#if DCRN_POL > 0
	polarity = mfdcr(DCRN_POL);
#else
	polarity = 0;
#endif

	p_dma_ch->polarity = polarity & GET_DMA_POLARITY(dmanr);
	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));

	p_dma_ch->cp = GET_DMA_PRIORITY(control);
	p_dma_ch->pwidth = GET_DMA_PW(control);
	p_dma_ch->psc = GET_DMA_PSC(control);
	p_dma_ch->pwc = GET_DMA_PWC(control);
	p_dma_ch->phc = GET_DMA_PHC(control);
	p_dma_ch->ce = GET_DMA_CE_ENABLE(control);
	p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control);
	p_dma_ch->shift = GET_DMA_PW(control);

#ifdef CONFIG_PPC4xx_EDMA
	p_dma_ch->pf = GET_DMA_PREFETCH(control);
#else
	p_dma_ch->ch_enable = GET_DMA_CH(control);
	p_dma_ch->ece_enable = GET_DMA_ECE(control);
	p_dma_ch->tcd_disable = GET_DMA_TCD(control);
#endif
	return DMA_STATUS_GOOD;
}

/*
 * Sets the priority for the DMA channel dmanr.
 * Since this is setup by the hardware init function, this function
 * can be used to dynamically change the priority of a channel.
 *
 * Acceptable priorities:
 *
 * PRIORITY_LOW
 * PRIORITY_MID_LOW
 * PRIORITY_MID_HIGH
 * PRIORITY_HIGH
 *
 */
int
ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority)
{
	unsigned int control;

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_set_channel_priority: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	if ((priority != PRIORITY_LOW) &&
	    (priority != PRIORITY_MID_LOW) &&
	    (priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) {
		printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority);
	}

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control |= SET_DMA_PRIORITY(priority);
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	return DMA_STATUS_GOOD;
}

/*
 * Returns the width of the peripheral attached to this channel. This assumes
 * that someone who knows the hardware configuration, boot code or some other
 * init code, already set the width.
 *
 * The return value is one of:
 *   PW_8
 *   PW_16
 *   PW_32
 *   PW_64
 *
 *   The function returns 0 on error.
 */
unsigned int
ppc4xx_get_peripheral_width(unsigned int dmanr)
{
	unsigned int control;

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_get_peripheral_width: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));

	return (GET_DMA_PW(control));
}

/*
 * Clears the channel status bits
 */
int
ppc4xx_clr_dma_status(unsigned int dmanr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_clr_dma_status: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
	mtdcr(DCRN_DMASR, ((u32)DMA_CH0_ERR | (u32)DMA_CS0 | (u32)DMA_TS0) >> dmanr);
	return DMA_STATUS_GOOD;
}

#ifdef CONFIG_PPC4xx_EDMA
/*
 * Enables the burst on the channel (BTEN bit in the control/count register)
 * Note:
 * For scatter/gather dma, this function MUST be called before the
 * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
 * sgl list and used as each sgl element is added.
 */
int
ppc4xx_enable_burst(unsigned int dmanr)
{
	unsigned int ctc;
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_enable_burst: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
        ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) | DMA_CTC_BTEN;
	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
	return DMA_STATUS_GOOD;
}
/*
 * Disables the burst on the channel (BTEN bit in the control/count register)
 * Note:
 * For scatter/gather dma, this function MUST be called before the
 * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
 * sgl list and used as each sgl element is added.
 */
int
ppc4xx_disable_burst(unsigned int dmanr)
{
	unsigned int ctc;
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_disable_burst: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
	ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BTEN;
	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
	return DMA_STATUS_GOOD;
}
/*
 * Sets the burst size (number of peripheral widths) for the channel
 * (BSIZ bits in the control/count register))
 * must be one of:
 *    DMA_CTC_BSIZ_2
 *    DMA_CTC_BSIZ_4
 *    DMA_CTC_BSIZ_8
 *    DMA_CTC_BSIZ_16
 * Note:
 * For scatter/gather dma, this function MUST be called before the
 * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
 * sgl list and used as each sgl element is added.
 */
int
ppc4xx_set_burst_size(unsigned int dmanr, unsigned int bsize)
{
	unsigned int ctc;
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_set_burst_size: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
	ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BSIZ_MSK;
	ctc |= (bsize & DMA_CTC_BSIZ_MSK);
	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
	return DMA_STATUS_GOOD;
}

EXPORT_SYMBOL(ppc4xx_enable_burst);
EXPORT_SYMBOL(ppc4xx_disable_burst);
EXPORT_SYMBOL(ppc4xx_set_burst_size);
#endif /* CONFIG_PPC4xx_EDMA */

EXPORT_SYMBOL(ppc4xx_init_dma_channel);
EXPORT_SYMBOL(ppc4xx_get_channel_config);
EXPORT_SYMBOL(ppc4xx_set_channel_priority);
EXPORT_SYMBOL(ppc4xx_get_peripheral_width);
EXPORT_SYMBOL(dma_channels);
EXPORT_SYMBOL(ppc4xx_set_src_addr);
EXPORT_SYMBOL(ppc4xx_set_dst_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr2);
EXPORT_SYMBOL(ppc4xx_enable_dma);
EXPORT_SYMBOL(ppc4xx_disable_dma);
EXPORT_SYMBOL(ppc4xx_set_dma_mode);
EXPORT_SYMBOL(ppc4xx_set_dma_count);
EXPORT_SYMBOL(ppc4xx_get_dma_residue);
EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_get_dma_status);
EXPORT_SYMBOL(ppc4xx_clr_dma_status);


[-- Attachment #3: dcr-native.h --]
[-- Type: application/octet-stream, Size: 3320 bytes --]

/*
 * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
 *                    <benh@kernel.crashing.org>
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef _ASM_POWERPC_DCR_NATIVE_H
#define _ASM_POWERPC_DCR_NATIVE_H
#ifdef __KERNEL__
#ifndef __ASSEMBLY__

#include <linux/spinlock.h>

typedef struct {
	unsigned int base;
} dcr_host_t;

#define DCR_MAP_OK(host)	(1)

#define dcr_map(dev, dcr_n, dcr_c)	((dcr_host_t){ .base = (dcr_n) })
#define dcr_unmap(host, dcr_c)		do {} while (0)
#define dcr_read(host, dcr_n)		mfdcr(dcr_n + host.base)
#define dcr_write(host, dcr_n, value)	mtdcr(dcr_n + host.base, value)

/* Device Control Registers */
void __mtdcr(int reg, unsigned int val);
unsigned int __mfdcr(int reg);
#define mfdcr(rn)						\
	({unsigned int rval;					\
	if (__builtin_constant_p(rn))				\
		asm volatile("mfdcr %0," __stringify(rn)	\
		              : "=r" (rval));			\
	else							\
		rval = __mfdcr(rn);				\
	rval;})

#define mtdcr(rn, v)						\
do {								\
	if (__builtin_constant_p(rn))				\
		asm volatile("mtdcr " __stringify(rn) ",%0"	\
			      : : "r" (v)); 			\
	else							\
		__mtdcr(rn, v);					\
} while (0)

/* R/W of indirect DCRs make use of standard naming conventions for DCRs */
extern spinlock_t dcr_ind_lock;

static inline unsigned __mfdcri(int base_addr, int base_data, int reg)
{
	unsigned long flags;
	unsigned int val;

	spin_lock_irqsave(&dcr_ind_lock, flags);
	__mtdcr(base_addr, reg);
	val = __mfdcr(base_data);
	spin_unlock_irqrestore(&dcr_ind_lock, flags);
	return val;
}

static inline void __mtdcri(int base_addr, int base_data, int reg,
			    unsigned val)
{
	unsigned long flags;

	spin_lock_irqsave(&dcr_ind_lock, flags);
	__mtdcr(base_addr, reg);
	__mtdcr(base_data, val);
	spin_unlock_irqrestore(&dcr_ind_lock, flags);
}

static inline void __dcri_clrset(int base_addr, int base_data, int reg,
				 unsigned clr, unsigned set)
{
	unsigned long flags;
	unsigned int val;

	spin_lock_irqsave(&dcr_ind_lock, flags);
	__mtdcr(base_addr, reg);
	val = (__mfdcr(base_data) & ~clr) | set;
	__mtdcr(base_data, val);
	spin_unlock_irqrestore(&dcr_ind_lock, flags);
}

#define mfdcri(base, reg)	__mfdcri(DCRN_ ## base ## _CONFIG_ADDR,	\
					 DCRN_ ## base ## _CONFIG_DATA,	\
					 reg)

#define mtdcri(base, reg, data)	__mtdcri(DCRN_ ## base ## _CONFIG_ADDR,	\
					 DCRN_ ## base ## _CONFIG_DATA,	\
					 reg, data)

#define dcri_clrset(base, reg, clr, set)	__dcri_clrset(DCRN_ ## base ## _CONFIG_ADDR,	\
							      DCRN_ ## base ## _CONFIG_DATA,	\
							      reg, clr, set)

#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_DCR_NATIVE_H */

[-- Attachment #4: dcr.h --]
[-- Type: application/octet-stream, Size: 6308 bytes --]

#ifndef _PPC_BOOT_DCR_H_
#define _PPC_BOOT_DCR_H_

#define mfdcr(rn) \
	({	\
		unsigned long rval; \
		asm volatile("mfdcr %0,%1" : "=r"(rval) : "i"(rn)); \
		rval; \
	})
#define mtdcr(rn, val) \
	asm volatile("mtdcr %0,%1" : : "i"(rn), "r"(val))

/* 440GP/440GX SDRAM controller DCRs */
#define DCRN_SDRAM0_CFGADDR				0x010
#define DCRN_SDRAM0_CFGDATA				0x011

#define SDRAM0_READ(offset) ({\
	mtdcr(DCRN_SDRAM0_CFGADDR, offset); \
	mfdcr(DCRN_SDRAM0_CFGDATA); })
#define SDRAM0_WRITE(offset, data) ({\
	mtdcr(DCRN_SDRAM0_CFGADDR, offset); \
	mtdcr(DCRN_SDRAM0_CFGDATA, data); })

#define 	SDRAM0_B0CR				0x40
#define 	SDRAM0_B1CR				0x44
#define 	SDRAM0_B2CR				0x48
#define 	SDRAM0_B3CR				0x4c

static const unsigned long sdram_bxcr[] = { SDRAM0_B0CR, SDRAM0_B1CR,
					    SDRAM0_B2CR, SDRAM0_B3CR };

#define			SDRAM_CONFIG_BANK_ENABLE        0x00000001
#define			SDRAM_CONFIG_SIZE_MASK          0x000e0000
#define			SDRAM_CONFIG_BANK_SIZE(reg)	\
	(0x00400000 << ((reg & SDRAM_CONFIG_SIZE_MASK) >> 17))

/* 440GP External Bus Controller (EBC) */
#define DCRN_EBC0_CFGADDR				0x012
#define DCRN_EBC0_CFGDATA				0x013
#define   EBC_NUM_BANKS					  8
#define   EBC_B0CR					  0x00
#define   EBC_B1CR					  0x01
#define   EBC_B2CR					  0x02
#define   EBC_B3CR					  0x03
#define   EBC_B4CR					  0x04
#define   EBC_B5CR					  0x05
#define   EBC_B6CR					  0x06
#define   EBC_B7CR					  0x07
#define   EBC_BXCR(n)					  (n)
#define	    EBC_BXCR_BAS				    0xfff00000
#define	    EBC_BXCR_BS				  	    0x000e0000
#define	    EBC_BXCR_BANK_SIZE(reg) \
	(0x100000 << (((reg) & EBC_BXCR_BS) >> 17))
#define	    EBC_BXCR_BU				  	    0x00018000
#define	      EBC_BXCR_BU_OFF			  	      0x00000000
#define	      EBC_BXCR_BU_RO			  	      0x00008000
#define	      EBC_BXCR_BU_WO			  	      0x00010000
#define	      EBC_BXCR_BU_RW			  	      0x00018000
#define	    EBC_BXCR_BW				  	    0x00006000
#define   EBC_B0AP					  0x10
#define   EBC_B1AP					  0x11
#define   EBC_B2AP					  0x12
#define   EBC_B3AP					  0x13
#define   EBC_B4AP					  0x14
#define   EBC_B5AP					  0x15
#define   EBC_B6AP					  0x16
#define   EBC_B7AP					  0x17
#define   EBC_BXAP(n)					  (0x10+(n))
#define   EBC_BEAR					  0x20
#define   EBC_BESR					  0x21
#define   EBC_CFG					  0x23
#define   EBC_CID					  0x24

/* 440GP Clock, PM, chip control */
#define DCRN_CPC0_SR					0x0b0
#define DCRN_CPC0_ER					0x0b1
#define DCRN_CPC0_FR					0x0b2
#define DCRN_CPC0_SYS0					0x0e0
#define	  CPC0_SYS0_TUNE				  0xffc00000
#define	  CPC0_SYS0_FBDV_MASK				  0x003c0000
#define	  CPC0_SYS0_FWDVA_MASK				  0x00038000
#define	  CPC0_SYS0_FWDVB_MASK				  0x00007000
#define	  CPC0_SYS0_OPDV_MASK				  0x00000c00
#define	  CPC0_SYS0_EPDV_MASK				  0x00000300
/* Helper macros to compute the actual clock divider values from the
 * encodings in the CPC0 register */
#define	  CPC0_SYS0_FBDV(reg) \
		((((((reg) & CPC0_SYS0_FBDV_MASK) >> 18) - 1) & 0xf) + 1)
#define	  CPC0_SYS0_FWDVA(reg) \
		(8 - (((reg) & CPC0_SYS0_FWDVA_MASK) >> 15))
#define	  CPC0_SYS0_FWDVB(reg) \
		(8 - (((reg) & CPC0_SYS0_FWDVB_MASK) >> 12))
#define	  CPC0_SYS0_OPDV(reg) \
		((((reg) & CPC0_SYS0_OPDV_MASK) >> 10) + 1)
#define	  CPC0_SYS0_EPDV(reg) \
		((((reg) & CPC0_SYS0_EPDV_MASK) >> 8) + 1)
#define	  CPC0_SYS0_EXTSL				  0x00000080
#define	  CPC0_SYS0_RW_MASK				  0x00000060
#define	  CPC0_SYS0_RL					  0x00000010
#define	  CPC0_SYS0_ZMIISL_MASK				  0x0000000c
#define	  CPC0_SYS0_BYPASS				  0x00000002
#define	  CPC0_SYS0_NTO1				  0x00000001
#define DCRN_CPC0_SYS1					0x0e1
#define DCRN_CPC0_CUST0					0x0e2
#define DCRN_CPC0_CUST1					0x0e3
#define DCRN_CPC0_STRP0					0x0e4
#define DCRN_CPC0_STRP1					0x0e5
#define DCRN_CPC0_STRP2					0x0e6
#define DCRN_CPC0_STRP3					0x0e7
#define DCRN_CPC0_GPIO					0x0e8
#define DCRN_CPC0_PLB					0x0e9
#define DCRN_CPC0_CR1					0x0ea
#define DCRN_CPC0_CR0					0x0eb
#define	  CPC0_CR0_SWE					  0x80000000
#define	  CPC0_CR0_CETE					  0x40000000
#define	  CPC0_CR0_U1FCS				  0x20000000
#define	  CPC0_CR0_U0DTE				  0x10000000
#define	  CPC0_CR0_U0DRE				  0x08000000
#define	  CPC0_CR0_U0DC					  0x04000000
#define	  CPC0_CR0_U1DTE				  0x02000000
#define	  CPC0_CR0_U1DRE				  0x01000000
#define	  CPC0_CR0_U1DC					  0x00800000
#define	  CPC0_CR0_U0EC					  0x00400000
#define	  CPC0_CR0_U1EC					  0x00200000
#define	  CPC0_CR0_UDIV_MASK				  0x001f0000
#define	  CPC0_CR0_UDIV(reg) \
		((((reg) & CPC0_CR0_UDIV_MASK) >> 16) + 1)
#define DCRN_CPC0_MIRQ0					0x0ec
#define DCRN_CPC0_MIRQ1					0x0ed
#define DCRN_CPC0_JTAGID				0x0ef

#define DCRN_MAL0_CFG					0x180
#define MAL_RESET 0x80000000

/* 440EP Clock/Power-on Reset regs */
#define DCRN_CPR0_ADDR	0xc
#define DCRN_CPR0_DATA	0xd
#define CPR0_PLLD0	0x60
#define CPR0_OPBD0	0xc0
#define CPR0_PERD0	0xe0
#define CPR0_PRIMBD0	0xa0
#define CPR0_SCPID	0x120
#define CPR0_PLLC0	0x40

/* 405GP Clocking/Power Management/Chip Control regs */
#define DCRN_CPC0_PLLMR 0xb0
#define DCRN_405_CPC0_CR0 0xb1
#define DCRN_405_CPC0_CR1 0xb2
#define DCRN_405_CPC0_PSR 0xb4

/* 405EP Clocking/Power Management/Chip Control regs */
#define DCRN_CPC0_PLLMR0  0xf0
#define DCRN_CPC0_PLLMR1  0xf4
#define DCRN_CPC0_UCR     0xf5

/* 440GX Clock control etc */


#define DCRN_CPR0_CLKUPD				0x020
#define DCRN_CPR0_PLLC					0x040
#define DCRN_CPR0_PLLD					0x060
#define DCRN_CPR0_PRIMAD				0x080
#define DCRN_CPR0_PRIMBD				0x0a0
#define DCRN_CPR0_OPBD					0x0c0
#define DCRN_CPR0_PERD					0x0e0
#define DCRN_CPR0_MALD					0x100

#define DCRN_SDR0_CONFIG_ADDR 	0xe
#define DCRN_SDR0_CONFIG_DATA	0xf

/* SDR read/write helper macros */
#define SDR0_READ(offset) ({\
	mtdcr(DCRN_SDR0_CONFIG_ADDR, offset); \
	mfdcr(DCRN_SDR0_CONFIG_DATA); })
#define SDR0_WRITE(offset, data) ({\
	mtdcr(DCRN_SDR0_CONFIG_ADDR, offset); \
	mtdcr(DCRN_SDR0_CONFIG_DATA, data); })

#define DCRN_SDR0_UART0		0x0120
#define DCRN_SDR0_UART1		0x0121
#define DCRN_SDR0_UART2		0x0122
#define DCRN_SDR0_UART3		0x0123


/* CPRs read/write helper macros - based off include/asm-ppc/ibm44x.h */

#define DCRN_CPR0_CFGADDR				0xc
#define DCRN_CPR0_CFGDATA				0xd

#define CPR0_READ(offset) ({\
	mtdcr(DCRN_CPR0_CFGADDR, offset); \
	mfdcr(DCRN_CPR0_CFGDATA); })
#define CPR0_WRITE(offset, data) ({\
	mtdcr(DCRN_CPR0_CFGADDR, offset); \
	mtdcr(DCRN_CPR0_CFGDATA, data); })



#endif	/* _PPC_BOOT_DCR_H_ */

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: How to access DCR registers in powerpc440gx? Got err when use macro def in Linux kernel
  2009-09-11  4:14 ` g r1x
@ 2009-09-11  5:31   ` David Gibson
  2009-09-14 12:59     ` Arnd Bergmann
  0 siblings, 1 reply; 4+ messages in thread
From: David Gibson @ 2009-09-11  5:31 UTC (permalink / raw)
  To: g r1x; +Cc: linuxppc-dev

On Fri, Sep 11, 2009 at 12:14:55PM +0800, g r1x wrote:
> Now, I'm writing a DMA driver on powerpc
> 440gx platform(2.6.26.5), as the only way to set up DMA Controller is
> to access it's dcr registers with 'mfdcr' and 'mtdcr'.
> 
> I've found some dma code in Linux kernel 2.6.26.5, so I copy the code
> u wrote to my driver module directory, and include them, but when I
> compile my driver, gcc complains following err messages:

[snip]
> #define mfdcr(rn) \
>      ({      \
>              unsigned long rval; \
>              asm volatile("mfdcr %0,%1" : "=r"(rval) : "i"(rn)); \
>              rval; \
>      })

This form of mfdcr macro will only work when passed a constant DCR
number.  That's a limitation in the actual CPU implementation of the
mfdcr instruction (the DCR number is part of the instruction opcode).

Some newer cores have an indirect for of mfdcr which allows the DCR
number to be specified in a register, but I don't know if 440gx is one
of them.

In current kernels we have some DCR macros that use a big table of
pre-generated instructions in order to allow accesses to runtime
computed DCR numbers.  But either they didn't exist in 2.6.26, or they
have a different name, I don't remember.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: How to access DCR registers in powerpc440gx? Got err when use macro def in Linux kernel
  2009-09-11  5:31   ` David Gibson
@ 2009-09-14 12:59     ` Arnd Bergmann
  0 siblings, 0 replies; 4+ messages in thread
From: Arnd Bergmann @ 2009-09-14 12:59 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: g r1x, David Gibson

On Friday 11 September 2009, David Gibson wrote:
> On Fri, Sep 11, 2009 at 12:14:55PM +0800, g r1x wrote:
> > Now, I'm writing a DMA driver on powerpc
> > 440gx platform(2.6.26.5), as the only way to set up DMA Controller is
> > to access it's dcr registers with 'mfdcr' and 'mtdcr'.
> > 
> > I've found some dma code in Linux kernel 2.6.26.5, so I copy the code
> > u wrote to my driver module directory, and include them, but when I
> > compile my driver, gcc complains following err messages:
> 
> In current kernels we have some DCR macros that use a big table of
> pre-generated instructions in order to allow accesses to runtime
> computed DCR numbers.  But either they didn't exist in 2.6.26, or they
> have a different name, I don't remember.

The portable way to access DCRs is the API from asm/dcr.h, using dcr_map(),
dcr_read() and dcr_write().

	Arnd <><

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2009-09-14 13:05 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-10 17:24 How to access DCR registers in powerpc440gx? Got err when use macro def in Linux kernel g r1x
2009-09-11  4:14 ` g r1x
2009-09-11  5:31   ` David Gibson
2009-09-14 12:59     ` Arnd Bergmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.