diff -x .git -Nur linux-2.6.22.1/arch/avr32/boards/atngw100/setup.c linux-avr32.git/arch/avr32/boards/atngw100/setup.c --- linux-2.6.22.1/arch/avr32/boards/atngw100/setup.c 2007-07-10 20:56:30.000000000 +0200 +++ linux-avr32.git/arch/avr32/boards/atngw100/setup.c 2007-07-12 13:59:49.000000000 +0200 @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include /* Initialized by bootloader-specific startup code. */ struct tag *bootloader_tags __initdata; @@ -39,6 +41,11 @@ }, }; +static struct mci_platform_data __initdata mci0_data = { + .detect_pin = GPIO_PIN_PC(25), + .wp_pin = GPIO_PIN_PE(0), +}; + /* * The next two functions should go away as the boot loader is * supposed to initialize the macb address registers with a valid @@ -100,8 +107,31 @@ at32_setup_serial_console(0); } +static const struct gpio_led ngw_leds[] = { + { .name = "sys", .gpio = GPIO_PIN_PA(16), .active_low = 1, + .default_trigger = "heartbeat", + }, + { .name = "a", .gpio = GPIO_PIN_PA(19), .active_low = 1, }, + { .name = "b", .gpio = GPIO_PIN_PE(19), .active_low = 1, }, +}; + +static const struct gpio_led_platform_data ngw_led_data = { + .num_leds = ARRAY_SIZE(ngw_leds), + .leds = (void *) ngw_leds, +}; + +static struct platform_device ngw_gpio_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = (void *) &ngw_led_data, + } +}; + static int __init atngw100_init(void) { + unsigned i; + /* * ATNGW100 uses 16-bit SDRAM interface, so we don't need to * reserve any pins for it. @@ -115,6 +145,14 @@ set_hw_addr(at32_add_device_eth(1, ð_data[1])); at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info)); + at32_add_device_mci(0, &mci0_data); + at32_add_device_usba(0, NULL); + + for (i = 0; i < ARRAY_SIZE(ngw_leds); i++) { + at32_select_gpio(ngw_leds[i].gpio, + AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); + } + platform_device_register(&ngw_gpio_leds); return 0; } diff -x .git -Nur linux-2.6.22.1/arch/avr32/boards/atstk1000/atstk1002.c linux-avr32.git/arch/avr32/boards/atstk1000/atstk1002.c --- linux-2.6.22.1/arch/avr32/boards/atstk1000/atstk1002.c 2007-07-10 20:56:30.000000000 +0200 +++ linux-avr32.git/arch/avr32/boards/atstk1000/atstk1002.c 2007-07-12 13:59:49.000000000 +0200 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,6 @@ #include "atstk1000.h" -#define SW2_DEFAULT /* MMCI and UART_A available */ struct eth_addr { u8 addr[6]; @@ -36,6 +36,7 @@ static struct eth_addr __initdata hw_addr[2]; static struct eth_platform_data __initdata eth_data[2]; +#ifndef CONFIG_BOARD_ATSTK1002_SW1_CUSTOM static struct spi_board_info spi0_board_info[] __initdata = { { /* QVGA display */ @@ -45,6 +46,18 @@ .mode = SPI_MODE_3, }, }; +#endif + +#ifdef CONFIG_BOARD_ATSTK1002_SPI1 +static struct spi_board_info spi1_board_info[] __initdata = { { + /* patch in custom entries here */ +} }; +#endif + +static struct mci_platform_data __initdata mci0_data = { + .detect_pin = GPIO_PIN_NONE, + .wp_pin = GPIO_PIN_NONE, +}; /* * The next two functions should go away as the boot loader is @@ -101,12 +114,71 @@ clk_put(pclk); } -void __init setup_board(void) +#ifdef CONFIG_BOARD_ATSTK1002_J2_LED + +static struct gpio_led stk_j2_led[] = { +#ifdef CONFIG_BOARD_ATSTK1002_J2_LED8 +#define LEDSTRING "J2 jumpered to LED8" + { .name = "led0:amber", .gpio = GPIO_PIN_PB( 8), }, + { .name = "led1:amber", .gpio = GPIO_PIN_PB( 9), }, + { .name = "led2:amber", .gpio = GPIO_PIN_PB(10), }, + { .name = "led3:amber", .gpio = GPIO_PIN_PB(13), }, + { .name = "led4:amber", .gpio = GPIO_PIN_PB(14), }, + { .name = "led5:amber", .gpio = GPIO_PIN_PB(15), }, + { .name = "led6:amber", .gpio = GPIO_PIN_PB(16), }, + { .name = "led7:amber", .gpio = GPIO_PIN_PB(30), + .default_trigger = "heartbeat", }, +#else /* RGB */ +#define LEDSTRING "J2 jumpered to RGB LEDs" + { .name = "r1:red", .gpio = GPIO_PIN_PB( 8), }, + { .name = "g1:green", .gpio = GPIO_PIN_PB(10), }, + { .name = "b1:blue", .gpio = GPIO_PIN_PB(14), }, + + { .name = "r2:red", .gpio = GPIO_PIN_PB( 9), + .default_trigger = "heartbeat", }, + { .name = "g2:green", .gpio = GPIO_PIN_PB(13), }, + { .name = "b2:blue", .gpio = GPIO_PIN_PB(15), + .default_trigger = "heartbeat", }, + /* PB16, PB30 unused */ +#endif +}; + +static struct gpio_led_platform_data stk_j2_led_data = { + .num_leds = ARRAY_SIZE(stk_j2_led), + .leds = stk_j2_led, +}; + +static struct platform_device stk_j2_led_dev = { + .name = "leds-gpio", + .id = 2, /* gpio block J2 */ + .dev = { + .platform_data = &stk_j2_led_data, + }, +}; + +static void setup_j2_leds(void) { -#ifdef SW2_DEFAULT - at32_map_usart(1, 0); /* USART 1/A: /dev/ttyS0, DB9 */ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(stk_j2_led); i++) + at32_select_gpio(stk_j2_led[i].gpio, AT32_GPIOF_OUTPUT); + + printk("STK1002: " LEDSTRING "\n"); + platform_device_register(&stk_j2_led_dev); +} + #else +static void setup_j2_leds(void) +{ +} +#endif + +void __init setup_board(void) +{ +#ifdef CONFIG_BOARD_ATSTK1002_SW2_CUSTOM at32_map_usart(0, 1); /* USART 0/B: /dev/ttyS1, IRDA */ +#else + at32_map_usart(1, 0); /* USART 1/A: /dev/ttyS0, DB9 */ #endif /* USART 2/unused: expansion connector */ at32_map_usart(3, 2); /* USART 3/C: /dev/ttyS2, DB9 */ @@ -140,18 +212,40 @@ at32_add_system_devices(); -#ifdef SW2_DEFAULT - at32_add_device_usart(0); -#else +#ifdef CONFIG_BOARD_ATSTK1002_SW2_CUSTOM at32_add_device_usart(1); +#else + at32_add_device_usart(0); #endif at32_add_device_usart(2); +#ifndef CONFIG_BOARD_ATSTK1002_SW6_CUSTOM set_hw_addr(at32_add_device_eth(0, ð_data[0])); - - at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info)); +#endif +#ifdef CONFIG_BOARD_ATSTK1002_SW5_CUSTOM + set_hw_addr(at32_add_device_eth(1, ð_data[1])); +#else at32_add_device_lcdc(0, &atstk1000_lcdc_data, fbmem_start, fbmem_size); +#endif + +#ifndef CONFIG_BOARD_ATSTK1002_SW1_CUSTOM + at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info)); +#endif +#ifdef CONFIG_BOARD_ATSTK1002_SPI1 + at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info)); +#endif + at32_add_device_twi(0); +#ifndef CONFIG_BOARD_ATSTK1002_SW2_CUSTOM + at32_add_device_mci(0, &mci0_data); +#endif + at32_add_device_usba(0, NULL); + at32_add_device_abdac(0); +#ifndef CONFIG_BOARD_ATSTK1002_SW3_CUSTOM + at32_add_device_ssc(0, ATMEL_SSC_TX); +#endif + + setup_j2_leds(); return 0; } diff -x .git -Nur linux-2.6.22.1/arch/avr32/boards/atstk1000/Kconfig linux-avr32.git/arch/avr32/boards/atstk1000/Kconfig --- linux-2.6.22.1/arch/avr32/boards/atstk1000/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-avr32.git/arch/avr32/boards/atstk1000/Kconfig 2007-07-12 13:59:49.000000000 +0200 @@ -0,0 +1,79 @@ +# STK1000 customization + +if BOARD_ATSTK1002 + +config BOARD_ATSTK1002_CUSTOM + bool "Non-default STK-1002 jumper settings" + help + You will normally leave the jumpers on the CPU card at their + default settings. If you need to use certain peripherals, + you will need to change some of those jumpers. + +if BOARD_ATSTK1002_CUSTOM + +config BOARD_ATSTK1002_SW1_CUSTOM + bool "SW1: use SSC1 (not SPI0)" + help + This also prevents using the external DAC as an audio interface, + and means you can't initialize the on-board QVGA display. + +config BOARD_ATSTK1002_SW2_CUSTOM + bool "SW2: use IRDA or TIMER0 (not UART-A, MMC/SD, and PS2-A)" + help + If you change this you'll want an updated boot loader putting + the console on UART-C not UART-A. + +config BOARD_ATSTK1002_SW3_CUSTOM + bool "SW3: use TIMER1 (not SSC0 and GCLK)" + help + This also prevents using the external DAC as an audio interface. + +config BOARD_ATSTK1002_SW4_CUSTOM + bool "SW4: use ISI/Camera (not GPIOs, SPI1, and PS2-B)" + help + To use the camera interface you'll need a custom card (on the + PCI-format connector) connect a video sensor. + +config BOARD_ATSTK1002_SW5_CUSTOM + bool "SW5: use MACB1 (not LCDC)" + +config BOARD_ATSTK1002_SW6_CUSTOM + bool "SW6: more GPIOs (not MACB0)" + +endif # custom + +config BOARD_ATSTK1002_SPI1 + bool "Configure SPI1 controller" + depends on !BOARD_ATSTK1002_SW4_CUSTOM + help + All the signals for the second SPI controller are available on + GPIO lines and accessed through the J1 jumper block. Say "y" + here to configure that SPI controller. + +config BOARD_ATSTK1002_J2_LED + bool + default BOARD_ATSTK1002_J2_LED8 || BOARD_ATSTK1002_J2_RGB + +choice + prompt "LEDs connected to J2:" + depends on LEDS_GPIO && !BOARD_ATSTK1002_SW4_CUSTOM + optional + help + Select this if you have jumpered the J2 jumper block to the + LED0..LED7 amber leds, or to the RGB leds, using a ten-pin + IDC cable. A default "heartbeat" trigger is provided, but + you can of course override this. + +config BOARD_ATSTK1002_J2_LED8 + bool "LED0..LED7" + help + Select this if J2 is jumpered to LED0..LED7 amber leds. + +config BOARD_ATSTK1002_J2_RGB + bool "RGB leds" + help + Select this if J2 is jumpered to the RGB leds. + +endchoice + +endif # stk 1002 diff -x .git -Nur linux-2.6.22.1/arch/avr32/configs/atngw100_defconfig linux-avr32.git/arch/avr32/configs/atngw100_defconfig --- linux-2.6.22.1/arch/avr32/configs/atngw100_defconfig 2007-07-10 20:56:30.000000000 +0200 +++ linux-avr32.git/arch/avr32/configs/atngw100_defconfig 2007-07-12 13:59:49.000000000 +0200 @@ -712,7 +712,21 @@ # # LED devices # -# CONFIG_NEW_LEDS is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +CONFIG_LEDS_GPIO=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y + # # LED drivers diff -x .git -Nur linux-2.6.22.1/arch/avr32/drivers/dw-dmac.c linux-avr32.git/arch/avr32/drivers/dw-dmac.c --- linux-2.6.22.1/arch/avr32/drivers/dw-dmac.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-avr32.git/arch/avr32/drivers/dw-dmac.c 2007-06-06 11:33:46.000000000 +0200 @@ -0,0 +1,761 @@ +/* + * Driver for the Synopsys DesignWare DMA Controller + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dw-dmac.h" + +#define DMAC_NR_CHANNELS 3 +#define DMAC_MAX_BLOCKSIZE 4095 + +enum { + CH_STATE_FREE = 0, + CH_STATE_ALLOCATED, + CH_STATE_BUSY, +}; + +struct dw_dma_lli { + dma_addr_t sar; + dma_addr_t dar; + dma_addr_t llp; + u32 ctllo; + u32 ctlhi; + u32 sstat; + u32 dstat; +}; + +struct dw_dma_block { + struct dw_dma_lli *lli_vaddr; + dma_addr_t lli_dma_addr; +}; + +struct dw_dma_channel { + unsigned int state; + int is_cyclic; + struct dma_request_sg *req_sg; + struct dma_request_cyclic *req_cyclic; + unsigned int nr_blocks; + int direction; + struct dw_dma_block *block; +}; + +struct dw_dma_controller { + spinlock_t lock; + void * __iomem regs; + struct dma_pool *lli_pool; + struct clk *hclk; + struct dma_controller dma; + struct dw_dma_channel channel[DMAC_NR_CHANNELS]; +}; +#define to_dw_dmac(dmac) container_of(dmac, struct dw_dma_controller, dma) + +#define dmac_writel_hi(dmac, reg, value) \ + __raw_writel((value), (dmac)->regs + DW_DMAC_##reg + 4) +#define dmac_readl_hi(dmac, reg) \ + __raw_readl((dmac)->regs + DW_DMAC_##reg + 4) +#define dmac_writel_lo(dmac, reg, value) \ + __raw_writel((value), (dmac)->regs + DW_DMAC_##reg) +#define dmac_readl_lo(dmac, reg) \ + __raw_readl((dmac)->regs + DW_DMAC_##reg) +#define dmac_chan_writel_hi(dmac, chan, reg, value) \ + __raw_writel((value), ((dmac)->regs + 0x58 * (chan) \ + + DW_DMAC_CHAN_##reg + 4)) +#define dmac_chan_readl_hi(dmac, chan, reg) \ + __raw_readl((dmac)->regs + 0x58 * (chan) + DW_DMAC_CHAN_##reg + 4) +#define dmac_chan_writel_lo(dmac, chan, reg, value) \ + __raw_writel((value), (dmac)->regs + 0x58 * (chan) + DW_DMAC_CHAN_##reg) +#define dmac_chan_readl_lo(dmac, chan, reg) \ + __raw_readl((dmac)->regs + 0x58 * (chan) + DW_DMAC_CHAN_##reg) +#define set_channel_bit(dmac, reg, chan) \ + dmac_writel_lo(dmac, reg, (1 << (chan)) | (1 << ((chan) + 8))) +#define clear_channel_bit(dmac, reg, chan) \ + dmac_writel_lo(dmac, reg, (0 << (chan)) | (1 << ((chan) + 8))) + +static int dmac_alloc_channel(struct dma_controller *_dmac) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + struct dw_dma_channel *chan; + unsigned long flags; + int i; + + spin_lock_irqsave(&dmac->lock, flags); + for (i = 0; i < DMAC_NR_CHANNELS; i++) + if (dmac->channel[i].state == CH_STATE_FREE) + break; + + if (i < DMAC_NR_CHANNELS) { + chan = &dmac->channel[i]; + chan->state = CH_STATE_ALLOCATED; + } else { + i = -EBUSY; + } + + spin_unlock_irqrestore(&dmac->lock, flags); + + return i; +} + +static void dmac_release_channel(struct dma_controller *_dmac, int channel) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + + BUG_ON(channel >= DMAC_NR_CHANNELS + || dmac->channel[channel].state != CH_STATE_ALLOCATED); + + dmac->channel[channel].state = CH_STATE_FREE; +} + +static struct dw_dma_block *allocate_blocks(struct dw_dma_controller *dmac, + unsigned int nr_blocks) +{ + struct dw_dma_block *block; + void *p; + unsigned int i; + + block = kmalloc(nr_blocks * sizeof(*block), + GFP_KERNEL); + if (unlikely(!block)) + return NULL; + + for (i = 0; i < nr_blocks; i++) { + p = dma_pool_alloc(dmac->lli_pool, GFP_KERNEL, + &block[i].lli_dma_addr); + block[i].lli_vaddr = p; + if (unlikely(!p)) + goto fail; + } + + return block; + +fail: + for (i = 0; i < nr_blocks; i++) { + if (!block[i].lli_vaddr) + break; + dma_pool_free(dmac->lli_pool, block[i].lli_vaddr, + block[i].lli_dma_addr); + } + kfree(block); + return NULL; +} + +static void cleanup_channel(struct dw_dma_controller *dmac, + struct dw_dma_channel *chan) +{ + unsigned int i; + + if (chan->nr_blocks > 1) { + for (i = 0; i < chan->nr_blocks; i++) + dma_pool_free(dmac->lli_pool, chan->block[i].lli_vaddr, + chan->block[i].lli_dma_addr); + kfree(chan->block); + } + + chan->state = CH_STATE_ALLOCATED; +} + +static int dmac_prepare_request_sg(struct dma_controller *_dmac, + struct dma_request_sg *req) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + struct dw_dma_channel *chan; + unsigned long ctlhi, ctllo, cfghi, cfglo; + unsigned long block_size; + unsigned int nr_blocks; + int ret, i, direction; + unsigned long flags; + + spin_lock_irqsave(&dmac->lock, flags); + + ret = -EINVAL; + if (req->req.channel >= DMAC_NR_CHANNELS + || dmac->channel[req->req.channel].state != CH_STATE_ALLOCATED + || req->block_size > DMAC_MAX_BLOCKSIZE) { + spin_unlock_irqrestore(&dmac->lock, flags); + return -EINVAL; + } + + chan = &dmac->channel[req->req.channel]; + chan->state = CH_STATE_BUSY; + chan->req_sg = req; + chan->is_cyclic = 0; + + /* + * We have marked the channel as busy, so no need to keep the + * lock as long as we only touch the channel-specific + * registers + */ + spin_unlock_irqrestore(&dmac->lock, flags); + + /* + * There may be limitations in the driver and/or the DMA + * controller that prevents us from sending a whole + * scatterlist item in one go. Taking this into account, + * calculate the number of block transfers we need to set up. + * + * FIXME: Let the peripheral driver know about the maximum + * block size we support. We really don't want to use a + * different block size than what was suggested by the + * peripheral. + * + * Each block will get its own Linked List Item (LLI) below. + */ + block_size = req->block_size; + nr_blocks = req->nr_blocks; + pr_debug("block_size %lu, nr_blocks %u nr_sg = %u\n", + block_size, nr_blocks, req->nr_sg); + + BUG_ON(nr_blocks == 0); + chan->nr_blocks = nr_blocks; + + ret = -EINVAL; + cfglo = cfghi = 0; + switch (req->direction) { + case DMA_DIR_MEM_TO_PERIPH: + direction = DMA_TO_DEVICE; + cfghi = req->periph_id << (43 - 32); + break; + + case DMA_DIR_PERIPH_TO_MEM: + direction = DMA_FROM_DEVICE; + cfghi = req->periph_id << (39 - 32); + break; + default: + goto out_unclaim_channel; + } + + chan->direction = direction; + + dmac_chan_writel_hi(dmac, req->req.channel, CFG, cfghi); + dmac_chan_writel_lo(dmac, req->req.channel, CFG, cfglo); + + ctlhi = block_size >> req->width; + ctllo = ((req->direction << 20) + // | (1 << 14) | (1 << 11) // source/dest burst trans len + | (req->width << 4) | (req->width << 1) + | (1 << 0)); // interrupt enable + + if (nr_blocks == 1) { + /* Only one block: No need to use block chaining */ + if (direction == DMA_TO_DEVICE) { + dmac_chan_writel_lo(dmac, req->req.channel, SAR, + req->sg->dma_address); + dmac_chan_writel_lo(dmac, req->req.channel, DAR, + req->data_reg); + ctllo |= 2 << 7; // no dst increment + } else { + dmac_chan_writel_lo(dmac, req->req.channel, SAR, + req->data_reg); + dmac_chan_writel_lo(dmac, req->req.channel, DAR, + req->sg->dma_address); + ctllo |= 2 << 9; // no src increment + } + dmac_chan_writel_lo(dmac, req->req.channel, CTL, ctllo); + dmac_chan_writel_hi(dmac, req->req.channel, CTL, ctlhi); + pr_debug("ctl hi:lo 0x%lx:%lx\n", ctlhi, ctllo); + } else { + struct dw_dma_lli *lli, *lli_prev = NULL; + int j = 0, offset = 0; + + ret = -ENOMEM; + chan->block = allocate_blocks(dmac, nr_blocks); + if (!chan->block) + goto out_unclaim_channel; + + if (direction == DMA_TO_DEVICE) + ctllo |= 1 << 28 | 1 << 27 | 2 << 7; + else + ctllo |= 1 << 28 | 1 << 27 | 2 << 9; + + /* + * Map scatterlist items to blocks. One scatterlist + * item may need more than one block for the reasons + * mentioned above. + */ + for (i = 0; i < nr_blocks; i++) { + lli = chan->block[i].lli_vaddr; + if (lli_prev) { + lli_prev->llp = chan->block[i].lli_dma_addr; + pr_debug("lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n", + i - 1, chan->block[i - 1].lli_vaddr, + chan->block[i - 1].lli_dma_addr, + lli_prev->sar, lli_prev->dar, lli_prev->llp, + lli_prev->ctllo, lli_prev->ctlhi); + } + lli->llp = 0; + lli->ctllo = ctllo; + lli->ctlhi = ctlhi; + if (direction == DMA_TO_DEVICE) { + lli->sar = req->sg[j].dma_address + offset; + lli->dar = req->data_reg; + } else { + lli->sar = req->data_reg; + lli->dar = req->sg[j].dma_address + offset; + } + lli_prev = lli; + + offset += block_size; + if (offset > req->sg[j].length) { + j++; + offset = 0; + } + } + + pr_debug("lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n", + i - 1, chan->block[i - 1].lli_vaddr, + chan->block[i - 1].lli_dma_addr, lli_prev->sar, + lli_prev->dar, lli_prev->llp, + lli_prev->ctllo, lli_prev->ctlhi); + + /* + * SAR, DAR and CTL are initialized from the LLI. We + * only have to enable the LLI bits in CTL. + */ + dmac_chan_writel_hi(dmac, req->req.channel, CTL, 0); + dmac_chan_writel_lo(dmac, req->req.channel, LLP, + chan->block[0].lli_dma_addr); + dmac_chan_writel_lo(dmac, req->req.channel, CTL, 1 << 28 | 1 << 27); + } + + set_channel_bit(dmac, MASK_XFER, req->req.channel); + set_channel_bit(dmac, MASK_ERROR, req->req.channel); + if (req->req.block_complete) + set_channel_bit(dmac, MASK_BLOCK, req->req.channel); + else + clear_channel_bit(dmac, MASK_BLOCK, req->req.channel); + + return 0; + +out_unclaim_channel: + chan->state = CH_STATE_ALLOCATED; + return ret; +} + +static int dmac_prepare_request_cyclic(struct dma_controller *_dmac, + struct dma_request_cyclic *req) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + struct dw_dma_channel *chan; + unsigned long ctlhi, ctllo, cfghi, cfglo; + unsigned long block_size; + int ret, i, direction; + unsigned long flags; + + spin_lock_irqsave(&dmac->lock, flags); + + block_size = (req->buffer_size/req->periods) >> req->width; + + ret = -EINVAL; + if (req->req.channel >= DMAC_NR_CHANNELS + || dmac->channel[req->req.channel].state != CH_STATE_ALLOCATED + || (req->periods == 0) + || block_size > DMAC_MAX_BLOCKSIZE) { + spin_unlock_irqrestore(&dmac->lock, flags); + return -EINVAL; + } + + chan = &dmac->channel[req->req.channel]; + chan->state = CH_STATE_BUSY; + chan->is_cyclic = 1; + chan->req_cyclic = req; + + /* + * We have marked the channel as busy, so no need to keep the + * lock as long as we only touch the channel-specific + * registers + */ + spin_unlock_irqrestore(&dmac->lock, flags); + + /* + Setup + */ + BUG_ON(req->buffer_size % req->periods); + /* printk(KERN_INFO "block_size = %lu, periods = %u\n", block_size, req->periods); */ + + chan->nr_blocks = req->periods; + + ret = -EINVAL; + cfglo = cfghi = 0; + switch (req->direction) { + case DMA_DIR_MEM_TO_PERIPH: + direction = DMA_TO_DEVICE; + cfghi = req->periph_id << (43 - 32); + break; + + case DMA_DIR_PERIPH_TO_MEM: + direction = DMA_FROM_DEVICE; + cfghi = req->periph_id << (39 - 32); + break; + default: + goto out_unclaim_channel; + } + + chan->direction = direction; + + dmac_chan_writel_hi(dmac, req->req.channel, CFG, cfghi); + dmac_chan_writel_lo(dmac, req->req.channel, CFG, cfglo); + + ctlhi = block_size; + ctllo = ((req->direction << 20) + | (req->width << 4) | (req->width << 1) + | (1 << 0)); // interrupt enable + + { + struct dw_dma_lli *lli = NULL, *lli_prev = NULL; + + ret = -ENOMEM; + chan->block = allocate_blocks(dmac, req->periods); + if (!chan->block) + goto out_unclaim_channel; + + if (direction == DMA_TO_DEVICE) + ctllo |= 1 << 28 | 1 << 27 | 2 << 7; + else + ctllo |= 1 << 28 | 1 << 27 | 2 << 9; + + /* + * Set up a linked list items where each period gets + * an item. The linked list item for the last period + * points back to the star of the buffer making a + * cyclic buffer. + */ + for (i = 0; i < req->periods; i++) { + lli = chan->block[i].lli_vaddr; + if (lli_prev) { + lli_prev->llp = chan->block[i].lli_dma_addr; + /* printk(KERN_INFO "lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n", + i - 1, chan->block[i - 1].lli_vaddr, + chan->block[i - 1].lli_dma_addr, + lli_prev->sar, lli_prev->dar, lli_prev->llp, + lli_prev->ctllo, lli_prev->ctlhi);*/ + } + lli->llp = 0; + lli->ctllo = ctllo; + lli->ctlhi = ctlhi; + if (direction == DMA_TO_DEVICE) { + lli->sar = req->buffer_start + i*(block_size << req->width); + lli->dar = req->data_reg; + } else { + lli->sar = req->data_reg; + lli->dar = req->buffer_start + i*(block_size << req->width); + } + lli_prev = lli; + } + lli->llp = chan->block[0].lli_dma_addr; + + /*printk(KERN_INFO "lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n", + i - 1, chan->block[i - 1].lli_vaddr, + chan->block[i - 1].lli_dma_addr, lli_prev->sar, + lli_prev->dar, lli_prev->llp, + lli_prev->ctllo, lli_prev->ctlhi); */ + + /* + * SAR, DAR and CTL are initialized from the LLI. We + * only have to enable the LLI bits in CTL. + */ + dmac_chan_writel_lo(dmac, req->req.channel, LLP, + chan->block[0].lli_dma_addr); + dmac_chan_writel_lo(dmac, req->req.channel, CTL, 1 << 28 | 1 << 27); + } + + clear_channel_bit(dmac, MASK_XFER, req->req.channel); + set_channel_bit(dmac, MASK_ERROR, req->req.channel); + if (req->req.block_complete) + set_channel_bit(dmac, MASK_BLOCK, req->req.channel); + else + clear_channel_bit(dmac, MASK_BLOCK, req->req.channel); + + return 0; + +out_unclaim_channel: + chan->state = CH_STATE_ALLOCATED; + return ret; +} + +static int dmac_start_request(struct dma_controller *_dmac, + unsigned int channel) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + + BUG_ON(channel >= DMAC_NR_CHANNELS); + + set_channel_bit(dmac, CH_EN, channel); + + return 0; +} + +static dma_addr_t dmac_get_current_pos(struct dma_controller *_dmac, + unsigned int channel) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + struct dw_dma_channel *chan; + dma_addr_t current_pos; + + BUG_ON(channel >= DMAC_NR_CHANNELS); + + chan = &dmac->channel[channel]; + + switch (chan->direction) { + case DMA_TO_DEVICE: + current_pos = dmac_chan_readl_lo(dmac, channel, SAR); + break; + case DMA_FROM_DEVICE: + current_pos = dmac_chan_readl_lo(dmac, channel, DAR); + break; + default: + return 0; + } + + + if (!current_pos) { + if (chan->is_cyclic) { + current_pos = chan->req_cyclic->buffer_start; + } else { + current_pos = chan->req_sg->sg->dma_address; + } + } + + return current_pos; +} + + +static int dmac_stop_request(struct dma_controller *_dmac, + unsigned int channel) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + struct dw_dma_channel *chan; + + BUG_ON(channel >= DMAC_NR_CHANNELS); + + chan = &dmac->channel[channel]; + pr_debug("stop: st%u s%08x d%08x l%08x ctl0x%08x:0x%08x\n", + chan->state, dmac_chan_readl_lo(dmac, channel, SAR), + dmac_chan_readl_lo(dmac, channel, DAR), + dmac_chan_readl_lo(dmac, channel, LLP), + dmac_chan_readl_hi(dmac, channel, CTL), + dmac_chan_readl_lo(dmac, channel, CTL)); + + if (chan->state == CH_STATE_BUSY) { + clear_channel_bit(dmac, CH_EN, channel); + cleanup_channel(dmac, &dmac->channel[channel]); + } + + return 0; +} + + +static void dmac_block_complete(struct dw_dma_controller *dmac) +{ + struct dw_dma_channel *chan; + unsigned long status, chanid; + + status = dmac_readl_lo(dmac, STATUS_BLOCK); + + while (status) { + struct dma_request *req; + chanid = __ffs(status); + chan = &dmac->channel[chanid]; + + if (chan->is_cyclic) { + BUG_ON(!chan->req_cyclic + || !chan->req_cyclic->req.block_complete); + req = &chan->req_cyclic->req; + } else { + BUG_ON(!chan->req_sg || !chan->req_sg->req.block_complete); + req = &chan->req_sg->req; + } + dmac_writel_lo(dmac, CLEAR_BLOCK, 1 << chanid); + req->block_complete(req); + status = dmac_readl_lo(dmac, STATUS_BLOCK); + } +} + +static void dmac_xfer_complete(struct dw_dma_controller *dmac) +{ + struct dw_dma_channel *chan; + struct dma_request *req; + unsigned long status, chanid; + + status = dmac_readl_lo(dmac, STATUS_XFER); + + while (status) { + chanid = __ffs(status); + chan = &dmac->channel[chanid]; + + dmac_writel_lo(dmac, CLEAR_XFER, 1 << chanid); + + req = &chan->req_sg->req; + BUG_ON(!req); + cleanup_channel(dmac, chan); + if (req->xfer_complete) + req->xfer_complete(req); + + status = dmac_readl_lo(dmac, STATUS_XFER); + } +} + +static void dmac_error(struct dw_dma_controller *dmac) +{ + struct dw_dma_channel *chan; + unsigned long status, chanid; + + status = dmac_readl_lo(dmac, STATUS_ERROR); + + while (status) { + struct dma_request *req; + + chanid = __ffs(status); + chan = &dmac->channel[chanid]; + + dmac_writel_lo(dmac, CLEAR_ERROR, 1 << chanid); + clear_channel_bit(dmac, CH_EN, chanid); + + if (chan->is_cyclic) { + BUG_ON(!chan->req_cyclic); + req = &chan->req_cyclic->req; + } else { + BUG_ON(!chan->req_sg); + req = &chan->req_sg->req; + } + + cleanup_channel(dmac, chan); + if (req->error) + req->error(req); + + status = dmac_readl_lo(dmac, STATUS_XFER); + } +} + +static irqreturn_t dmac_interrupt(int irq, void *dev_id) +{ + struct dw_dma_controller *dmac = dev_id; + unsigned long status; + int ret = IRQ_NONE; + + spin_lock(&dmac->lock); + + status = dmac_readl_lo(dmac, STATUS_INT); + + while (status) { + ret = IRQ_HANDLED; + if (status & 0x10) + dmac_error(dmac); + if (status & 0x02) + dmac_block_complete(dmac); + if (status & 0x01) + dmac_xfer_complete(dmac); + + status = dmac_readl_lo(dmac, STATUS_INT); + } + + spin_unlock(&dmac->lock); + return ret; +} + +static int __devinit dmac_probe(struct platform_device *pdev) +{ + struct dw_dma_controller *dmac; + struct resource *regs; + int ret; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + + dmac = kmalloc(sizeof(*dmac), GFP_KERNEL); + if (!dmac) + return -ENOMEM; + memset(dmac, 0, sizeof(*dmac)); + + dmac->hclk = clk_get(&pdev->dev, "hclk"); + if (IS_ERR(dmac->hclk)) { + ret = PTR_ERR(dmac->hclk); + goto out_free_dmac; + } + clk_enable(dmac->hclk); + + ret = -ENOMEM; + dmac->lli_pool = dma_pool_create("dmac", &pdev->dev, + sizeof(struct dw_dma_lli), 4, 0); + if (!dmac->lli_pool) + goto out_disable_clk; + + spin_lock_init(&dmac->lock); + dmac->dma.dev = &pdev->dev; + dmac->dma.alloc_channel = dmac_alloc_channel; + dmac->dma.release_channel = dmac_release_channel; + dmac->dma.prepare_request_sg = dmac_prepare_request_sg; + dmac->dma.prepare_request_cyclic = dmac_prepare_request_cyclic; + dmac->dma.start_request = dmac_start_request; + dmac->dma.stop_request = dmac_stop_request; + dmac->dma.get_current_pos = dmac_get_current_pos; + + dmac->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!dmac->regs) + goto out_free_pool; + + ret = request_irq(platform_get_irq(pdev, 0), dmac_interrupt, + IRQF_SAMPLE_RANDOM, pdev->name, dmac); + if (ret) + goto out_unmap_regs; + + /* Enable the DMA controller */ + dmac_writel_lo(dmac, CFG, 1); + + register_dma_controller(&dmac->dma); + + printk(KERN_INFO + "dmac%d: DesignWare DMA controller at 0x%p irq %d\n", + dmac->dma.id, dmac->regs, platform_get_irq(pdev, 0)); + + return 0; + +out_unmap_regs: + iounmap(dmac->regs); +out_free_pool: + dma_pool_destroy(dmac->lli_pool); +out_disable_clk: + clk_disable(dmac->hclk); + clk_put(dmac->hclk); +out_free_dmac: + kfree(dmac); + return ret; +} + +static struct platform_driver dmac_driver = { + .probe = dmac_probe, + .driver = { + .name = "dmaca", + }, +}; + +static int __init dmac_init(void) +{ + return platform_driver_register(&dmac_driver); +} +subsys_initcall(dmac_init); + +static void __exit dmac_exit(void) +{ + platform_driver_unregister(&dmac_driver); +} +module_exit(dmac_exit); + +MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver"); +MODULE_AUTHOR("Haavard Skinnemoen "); +MODULE_LICENSE("GPL"); diff -x .git -Nur linux-2.6.22.1/arch/avr32/drivers/dw-dmac.h linux-avr32.git/arch/avr32/drivers/dw-dmac.h --- linux-2.6.22.1/arch/avr32/drivers/dw-dmac.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-avr32.git/arch/avr32/drivers/dw-dmac.h 2007-06-06 11:33:46.000000000 +0200 @@ -0,0 +1,42 @@ +/* + * Driver for the Synopsys DesignWare DMA Controller + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __AVR32_DW_DMAC_H__ +#define __AVR32_DW_DMAC_H__ + +#define DW_DMAC_CFG 0x398 +#define DW_DMAC_CH_EN 0x3a0 + +#define DW_DMAC_STATUS_XFER 0x2e8 +#define DW_DMAC_STATUS_BLOCK 0x2f0 +#define DW_DMAC_STATUS_ERROR 0x308 + +#define DW_DMAC_MASK_XFER 0x310 +#define DW_DMAC_MASK_BLOCK 0x318 +#define DW_DMAC_MASK_ERROR 0x330 + +#define DW_DMAC_CLEAR_XFER 0x338 +#define DW_DMAC_CLEAR_BLOCK 0x340 +#define DW_DMAC_CLEAR_ERROR 0x358 + +#define DW_DMAC_STATUS_INT 0x360 + +#define DW_DMAC_CHAN_SAR 0x000 +#define DW_DMAC_CHAN_DAR 0x008 +#define DW_DMAC_CHAN_LLP 0x010 +#define DW_DMAC_CHAN_CTL 0x018 +#define DW_DMAC_CHAN_SSTAT 0x020 +#define DW_DMAC_CHAN_DSTAT 0x028 +#define DW_DMAC_CHAN_SSTATAR 0x030 +#define DW_DMAC_CHAN_DSTATAR 0x038 +#define DW_DMAC_CHAN_CFG 0x040 +#define DW_DMAC_CHAN_SGR 0x048 +#define DW_DMAC_CHAN_DSR 0x050 + +#endif /* __AVR32_DW_DMAC_H__ */ diff -x .git -Nur linux-2.6.22.1/arch/avr32/drivers/Makefile linux-avr32.git/arch/avr32/drivers/Makefile --- linux-2.6.22.1/arch/avr32/drivers/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-avr32.git/arch/avr32/drivers/Makefile 2007-06-06 11:33:46.000000000 +0200 @@ -0,0 +1 @@ +obj-$(CONFIG_DW_DMAC) += dw-dmac.o diff -x .git -Nur linux-2.6.22.1/arch/avr32/Kconfig linux-avr32.git/arch/avr32/Kconfig --- linux-2.6.22.1/arch/avr32/Kconfig 2007-07-10 20:56:30.000000000 +0200 +++ linux-avr32.git/arch/avr32/Kconfig 2007-07-12 13:59:49.000000000 +0200 @@ -113,6 +113,10 @@ bool "ATNGW100 Network Gateway" endchoice +if BOARD_ATSTK1000 +source "arch/avr32/boards/atstk1000/Kconfig" +endif + choice prompt "Boot loader type" default LOADER_U_BOOT @@ -171,6 +175,10 @@ enabling Nexus-compliant debuggers to keep track of the PID of the currently executing task. +config DW_DMAC + tristate "Synopsys DesignWare DMA Controller support" + default y if CPU_AT32AP7000 + # FPU emulation goes here source "kernel/Kconfig.hz" @@ -185,6 +193,27 @@ endmenu +menu "Power managment options" + +menu "CPU Frequency scaling" + +source "drivers/cpufreq/Kconfig" + +config CPU_FREQ_AT32AP + bool "CPU frequency driver for AT32AP" + depends on CPU_FREQ && PLATFORM_AT32AP + default n + help + This enables the CPU frequency driver for AT32AP processors. + + For details, take a look in . + + If in doubt, say N. + +endmenu + +endmenu + menu "Bus options" config PCI diff -x .git -Nur linux-2.6.22.1/arch/avr32/kernel/dma-controller.c linux-avr32.git/arch/avr32/kernel/dma-controller.c --- linux-2.6.22.1/arch/avr32/kernel/dma-controller.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-avr32.git/arch/avr32/kernel/dma-controller.c 2007-06-06 11:33:46.000000000 +0200 @@ -0,0 +1,34 @@ +/* + * Preliminary DMA controller framework for AVR32 + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include + +static LIST_HEAD(controllers); + +int register_dma_controller(struct dma_controller *dmac) +{ + static int next_id; + + dmac->id = next_id++; + list_add_tail(&dmac->list, &controllers); + + return 0; +} +EXPORT_SYMBOL(register_dma_controller); + +struct dma_controller *find_dma_controller(int id) +{ + struct dma_controller *dmac; + + list_for_each_entry(dmac, &controllers, list) + if (dmac->id == id) + return dmac; + return NULL; +} +EXPORT_SYMBOL(find_dma_controller); diff -x .git -Nur linux-2.6.22.1/arch/avr32/kernel/Makefile linux-avr32.git/arch/avr32/kernel/Makefile --- linux-2.6.22.1/arch/avr32/kernel/Makefile 2007-07-10 20:56:30.000000000 +0200 +++ linux-avr32.git/arch/avr32/kernel/Makefile 2007-06-06 11:33:46.000000000 +0200 @@ -9,6 +9,7 @@ obj-y += setup.o traps.o semaphore.o ptrace.o obj-y += signal.o sys_avr32.o process.o time.o obj-y += init_task.o switch_to.o cpu.o +obj-y += dma-controller.o obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o obj-$(CONFIG_KPROBES) += kprobes.o diff -x .git -Nur linux-2.6.22.1/arch/avr32/mach-at32ap/at32ap7000.c linux-avr32.git/arch/avr32/mach-at32ap/at32ap7000.c --- linux-2.6.22.1/arch/avr32/mach-at32ap/at32ap7000.c 2007-07-10 20:56:30.000000000 +0200 +++ linux-avr32.git/arch/avr32/mach-at32ap/at32ap7000.c 2007-07-12 13:59:49.000000000 +0200 @@ -17,14 +17,20 @@ #include #include #include -#include #include