From b788ed5c97c2bae978cb84a95ea37a1aa6458ea8 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Sun, 8 Jun 2008 23:16:24 +0200 Subject: [PATCH 01/31] avr32: Provide PCI DMA mapping API Some non-PCI drivers need the PCI variant of the DMA mapping API. Include to provide this through the non-PCI DMA mapping API. Signed-off-by: Haavard Skinnemoen --- include/asm-avr32/pci.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/asm-avr32/pci.h b/include/asm-avr32/pci.h index 0f5f134b896a..a32a02372017 100644 --- a/include/asm-avr32/pci.h +++ b/include/asm-avr32/pci.h @@ -5,4 +5,6 @@ #define PCI_DMA_BUS_IS_PHYS (1) +#include + #endif /* __ASM_AVR32_PCI_H__ */ From 8bd8974fcddc468d66bd67f33c578f37987b302e Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Sun, 8 Jun 2008 23:28:45 +0200 Subject: [PATCH 02/31] avr32: export empty_zero_page Fixes one of two ext4 build problems: ERROR: "empty_zero_page" [fs/ext4/ext4dev.ko] undefined! Signed-off-by: Haavard Skinnemoen --- arch/avr32/mm/init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 0e64ddc45e37..5ee1e407dcf7 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); pgd_t swapper_pg_dir[PTRS_PER_PGD]; struct page *empty_zero_page; +EXPORT_SYMBOL(empty_zero_page); /* * Cache of MMU context last used. From 60ed7951d0c9bf8de8588384134f16474367b410 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 17 Mar 2008 14:55:06 +0100 Subject: [PATCH 03/31] avr32: Allow board to define oscillator rates On our custom board we have other oscillator rates than on atngw100 and atstk100x. Currently these rates are hardcoded in arch/avr32/mach-at32ap/at32ap700x.c. This patch moves them into board specific code. Signed-off-by: Alex Raimondi Signed-off-by: Haavard Skinnemoen --- arch/avr32/boards/atngw100/setup.c | 7 +++++++ arch/avr32/boards/atstk1000/atstk1002.c | 6 ++++++ arch/avr32/boards/atstk1000/atstk1003.c | 7 +++++++ arch/avr32/boards/atstk1000/atstk1004.c | 7 +++++++ arch/avr32/mach-at32ap/at32ap700x.c | 9 +-------- include/asm-avr32/arch-at32ap/board.h | 6 ++++++ 6 files changed, 34 insertions(+), 8 deletions(-) diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c index a398be284966..00ce961c52a5 100644 --- a/arch/avr32/boards/atngw100/setup.c +++ b/arch/avr32/boards/atngw100/setup.c @@ -25,6 +25,13 @@ #include #include +/* Oscillator frequencies. These are board-specific */ +unsigned long at32_board_osc_rates[3] = { + [0] = 32768, /* 32.768 kHz on RTC osc */ + [1] = 20000000, /* 20 MHz on osc0 */ + [2] = 12000000, /* 12 MHz on osc1 */ +}; + /* Initialized by bootloader-specific startup code. */ struct tag *bootloader_tags __initdata; diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index 000eb4220a12..0e1332686867 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -28,6 +28,12 @@ #include "atstk1000.h" +/* Oscillator frequencies. These are board specific */ +unsigned long at32_board_osc_rates[3] = { + [0] = 32768, /* 32.768 kHz on RTC osc */ + [1] = 20000000, /* 20 MHz on osc0 */ + [2] = 12000000, /* 12 MHz on osc1 */ +}; struct eth_addr { u8 addr[6]; diff --git a/arch/avr32/boards/atstk1000/atstk1003.c b/arch/avr32/boards/atstk1000/atstk1003.c index a0b223df35a2..ea109f435a83 100644 --- a/arch/avr32/boards/atstk1000/atstk1003.c +++ b/arch/avr32/boards/atstk1000/atstk1003.c @@ -27,6 +27,13 @@ #include "atstk1000.h" +/* Oscillator frequencies. These are board specific */ +unsigned long at32_board_osc_rates[3] = { + [0] = 32768, /* 32.768 kHz on RTC osc */ + [1] = 20000000, /* 20 MHz on osc0 */ + [2] = 12000000, /* 12 MHz on osc1 */ +}; + #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC static struct at73c213_board_info at73c213_data = { .ssc_id = 0, diff --git a/arch/avr32/boards/atstk1000/atstk1004.c b/arch/avr32/boards/atstk1000/atstk1004.c index e765a8652b3e..cbf09dd0db53 100644 --- a/arch/avr32/boards/atstk1000/atstk1004.c +++ b/arch/avr32/boards/atstk1000/atstk1004.c @@ -29,6 +29,13 @@ #include "atstk1000.h" +/* Oscillator frequencies. These are board specific */ +unsigned long at32_board_osc_rates[3] = { + [0] = 32768, /* 32.768 kHz on RTC osc */ + [1] = 20000000, /* 20 MHz on osc0 */ + [2] = 12000000, /* 12 MHz on osc1 */ +}; + #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC static struct at73c213_board_info at73c213_data = { .ssc_id = 0, diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 0f24b4f85c17..34b56de4ea60 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -93,19 +93,12 @@ static struct clk devname##_##_name = { \ static DEFINE_SPINLOCK(pm_lock); -unsigned long at32ap7000_osc_rates[3] = { - [0] = 32768, - /* FIXME: these are ATSTK1002-specific */ - [1] = 20000000, - [2] = 12000000, -}; - static struct clk osc0; static struct clk osc1; static unsigned long osc_get_rate(struct clk *clk) { - return at32ap7000_osc_rates[clk->index]; + return at32_board_osc_rates[clk->index]; } static unsigned long pll_get_rate(struct clk *clk, unsigned long control) diff --git a/include/asm-avr32/arch-at32ap/board.h b/include/asm-avr32/arch-at32ap/board.h index a4e2d28bfb58..cfd3060d5b26 100644 --- a/include/asm-avr32/arch-at32ap/board.h +++ b/include/asm-avr32/arch-at32ap/board.h @@ -8,6 +8,12 @@ #define GPIO_PIN_NONE (-1) +/* + * Clock rates for various on-board oscillators. The number of entries + * in this array is chip-dependent. + */ +extern unsigned long at32_board_osc_rates[]; + /* Add basic devices: system manager, interrupt controller, portmuxes, etc. */ void at32_add_system_devices(void); From 7c1b90a1e964f72bde88511e5cfe1c04318ff3d1 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Wed, 28 May 2008 20:50:07 +0200 Subject: [PATCH 04/31] avr32: Fix sigaltstack behaviour A signal handler should be able to change the signal stack used for the next signal by altering the ucontext_t passed as a parameter to the handler. This does not currently work on avr32 since it doesn't update the in-kernel signal context from the ucontext_t upon signal handler return. Fix it by adding a call to do_sigaltstack() from sys_rt_sigreturn(), bringing it in line with most other architectures. Signed-off-by: Martin Koegler [haavard.skinnemoen@atmel.com: changed patch description] Signed-off-by: Haavard Skinnemoen --- arch/avr32/kernel/signal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/avr32/kernel/signal.c b/arch/avr32/kernel/signal.c index 5616a00c10ba..c5b11f9067f1 100644 --- a/arch/avr32/kernel/signal.c +++ b/arch/avr32/kernel/signal.c @@ -93,6 +93,9 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) goto badframe; + if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->sp) == -EFAULT) + goto badframe; + pr_debug("Context restored: pc = %08lx, lr = %08lx, sp = %08lx\n", regs->pc, regs->lr, regs->sp); From c1f24ac99f3711a6caa0e1d1c01a071ed72a7e0b Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 9 Jun 2008 14:16:06 +0200 Subject: [PATCH 05/31] avr32: Fix wrong I/O access size in __raw_readsb __raw_readsb() should always use byte accesses, never halfword accesses, to I/O memory. Signed-off-by: Haavard Skinnemoen --- arch/avr32/lib/io-readsb.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/avr32/lib/io-readsb.S b/arch/avr32/lib/io-readsb.S index 2be5da7ed26b..cb2d86945559 100644 --- a/arch/avr32/lib/io-readsb.S +++ b/arch/avr32/lib/io-readsb.S @@ -41,7 +41,7 @@ __raw_readsb: 2: sub r10, -4 reteq r12 -3: ld.uh r8, r12[0] +3: ld.ub r8, r12[0] sub r10, 1 st.b r11++, r8 brne 3b From aafafddb01e259cff61b3d3f5b3466f0a6a65b84 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 10 Jun 2008 13:55:52 +0200 Subject: [PATCH 06/31] avr32: minor GPIO handling updates On the odd chance some code uses a pin as a GPIO IRQ without calling gpio_request() or gpio_direction_input(), the debug dump should still show its pin status. Signed-off-by: David Brownell Signed-off-by: Haavard Skinnemoen --- arch/avr32/mach-at32ap/pio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index 38a8fa31c0b5..60da03ba7117 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c @@ -318,6 +318,8 @@ static void pio_bank_show(struct seq_file *s, struct gpio_chip *chip) const char *label; label = gpiochip_is_requested(chip, i); + if (!label && (imr & mask)) + label = "[irq]"; if (!label) continue; From 47882cf620bcd7b014f6f7fc11be8c045787adac Mon Sep 17 00:00:00 2001 From: Hans-Christian Egtvedt Date: Tue, 5 Feb 2008 15:27:16 +0100 Subject: [PATCH 07/31] avr32: Add pin configuration choice to LCDC peripheral This patch lets the board code choose which pin out to use for the LCD interface. On AT32AP7000 the LCDC is wired to two sets of pins, which lets the user choose between dual ethernet and 32-bit EBI. For the ATNGW100 board it is vital to have the choice to select the alternative pinout since this pinout is routed to the external headers. Update ATSTK1002 and ATSTK1004 to use the new interface. Signed-off-by: Hans-Christian Egtvedt Signed-off-by: Haavard Skinnemoen --- arch/avr32/boards/atstk1000/atstk1002.c | 2 +- arch/avr32/boards/atstk1000/atstk1004.c | 2 +- arch/avr32/mach-at32ap/at32ap700x.c | 105 ++++++++++++++++-------- include/asm-avr32/arch-at32ap/board.h | 3 +- 4 files changed, 77 insertions(+), 35 deletions(-) diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index 0e1332686867..86b363c1c25b 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -238,7 +238,7 @@ static int __init atstk1002_init(void) set_hw_addr(at32_add_device_eth(1, ð_data[1])); #else at32_add_device_lcdc(0, &atstk1000_lcdc_data, - fbmem_start, fbmem_size); + fbmem_start, fbmem_size, 0); #endif at32_add_device_usba(0, NULL); #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM diff --git a/arch/avr32/boards/atstk1000/atstk1004.c b/arch/avr32/boards/atstk1000/atstk1004.c index cbf09dd0db53..c7236df74d74 100644 --- a/arch/avr32/boards/atstk1000/atstk1004.c +++ b/arch/avr32/boards/atstk1000/atstk1004.c @@ -140,7 +140,7 @@ static int __init atstk1004_init(void) at32_add_device_mci(0); #endif at32_add_device_lcdc(0, &atstk1000_lcdc_data, - fbmem_start, fbmem_size); + fbmem_start, fbmem_size, 0); at32_add_device_usba(0, NULL); #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM at32_add_device_ssc(0, ATMEL_SSC_TX); diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 34b56de4ea60..08115f9243fb 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1257,7 +1257,8 @@ static struct clk atmel_lcdfb0_pixclk = { struct platform_device *__init at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, - unsigned long fbmem_start, unsigned long fbmem_len) + unsigned long fbmem_start, unsigned long fbmem_len, + unsigned int pin_config) { struct platform_device *pdev; struct atmel_lcdfb_info *info; @@ -1284,37 +1285,77 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, switch (id) { case 0: pdev = &atmel_lcdfb0_device; - select_peripheral(PC(19), PERIPH_A, 0); /* CC */ - select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ - select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ - select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ - select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */ - select_peripheral(PC(24), PERIPH_A, 0); /* MODE */ - select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ - select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */ - select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */ - select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */ - select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */ - select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */ - select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ - select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ - select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ - select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */ - select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */ - select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */ - select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */ - select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */ - select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ - select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ - select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ - select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */ - select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */ - select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */ - select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */ - select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */ - select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */ - select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ - select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ + + switch (pin_config) { + case 0: + select_peripheral(PC(19), PERIPH_A, 0); /* CC */ + select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ + select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ + select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ + select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */ + select_peripheral(PC(24), PERIPH_A, 0); /* MODE */ + select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ + select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */ + select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */ + select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */ + select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */ + select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */ + select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ + select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ + select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ + select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */ + select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */ + select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */ + select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */ + select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */ + select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ + select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ + select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ + select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */ + select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */ + select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */ + select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */ + select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */ + select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */ + select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ + select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ + break; + case 1: + select_peripheral(PE(0), PERIPH_B, 0); /* CC */ + select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ + select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ + select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ + select_peripheral(PE(1), PERIPH_B, 0); /* DVAL */ + select_peripheral(PE(2), PERIPH_B, 0); /* MODE */ + select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ + select_peripheral(PE(3), PERIPH_B, 0); /* DATA0 */ + select_peripheral(PE(4), PERIPH_B, 0); /* DATA1 */ + select_peripheral(PE(5), PERIPH_B, 0); /* DATA2 */ + select_peripheral(PE(6), PERIPH_B, 0); /* DATA3 */ + select_peripheral(PE(7), PERIPH_B, 0); /* DATA4 */ + select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ + select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ + select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ + select_peripheral(PE(8), PERIPH_B, 0); /* DATA8 */ + select_peripheral(PE(9), PERIPH_B, 0); /* DATA9 */ + select_peripheral(PE(10), PERIPH_B, 0); /* DATA10 */ + select_peripheral(PE(11), PERIPH_B, 0); /* DATA11 */ + select_peripheral(PE(12), PERIPH_B, 0); /* DATA12 */ + select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ + select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ + select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ + select_peripheral(PE(13), PERIPH_B, 0); /* DATA16 */ + select_peripheral(PE(14), PERIPH_B, 0); /* DATA17 */ + select_peripheral(PE(15), PERIPH_B, 0); /* DATA18 */ + select_peripheral(PE(16), PERIPH_B, 0); /* DATA19 */ + select_peripheral(PE(17), PERIPH_B, 0); /* DATA20 */ + select_peripheral(PE(18), PERIPH_B, 0); /* DATA21 */ + select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ + select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ + break; + default: + goto err_invalid_id; + } clk_set_parent(&atmel_lcdfb0_pixclk, &pll0); clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0)); diff --git a/include/asm-avr32/arch-at32ap/board.h b/include/asm-avr32/arch-at32ap/board.h index cfd3060d5b26..a78206cdb3dc 100644 --- a/include/asm-avr32/arch-at32ap/board.h +++ b/include/asm-avr32/arch-at32ap/board.h @@ -42,7 +42,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n); struct atmel_lcdfb_info; struct platform_device * at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, - unsigned long fbmem_start, unsigned long fbmem_len); + unsigned long fbmem_start, unsigned long fbmem_len, + unsigned int pin_config); struct usba_platform_data; struct platform_device * From d86d314f67191011e6f494f6517b8cbd7dd3dc39 Mon Sep 17 00:00:00 2001 From: Hans-Christian Egtvedt Date: Mon, 25 Feb 2008 11:24:30 +0100 Subject: [PATCH 08/31] avr32: Add PSIF platform devices This patch adds the PS/2 interface (PSIF) to the device code, split into two platform devices, one for each port. The function for adding the PSIF platform device is also added to the board header file. Signed-off-by: Hans-Christian Egtvedt Signed-off-by: Haavard Skinnemoen --- arch/avr32/mach-at32ap/at32ap700x.c | 77 +++++++++++++++++++++++++++ include/asm-avr32/arch-at32ap/board.h | 1 + 2 files changed, 78 insertions(+) diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 08115f9243fb..a9ae7bb108b2 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -833,6 +833,81 @@ void __init at32_add_system_devices(void) platform_device_register(&pio4_device); } +/* -------------------------------------------------------------------- + * PSIF + * -------------------------------------------------------------------- */ +static struct resource atmel_psif0_resource[] __initdata = { + { + .start = 0xffe03c00, + .end = 0xffe03cff, + .flags = IORESOURCE_MEM, + }, + IRQ(18), +}; +static struct clk atmel_psif0_pclk = { + .name = "pclk", + .parent = &pba_clk, + .mode = pba_clk_mode, + .get_rate = pba_clk_get_rate, + .index = 15, +}; + +static struct resource atmel_psif1_resource[] __initdata = { + { + .start = 0xffe03d00, + .end = 0xffe03dff, + .flags = IORESOURCE_MEM, + }, + IRQ(18), +}; +static struct clk atmel_psif1_pclk = { + .name = "pclk", + .parent = &pba_clk, + .mode = pba_clk_mode, + .get_rate = pba_clk_get_rate, + .index = 15, +}; + +struct platform_device *__init at32_add_device_psif(unsigned int id) +{ + struct platform_device *pdev; + + if (!(id == 0 || id == 1)) + return NULL; + + pdev = platform_device_alloc("atmel_psif", id); + if (!pdev) + return NULL; + + switch (id) { + case 0: + if (platform_device_add_resources(pdev, atmel_psif0_resource, + ARRAY_SIZE(atmel_psif0_resource))) + goto err_add_resources; + atmel_psif0_pclk.dev = &pdev->dev; + select_peripheral(PA(8), PERIPH_A, 0); /* CLOCK */ + select_peripheral(PA(9), PERIPH_A, 0); /* DATA */ + break; + case 1: + if (platform_device_add_resources(pdev, atmel_psif1_resource, + ARRAY_SIZE(atmel_psif1_resource))) + goto err_add_resources; + atmel_psif1_pclk.dev = &pdev->dev; + select_peripheral(PB(11), PERIPH_A, 0); /* CLOCK */ + select_peripheral(PB(12), PERIPH_A, 0); /* DATA */ + break; + default: + return NULL; + } + + platform_device_add(pdev); + return pdev; + +err_add_resources: + platform_device_put(pdev); + return NULL; +} + /* -------------------------------------------------------------------- * USART * -------------------------------------------------------------------- */ @@ -1934,6 +2009,8 @@ struct clk *at32_clock_list[] = { &pio4_mck, &at32_tcb0_t0_clk, &at32_tcb1_t0_clk, + &atmel_psif0_pclk, + &atmel_psif1_pclk, &atmel_usart0_usart, &atmel_usart1_usart, &atmel_usart2_usart, diff --git a/include/asm-avr32/arch-at32ap/board.h b/include/asm-avr32/arch-at32ap/board.h index a78206cdb3dc..b4cddfaca90e 100644 --- a/include/asm-avr32/arch-at32ap/board.h +++ b/include/asm-avr32/arch-at32ap/board.h @@ -80,6 +80,7 @@ struct platform_device *at32_add_device_twi(unsigned int id, struct platform_device *at32_add_device_mci(unsigned int id); struct platform_device *at32_add_device_ac97c(unsigned int id); struct platform_device *at32_add_device_abdac(unsigned int id); +struct platform_device *at32_add_device_psif(unsigned int id); struct cf_platform_data { int detect_pin; From 7ef31e9c4e711bfb817a15b54e428e5c4a7c0032 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 12 Jun 2008 12:18:05 -0700 Subject: [PATCH 09/31] avr32: improve NGW100 I2C/PMBus setup Basic I2C initialization for the NGW100 board: - Provide empty i2c device table. Daughtercards may add devices, and the ATtiny24 could do stuff too. - Set up EXTINT(3) so the ATtiny24 can interrupt the AP7000. Signed-off-by: David Brownell Signed-off-by: Haavard Skinnemoen --- arch/avr32/boards/atngw100/setup.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c index 00ce961c52a5..a51bb9fb3c89 100644 --- a/arch/avr32/boards/atngw100/setup.c +++ b/arch/avr32/boards/atngw100/setup.c @@ -9,6 +9,8 @@ */ #include #include +#include +#include #include #include #include @@ -147,6 +149,10 @@ static struct platform_device i2c_gpio_device = { }, }; +static struct i2c_board_info __initdata i2c_info[] = { + /* NOTE: original ATtiny24 firmware is at address 0x0b */ +}; + static int __init atngw100_init(void) { unsigned i; @@ -172,12 +178,28 @@ static int __init atngw100_init(void) } platform_device_register(&ngw_gpio_leds); + /* all these i2c/smbus pins should have external pullups for + * open-drain sharing among all I2C devices. SDA and SCL do; + * PB28/EXTINT3 doesn't; it should be SMBALERT# (for PMBus), + * but it's not available off-board. + */ + at32_select_periph(GPIO_PIN_PB(28), 0, AT32_GPIOF_PULLUP); at32_select_gpio(i2c_gpio_data.sda_pin, AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); at32_select_gpio(i2c_gpio_data.scl_pin, AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); platform_device_register(&i2c_gpio_device); + i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info)); return 0; } postcore_initcall(atngw100_init); + +static int __init atngw100_arch_init(void) +{ + /* set_irq_type() after the arch_initcall for EIC has run, and + * before the I2C subsystem could try using this IRQ. + */ + return set_irq_type(AT32_EXTINT(3), IRQ_TYPE_EDGE_FALLING); +} +arch_initcall(atngw100_arch_init); From 9c2baf785e90419d6f9af6bd07aa0694020cba8d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 18 Jun 2008 02:31:43 -0700 Subject: [PATCH 10/31] at32ap700x spi: enable pullups on MISO This is a minor tweak to the at32ap700x pin configuration for the SPI input pin (MISO), enabling the on-chip weak pullup (typical 190K) to (a) ensure a fixed data value for missing or input-only slaves; (b) prevent power waste associated with inputs floating near VDDIO/2. Atmel's boards have no external pullup or pulldown on these pins, so it's unlikely other boards would address these issues with external pulldowns. Were there trouble, board-specific code could turn off the relevant pullup(s). Signed-off-by: David Brownell Signed-off-by: Haavard Skinnemoen --- arch/avr32/mach-at32ap/at32ap700x.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index a9ae7bb108b2..a2e7b96b5e8c 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1181,7 +1181,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n) switch (id) { case 0: pdev = &atmel_spi0_device; - select_peripheral(PA(0), PERIPH_A, 0); /* MISO */ + /* pullup MISO so a level is always defined */ + select_peripheral(PA(0), PERIPH_A, AT32_GPIOF_PULLUP); select_peripheral(PA(1), PERIPH_A, 0); /* MOSI */ select_peripheral(PA(2), PERIPH_A, 0); /* SCK */ at32_spi_setup_slaves(0, b, n, spi0_pins); @@ -1189,7 +1190,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n) case 1: pdev = &atmel_spi1_device; - select_peripheral(PB(0), PERIPH_B, 0); /* MISO */ + /* pullup MISO so a level is always defined */ + select_peripheral(PB(0), PERIPH_B, AT32_GPIOF_PULLUP); select_peripheral(PB(1), PERIPH_B, 0); /* MOSI */ select_peripheral(PB(5), PERIPH_B, 0); /* SCK */ at32_spi_setup_slaves(1, b, n, spi1_pins); From 8405996ff6d89bbf275a206eb69d10b98a8d5257 Mon Sep 17 00:00:00 2001 From: Sedji Gaouaou Date: Wed, 25 Jun 2008 10:32:50 +0200 Subject: [PATCH 11/31] atmel_pwm: Rename the "mck" clock to "pwm_clk" The name "mck" causes a conflict on AT91. Call it "pwm_clk" instead. Signed-off-by: Sedji Gaouaou Signed-off-by: Haavard Skinnemoen --- arch/avr32/mach-at32ap/at32ap700x.c | 2 +- drivers/misc/atmel_pwm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index a2e7b96b5e8c..2f208eb4d3c9 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1471,7 +1471,7 @@ static struct resource atmel_pwm0_resource[] __initdata = { IRQ(24), }; static struct clk atmel_pwm0_mck = { - .name = "mck", + .name = "pwm_clk", .parent = &pbb_clk, .mode = pbb_clk_mode, .get_rate = pbb_clk_get_rate, diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 0d5ce03cdff2..5b5a14dab3d3 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c @@ -332,7 +332,7 @@ static int __init pwm_probe(struct platform_device *pdev) p->base = ioremap(r->start, r->end - r->start + 1); if (!p->base) goto fail; - p->clk = clk_get(&pdev->dev, "mck"); + p->clk = clk_get(&pdev->dev, "pwm_clk"); if (IS_ERR(p->clk)) { status = PTR_ERR(p->clk); p->clk = NULL; From 9dbef285a359c9bf6f04f907bdfb9f35f37a8513 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 28 May 2008 13:07:40 +0200 Subject: [PATCH 12/31] avr32: Clean up time.c #includes Remove lots of unneeded #includes, add #include and sort alphabetically. Signed-off-by: Haavard Skinnemoen --- arch/avr32/kernel/time.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c index 00a9862380ff..abd954fb7ba0 100644 --- a/arch/avr32/kernel/time.c +++ b/arch/avr32/kernel/time.c @@ -7,21 +7,13 @@ */ #include #include -#include -#include +#include #include #include -#include -#include -#include -#include -#include -#include +#include +#include -#include #include -#include -#include #include From 5b72b52cab872396c93d808d7fa14017e9061430 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 14 Jan 2008 23:27:30 +0100 Subject: [PATCH 13/31] avr32: Kill unneeded #include from asm/mmu_context.h Signed-off-by: Haavard Skinnemoen --- include/asm-avr32/mmu_context.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/asm-avr32/mmu_context.h b/include/asm-avr32/mmu_context.h index c37c391faef6..27ff23407100 100644 --- a/include/asm-avr32/mmu_context.h +++ b/include/asm-avr32/mmu_context.h @@ -13,7 +13,6 @@ #define __ASM_AVR32_MMU_CONTEXT_H #include -#include #include #include From d704fb0cc0cce474ac959adca6c3d1f606fcfa2a Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 14 Jan 2008 21:42:56 +0100 Subject: [PATCH 14/31] avr32: Kill special exception handler sections Kill the special exception handler sections .tlbx.ex.text, .tlbr.ex.text, tlbw.ex.text and .scall.text. Use .org instead to place the handlers at the required offsets from EVBA. Signed-off-by: Haavard Skinnemoen --- arch/avr32/kernel/entry-avr32b.S | 9 +++++---- arch/avr32/kernel/vmlinux.lds.S | 8 -------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S index 5f31702d6b1c..050c006a7f0c 100644 --- a/arch/avr32/kernel/entry-avr32b.S +++ b/arch/avr32/kernel/entry-avr32b.S @@ -83,22 +83,23 @@ exception_vectors: #define tlbmiss_save pushm r0-r3 #define tlbmiss_restore popm r0-r3 - .section .tlbx.ex.text,"ax",@progbits + .org 0x50 .global itlb_miss itlb_miss: tlbmiss_save rjmp tlb_miss_common - .section .tlbr.ex.text,"ax",@progbits + .org 0x60 dtlb_miss_read: tlbmiss_save rjmp tlb_miss_common - .section .tlbw.ex.text,"ax",@progbits + .org 0x70 dtlb_miss_write: tlbmiss_save .global tlb_miss_common + .align 2 tlb_miss_common: mfsr r0, SYSREG_TLBEAR mfsr r1, SYSREG_PTBR @@ -157,7 +158,7 @@ handle_vmalloc_miss: /* --- System Call --- */ - .section .scall.text,"ax",@progbits + .org 0x100 system_call: #ifdef CONFIG_PREEMPT mask_interrupts diff --git a/arch/avr32/kernel/vmlinux.lds.S b/arch/avr32/kernel/vmlinux.lds.S index 481cfd40c053..033dd46bfa62 100644 --- a/arch/avr32/kernel/vmlinux.lds.S +++ b/arch/avr32/kernel/vmlinux.lds.S @@ -68,14 +68,6 @@ SECTIONS _evba = .; _text = .; *(.ex.text) - . = 0x50; - *(.tlbx.ex.text) - . = 0x60; - *(.tlbr.ex.text) - . = 0x70; - *(.tlbw.ex.text) - . = 0x100; - *(.scall.text) *(.irq.text) KPROBES_TEXT TEXT_TEXT From 65033ed740ef06eddf9fde7a992eab336cbddd56 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 4 Mar 2008 15:15:00 +0100 Subject: [PATCH 15/31] avr32: Move setup_platform() into chip-specific file Combine at32_clock_init() and at32_portmux_init() into setup_platform() and remove setup_platform() from at32ap.c. No functional change since all setup_platform() ever did was call those two functions. Signed-off-by: Haavard Skinnemoen --- arch/avr32/mach-at32ap/at32ap.c | 8 -------- arch/avr32/mach-at32ap/at32ap700x.c | 18 ++++++++---------- include/asm-avr32/arch-at32ap/init.h | 4 ---- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/arch/avr32/mach-at32ap/at32ap.c b/arch/avr32/mach-at32ap/at32ap.c index 7c4987f3287a..1040bda4fda7 100644 --- a/arch/avr32/mach-at32ap/at32ap.c +++ b/arch/avr32/mach-at32ap/at32ap.c @@ -11,14 +11,6 @@ #include #include -#include - -void __init setup_platform(void) -{ - at32_clock_init(); - at32_portmux_init(); -} - static int __init pdc_probe(struct platform_device *pdev) { struct clk *pclk, *hclk; diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 2f208eb4d3c9..baf4680b4988 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -2048,16 +2048,7 @@ struct clk *at32_clock_list[] = { }; unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list); -void __init at32_portmux_init(void) -{ - at32_init_pio(&pio0_device); - at32_init_pio(&pio1_device); - at32_init_pio(&pio2_device); - at32_init_pio(&pio3_device); - at32_init_pio(&pio4_device); -} - -void __init at32_clock_init(void) +void __init setup_platform(void) { u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0; int i; @@ -2112,4 +2103,11 @@ void __init at32_clock_init(void) pm_writel(HSB_MASK, hsb_mask); pm_writel(PBA_MASK, pba_mask); pm_writel(PBB_MASK, pbb_mask); + + /* Initialize the port muxes */ + at32_init_pio(&pio0_device); + at32_init_pio(&pio1_device); + at32_init_pio(&pio2_device); + at32_init_pio(&pio3_device); + at32_init_pio(&pio4_device); } diff --git a/include/asm-avr32/arch-at32ap/init.h b/include/asm-avr32/arch-at32ap/init.h index 5e75d850d707..bc40e3d46150 100644 --- a/include/asm-avr32/arch-at32ap/init.h +++ b/include/asm-avr32/arch-at32ap/init.h @@ -13,10 +13,6 @@ void setup_platform(void); void setup_board(void); -/* Called by setup_platform */ -void at32_clock_init(void); -void at32_portmux_init(void); - void at32_setup_serial_console(unsigned int usart_id); #endif /* __ASM_AVR32_AT32AP_INIT_H__ */ From d7ff2a4a28ceadc03df2f5a20897165fda306382 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 4 Mar 2008 15:16:57 +0100 Subject: [PATCH 16/31] avr32: Rename at32ap.c -> pdc.c The only thing left in at32ap.c is some PDC stuff. Rename the file to reflect what it actually does. Signed-off-by: Haavard Skinnemoen --- arch/avr32/mach-at32ap/Makefile | 2 +- arch/avr32/mach-at32ap/{at32ap.c => pdc.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename arch/avr32/mach-at32ap/{at32ap.c => pdc.c} (100%) diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile index e89009439e4a..cb44182d3868 100644 --- a/arch/avr32/mach-at32ap/Makefile +++ b/arch/avr32/mach-at32ap/Makefile @@ -1,3 +1,3 @@ -obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o +obj-y += pdc.o clock.o intc.o extint.o pio.o hsmc.o obj-$(CONFIG_CPU_AT32AP700X) += at32ap700x.o pm-at32ap700x.o obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o diff --git a/arch/avr32/mach-at32ap/at32ap.c b/arch/avr32/mach-at32ap/pdc.c similarity index 100% rename from arch/avr32/mach-at32ap/at32ap.c rename to arch/avr32/mach-at32ap/pdc.c From b13d618b44fefea7529bd467e55423d353a599fc Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 15 May 2007 15:06:41 +0200 Subject: [PATCH 17/31] avr32: Clean up and optimize the TLB operations This and the following patches aim to optimize the code dealing with page tables and TLB operations. Each patch reduces the time it takes to gzip a 16 MB file slightly, but I expect things like fork() and mmap() will improve somewhat more. This patch deals with the low-level TLB operations: * Remove unused _TLBEHI_I define * Use gcc builtins instead of inline assembly * Remove a few unnecessary pipeline flushes and nops * Introduce NR_TLB_ENTRIES define and use it instead of hardcoding it to 32 a few places throughout the code. * Use sysreg bitops instead of hardcoded shifts and masks * Make a few needlessly global functions static Signed-off-by: Haavard Skinnemoen --- arch/avr32/mm/tlb.c | 175 +++++++++++++++++------------------ include/asm-avr32/tlbflush.h | 1 - 2 files changed, 87 insertions(+), 89 deletions(-) diff --git a/arch/avr32/mm/tlb.c b/arch/avr32/mm/tlb.c index cd12edbea9f2..06677be98ffb 100644 --- a/arch/avr32/mm/tlb.c +++ b/arch/avr32/mm/tlb.c @@ -11,21 +11,21 @@ #include -#define _TLBEHI_I 0x100 +/* TODO: Get the correct number from the CONFIG1 system register */ +#define NR_TLB_ENTRIES 32 -void show_dtlb_entry(unsigned int index) +static void show_dtlb_entry(unsigned int index) { - unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save; + u32 tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save; unsigned long flags; local_irq_save(flags); mmucr_save = sysreg_read(MMUCR); tlbehi_save = sysreg_read(TLBEHI); - mmucr = mmucr_save & 0x13; - mmucr |= index << 14; + mmucr = SYSREG_BFINS(DRP, index, mmucr_save); sysreg_write(MMUCR, mmucr); - asm volatile("tlbr" : : : "memory"); + __builtin_tlbr(); cpu_sync_pipeline(); tlbehi = sysreg_read(TLBEHI); @@ -33,15 +33,17 @@ void show_dtlb_entry(unsigned int index) printk("%2u: %c %c %02x %05x %05x %o %o %c %c %c %c\n", index, - (tlbehi & 0x200)?'1':'0', - (tlbelo & 0x100)?'1':'0', - (tlbehi & 0xff), - (tlbehi >> 12), (tlbelo >> 12), - (tlbelo >> 4) & 7, (tlbelo >> 2) & 3, - (tlbelo & 0x200)?'1':'0', - (tlbelo & 0x080)?'1':'0', - (tlbelo & 0x001)?'1':'0', - (tlbelo & 0x002)?'1':'0'); + SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '0', + SYSREG_BFEXT(G, tlbelo) ? '1' : '0', + SYSREG_BFEXT(ASID, tlbehi), + SYSREG_BFEXT(VPN, tlbehi) >> 2, + SYSREG_BFEXT(PFN, tlbelo) >> 2, + SYSREG_BFEXT(AP, tlbelo), + SYSREG_BFEXT(SZ, tlbelo), + SYSREG_BFEXT(TLBELO_C, tlbelo) ? 'C' : ' ', + SYSREG_BFEXT(B, tlbelo) ? 'B' : ' ', + SYSREG_BFEXT(W, tlbelo) ? 'W' : ' ', + SYSREG_BFEXT(TLBELO_D, tlbelo) ? 'D' : ' '); sysreg_write(MMUCR, mmucr_save); sysreg_write(TLBEHI, tlbehi_save); @@ -54,29 +56,33 @@ void dump_dtlb(void) unsigned int i; printk("ID V G ASID VPN PFN AP SZ C B W D\n"); - for (i = 0; i < 32; i++) + for (i = 0; i < NR_TLB_ENTRIES; i++) show_dtlb_entry(i); } -static unsigned long last_mmucr; - -static inline void set_replacement_pointer(unsigned shift) +static void update_dtlb(unsigned long address, pte_t pte) { - unsigned long mmucr, mmucr_save; + u32 tlbehi; + u32 mmucr; - mmucr = mmucr_save = sysreg_read(MMUCR); + /* + * We're not changing the ASID here, so no need to flush the + * pipeline. + */ + tlbehi = sysreg_read(TLBEHI); + tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi)); + tlbehi |= address & MMU_VPN_MASK; + tlbehi |= SYSREG_BIT(TLBEHI_V); + sysreg_write(TLBEHI, tlbehi); /* Does this mapping already exist? */ - __asm__ __volatile__( - " tlbs\n" - " mfsr %0, %1" - : "=r"(mmucr) - : "i"(SYSREG_MMUCR)); + __builtin_tlbs(); + mmucr = sysreg_read(MMUCR); if (mmucr & SYSREG_BIT(MMUCR_N)) { /* Not found -- pick a not-recently-accessed entry */ - unsigned long rp; - unsigned long tlbar = sysreg_read(TLBARLO); + unsigned int rp; + u32 tlbar = sysreg_read(TLBARLO); rp = 32 - fls(tlbar); if (rp == 32) { @@ -84,30 +90,14 @@ static inline void set_replacement_pointer(unsigned shift) sysreg_write(TLBARLO, -1L); } - mmucr &= 0x13; - mmucr |= (rp << shift); - + mmucr = SYSREG_BFINS(DRP, rp, mmucr); sysreg_write(MMUCR, mmucr); } - last_mmucr = mmucr; -} - -static void update_dtlb(unsigned long address, pte_t pte, unsigned long asid) -{ - unsigned long vpn; - - vpn = (address & MMU_VPN_MASK) | _TLBEHI_VALID | asid; - sysreg_write(TLBEHI, vpn); - cpu_sync_pipeline(); - - set_replacement_pointer(14); - sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK); /* Let's go */ - asm volatile("nop\n\ttlbw" : : : "memory"); - cpu_sync_pipeline(); + __builtin_tlbw(); } void update_mmu_cache(struct vm_area_struct *vma, @@ -120,39 +110,40 @@ void update_mmu_cache(struct vm_area_struct *vma, return; local_irq_save(flags); - update_dtlb(address, pte, get_asid()); + update_dtlb(address, pte); local_irq_restore(flags); } -void __flush_tlb_page(unsigned long asid, unsigned long page) +static void __flush_tlb_page(unsigned long asid, unsigned long page) { - unsigned long mmucr, tlbehi; + u32 mmucr, tlbehi; - page |= asid; - sysreg_write(TLBEHI, page); - cpu_sync_pipeline(); - asm volatile("tlbs"); + /* + * Caller is responsible for masking out non-PFN bits in page + * and changing the current ASID if necessary. This means that + * we don't need to flush the pipeline after writing TLBEHI. + */ + tlbehi = page | asid; + sysreg_write(TLBEHI, tlbehi); + + __builtin_tlbs(); mmucr = sysreg_read(MMUCR); if (!(mmucr & SYSREG_BIT(MMUCR_N))) { - unsigned long tlbarlo; - unsigned long entry; + unsigned int entry; + u32 tlbarlo; /* Clear the "valid" bit */ - tlbehi = sysreg_read(TLBEHI); - tlbehi &= ~_TLBEHI_VALID; sysreg_write(TLBEHI, tlbehi); - cpu_sync_pipeline(); /* mark the entry as "not accessed" */ - entry = (mmucr >> 14) & 0x3f; + entry = SYSREG_BFEXT(DRP, mmucr); tlbarlo = sysreg_read(TLBARLO); - tlbarlo |= (0x80000000 >> entry); + tlbarlo |= (0x80000000UL >> entry); sysreg_write(TLBARLO, tlbarlo); /* update the entry with valid bit clear */ - asm volatile("tlbw"); - cpu_sync_pipeline(); + __builtin_tlbw(); } } @@ -190,17 +181,22 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, local_irq_save(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */ mm->context = NO_CONTEXT; if (mm == current->mm) activate_context(mm); } else { - unsigned long asid = mm->context & MMU_CONTEXT_ASID_MASK; - unsigned long saved_asid = MMU_NO_ASID; + unsigned long asid; + unsigned long saved_asid; + + asid = mm->context & MMU_CONTEXT_ASID_MASK; + saved_asid = MMU_NO_ASID; start &= PAGE_MASK; end += (PAGE_SIZE - 1); end &= PAGE_MASK; + if (mm != current->mm) { saved_asid = get_asid(); set_asid(asid); @@ -218,33 +214,34 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, } /* - * TODO: If this is only called for addresses > TASK_SIZE, we can probably - * skip the ASID stuff and just use the Global bit... + * This function depends on the pages to be flushed having the G + * (global) bit set in their pte. This is true for all + * PAGE_KERNEL(_RO) pages. */ void flush_tlb_kernel_range(unsigned long start, unsigned long end) { unsigned long flags; int size; - local_irq_save(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */ flush_tlb_all(); } else { - unsigned long asid = init_mm.context & MMU_CONTEXT_ASID_MASK; - unsigned long saved_asid = get_asid(); + unsigned long asid; + + local_irq_save(flags); + asid = get_asid(); start &= PAGE_MASK; end += (PAGE_SIZE - 1); end &= PAGE_MASK; - set_asid(asid); + while (start < end) { __flush_tlb_page(asid, start); start += PAGE_SIZE; } - set_asid(saved_asid); + local_irq_restore(flags); } - local_irq_restore(flags); } void flush_tlb_mm(struct mm_struct *mm) @@ -280,7 +277,7 @@ static void *tlb_start(struct seq_file *tlb, loff_t *pos) { static unsigned long tlb_index; - if (*pos >= 32) + if (*pos >= NR_TLB_ENTRIES) return NULL; tlb_index = 0; @@ -291,7 +288,7 @@ static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos) { unsigned long *index = v; - if (*index >= 31) + if (*index >= NR_TLB_ENTRIES - 1) return NULL; ++*pos; @@ -313,16 +310,16 @@ static int tlb_show(struct seq_file *tlb, void *v) if (*index == 0) seq_puts(tlb, "ID V G ASID VPN PFN AP SZ C B W D\n"); - BUG_ON(*index >= 32); + BUG_ON(*index >= NR_TLB_ENTRIES); local_irq_save(flags); mmucr_save = sysreg_read(MMUCR); tlbehi_save = sysreg_read(TLBEHI); - mmucr = mmucr_save & 0x13; - mmucr |= *index << 14; + mmucr = SYSREG_BFINS(DRP, *index, mmucr_save); sysreg_write(MMUCR, mmucr); - asm volatile("tlbr" : : : "memory"); + /* TLBR might change the ASID */ + __builtin_tlbr(); cpu_sync_pipeline(); tlbehi = sysreg_read(TLBEHI); @@ -334,16 +331,18 @@ static int tlb_show(struct seq_file *tlb, void *v) local_irq_restore(flags); seq_printf(tlb, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n", - *index, - (tlbehi & 0x200)?'1':'0', - (tlbelo & 0x100)?'1':'0', - (tlbehi & 0xff), - (tlbehi >> 12), (tlbelo >> 12), - (tlbelo >> 4) & 7, (tlbelo >> 2) & 3, - (tlbelo & 0x200)?'1':'0', - (tlbelo & 0x080)?'1':'0', - (tlbelo & 0x001)?'1':'0', - (tlbelo & 0x002)?'1':'0'); + *index, + SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '0', + SYSREG_BFEXT(G, tlbelo) ? '1' : '0', + SYSREG_BFEXT(ASID, tlbehi), + SYSREG_BFEXT(VPN, tlbehi) >> 2, + SYSREG_BFEXT(PFN, tlbelo) >> 2, + SYSREG_BFEXT(AP, tlbelo), + SYSREG_BFEXT(SZ, tlbelo), + SYSREG_BFEXT(TLBELO_C, tlbelo) ? '1' : '0', + SYSREG_BFEXT(B, tlbelo) ? '1' : '0', + SYSREG_BFEXT(W, tlbelo) ? '1' : '0', + SYSREG_BFEXT(TLBELO_D, tlbelo) ? '1' : '0'); return 0; } diff --git a/include/asm-avr32/tlbflush.h b/include/asm-avr32/tlbflush.h index 5bc7c88a5770..bf90a786f6be 100644 --- a/include/asm-avr32/tlbflush.h +++ b/include/asm-avr32/tlbflush.h @@ -26,7 +26,6 @@ extern void flush_tlb_mm(struct mm_struct *mm); extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page); -extern void __flush_tlb_page(unsigned long asid, unsigned long page); extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); From ebe74597a55fef00edc80a414ef5c6477d035e0a Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 14 Jan 2008 22:16:50 +0100 Subject: [PATCH 18/31] avr32: Remove useless zeroing of swapper_pg_dir at startup swapper_pg_dir is stored in .bss, so it must already be zeroed out when we get there. Signed-off-by: Haavard Skinnemoen --- arch/avr32/mm/init.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 5ee1e407dcf7..0e77578c358d 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -108,19 +108,9 @@ void __init paging_init(void) zero_page = alloc_bootmem_low_pages_node(NODE_DATA(0), PAGE_SIZE); - { - pgd_t *pg_dir; - int i; - - pg_dir = swapper_pg_dir; - sysreg_write(PTBR, (unsigned long)pg_dir); - - for (i = 0; i < PTRS_PER_PGD; i++) - pgd_val(pg_dir[i]) = 0; - - enable_mmu(); - printk ("CPU: Paging enabled\n"); - } + sysreg_write(PTBR, (unsigned long)swapper_pg_dir); + enable_mmu(); + printk ("CPU: Paging enabled\n"); for_each_online_node(nid) { pg_data_t *pgdat = NODE_DATA(nid); From cfd23e93a0289cf6711fd3877c5226658d87240a Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 14 Jan 2008 22:15:05 +0100 Subject: [PATCH 19/31] avr32: Store virtual addresses in the PGD Instead of storing physical addresses along with page flags in the PGD, store virtual addresses and use NULL to indicate a not present second-level page table. A non-page-aligned page table indicates a bad PMD. This simplifies the TLB miss handler since it no longer has to check the Present bit and no longer has to convert the PGD entry from physical to virtual address. Instead, it has to check for a NULL entry, which is slightly cheaper than either. Signed-off-by: Haavard Skinnemoen --- arch/avr32/kernel/entry-avr32b.S | 45 ++++++++++++++------------------ arch/avr32/kernel/vmlinux.lds.S | 4 +++ arch/avr32/mm/init.c | 4 ++- include/asm-avr32/pgalloc.h | 18 +++++++------ include/asm-avr32/pgtable.h | 34 ++++++++++-------------- 5 files changed, 51 insertions(+), 54 deletions(-) diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S index 050c006a7f0c..3cdd7071f35e 100644 --- a/arch/avr32/kernel/entry-avr32b.S +++ b/arch/avr32/kernel/entry-avr32b.S @@ -74,12 +74,6 @@ exception_vectors: .align 2 bral do_dtlb_modified - /* - * r0 : PGD/PT/PTE - * r1 : Offending address - * r2 : Scratch register - * r3 : Cause (5, 12 or 13) - */ #define tlbmiss_save pushm r0-r3 #define tlbmiss_restore popm r0-r3 @@ -108,17 +102,17 @@ tlb_miss_common: bld r0, 31 brcs handle_vmalloc_miss - /* First level lookup */ + /* + * First level lookup: The PGD contains virtual pointers to + * the second-level page tables, but they may be NULL if not + * present. + */ pgtbl_lookup: lsr r2, r0, PGDIR_SHIFT ld.w r3, r1[r2 << 2] bfextu r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT - bld r3, _PAGE_BIT_PRESENT - brcc page_table_not_present - - /* Translate to virtual address in P1. */ - andl r3, 0xf000 - sbr r3, 31 + cp.w r3, 0 + breq page_table_not_present /* Second level lookup */ ld.w r2, r3[r1 << 2] @@ -155,6 +149,19 @@ handle_vmalloc_miss: orh r1, hi(swapper_pg_dir) rjmp pgtbl_lookup + /* The slow path of the TLB miss handler */ + .align 2 +page_table_not_present: +page_not_present: + tlbmiss_restore + sub sp, 4 + stmts --sp, r0-lr + rcall save_full_context_ex + mfsr r12, SYSREG_ECR + mov r11, sp + rcall do_page_fault + rjmp ret_from_exception + /* --- System Call --- */ @@ -267,18 +274,6 @@ syscall_exit_work: brcc syscall_exit_cont rjmp enter_monitor_mode - /* The slow path of the TLB miss handler */ -page_table_not_present: -page_not_present: - tlbmiss_restore - sub sp, 4 - stmts --sp, r0-lr - rcall save_full_context_ex - mfsr r12, SYSREG_ECR - mov r11, sp - rcall do_page_fault - rjmp ret_from_exception - /* This function expects to find offending PC in SYSREG_RAR_EX */ .type save_full_context_ex, @function .align 2 diff --git a/arch/avr32/kernel/vmlinux.lds.S b/arch/avr32/kernel/vmlinux.lds.S index 033dd46bfa62..5d25d8eeb750 100644 --- a/arch/avr32/kernel/vmlinux.lds.S +++ b/arch/avr32/kernel/vmlinux.lds.S @@ -99,6 +99,10 @@ SECTIONS */ *(.data.init_task) + /* Then, the page-aligned data */ + . = ALIGN(PAGE_SIZE); + *(.data.page_aligned) + /* Then, the cacheline aligned data */ . = ALIGN(L1_CACHE_BYTES); *(.data.cacheline_aligned) diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 0e77578c358d..3f90a87527bb 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -24,9 +24,11 @@ #include #include +#define __page_aligned __attribute__((section(".data.page_aligned"))) + DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); -pgd_t swapper_pg_dir[PTRS_PER_PGD]; +pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned; struct page *empty_zero_page; EXPORT_SYMBOL(empty_zero_page); diff --git a/include/asm-avr32/pgalloc.h b/include/asm-avr32/pgalloc.h index 51fc1f6e4b17..5b768fc2388e 100644 --- a/include/asm-avr32/pgalloc.h +++ b/include/asm-avr32/pgalloc.h @@ -8,25 +8,27 @@ #ifndef __ASM_AVR32_PGALLOC_H #define __ASM_AVR32_PGALLOC_H -#include -#include -#include #include +#include +#include -#define pmd_populate_kernel(mm, pmd, pte) \ - set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte))) +static inline void pmd_populate_kernel(struct mm_struct *mm, + pmd_t *pmd, pte_t *pte) +{ + set_pmd(pmd, __pmd((unsigned long)pte)); +} -static __inline__ void pmd_populate(struct mm_struct *mm, pmd_t *pmd, +static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, pgtable_t pte) { - set_pmd(pmd, __pmd(_PAGE_TABLE + page_to_phys(pte))); + set_pmd(pmd, __pmd((unsigned long)page_address(pte))); } #define pmd_pgtable(pmd) pmd_page(pmd) /* * Allocate and free page tables */ -static __inline__ pgd_t *pgd_alloc(struct mm_struct *mm) +static inline pgd_t *pgd_alloc(struct mm_struct *mm) { return kcalloc(USER_PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL); } diff --git a/include/asm-avr32/pgtable.h b/include/asm-avr32/pgtable.h index c0e5e29417df..fecdda16f444 100644 --- a/include/asm-avr32/pgtable.h +++ b/include/asm-avr32/pgtable.h @@ -129,13 +129,6 @@ extern struct page *empty_zero_page; #define _PAGE_FLAGS_CACHE_MASK (_PAGE_CACHABLE | _PAGE_BUFFER | _PAGE_WT) -/* TODO: Check for saneness */ -/* User-mode page table flags (to be set in a pgd or pmd entry) */ -#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_TYPE_SMALL | _PAGE_RW \ - | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY) -/* Kernel-mode page table flags */ -#define _KERNPG_TABLE (_PAGE_PRESENT | _PAGE_TYPE_SMALL | _PAGE_RW \ - | _PAGE_ACCESSED | _PAGE_DIRTY) /* Flags that may be modified by software */ #define _PAGE_CHG_MASK (PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY \ | _PAGE_FLAGS_CACHE_MASK) @@ -262,10 +255,14 @@ static inline pte_t pte_mkspecial(pte_t pte) } #define pmd_none(x) (!pmd_val(x)) -#define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT) -#define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0) -#define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) \ - != _KERNPG_TABLE) +#define pmd_present(x) (pmd_val(x)) + +static inline void pmd_clear(pmd_t *pmdp) +{ + set_pmd(pmdp, __pmd(0)); +} + +#define pmd_bad(x) (pmd_val(x) & ~PAGE_MASK) /* * Permanent address of a page. We don't support highmem, so this is @@ -303,19 +300,16 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) #define page_pte(page) page_pte_prot(page, __pgprot(0)) -#define pmd_page_vaddr(pmd) \ - ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) - -#define pmd_page(pmd) (phys_to_page(pmd_val(pmd))) +#define pmd_page_vaddr(pmd) pmd_val(pmd) +#define pmd_page(pmd) (virt_to_page(pmd_val(pmd))) /* to find an entry in a page-table-directory. */ -#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) -#define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address)) -#define pgd_offset_current(address) \ - ((pgd_t *)__mfsr(SYSREG_PTBR) + pgd_index(address)) +#define pgd_index(address) (((address) >> PGDIR_SHIFT) \ + & (PTRS_PER_PGD - 1)) +#define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address)) /* to find an entry in a kernel page-table-directory */ -#define pgd_offset_k(address) pgd_offset(&init_mm, address) +#define pgd_offset_k(address) pgd_offset(&init_mm, address) /* Find an entry in the third-level page table.. */ #define pte_index(address) \ From a9a934f278613885816aa9f177968c1dac557240 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 14 Jan 2008 23:11:26 +0100 Subject: [PATCH 20/31] avr32: Cover the kernel page tables in the user PGDs Expand the per-process PGDs so that they cover the kernel virtual memory area as well. This simplifies the TLB miss handler fastpath since it doesn't have to check for kernel addresses anymore. If a TLB miss happens on a kernel address and a second-level page table can't be found, we check swapper_pg_dir and copy the PGD entry into the user PGD if it can be found there. Signed-off-by: Haavard Skinnemoen --- arch/avr32/kernel/entry-avr32b.S | 42 ++++++++++++++++++++++++-------- include/asm-avr32/pgalloc.h | 14 ++++++++--- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S index 3cdd7071f35e..2b398cae110c 100644 --- a/arch/avr32/kernel/entry-avr32b.S +++ b/arch/avr32/kernel/entry-avr32b.S @@ -98,10 +98,6 @@ tlb_miss_common: mfsr r0, SYSREG_TLBEAR mfsr r1, SYSREG_PTBR - /* Is it the vmalloc space? */ - bld r0, 31 - brcs handle_vmalloc_miss - /* * First level lookup: The PGD contains virtual pointers to * the second-level page tables, but they may be NULL if not @@ -143,15 +139,13 @@ pgtbl_lookup: tlbmiss_restore rete -handle_vmalloc_miss: - /* Simply do the lookup in init's page table */ - mov r1, lo(swapper_pg_dir) - orh r1, hi(swapper_pg_dir) - rjmp pgtbl_lookup - /* The slow path of the TLB miss handler */ .align 2 page_table_not_present: + /* Do we need to synchronize with swapper_pg_dir? */ + bld r0, 31 + brcs sync_with_swapper_pg_dir + page_not_present: tlbmiss_restore sub sp, 4 @@ -162,6 +156,34 @@ page_not_present: rcall do_page_fault rjmp ret_from_exception + .align 2 +sync_with_swapper_pg_dir: + /* + * If swapper_pg_dir contains a non-NULL second-level page + * table pointer, copy it into the current PGD. If not, we + * must handle it as a full-blown page fault. + * + * Jumping back to pgtbl_lookup causes an unnecessary lookup, + * but it is guaranteed to be a cache hit, it won't happen + * very often, and we absolutely do not want to sacrifice any + * performance in the fast path in order to improve this. + */ + mov r1, lo(swapper_pg_dir) + orh r1, hi(swapper_pg_dir) + ld.w r3, r1[r2 << 2] + cp.w r3, 0 + breq page_not_present + mfsr r1, SYSREG_PTBR + st.w r1[r2 << 2], r3 + rjmp pgtbl_lookup + + /* + * We currently have two bytes left at this point until we + * crash into the system call handler... + * + * Don't worry, the assembler will let us know. + */ + /* --- System Call --- */ diff --git a/include/asm-avr32/pgalloc.h b/include/asm-avr32/pgalloc.h index 5b768fc2388e..e9636d1f383f 100644 --- a/include/asm-avr32/pgalloc.h +++ b/include/asm-avr32/pgalloc.h @@ -9,8 +9,6 @@ #define __ASM_AVR32_PGALLOC_H #include -#include -#include static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) @@ -30,12 +28,20 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, */ static inline pgd_t *pgd_alloc(struct mm_struct *mm) { - return kcalloc(USER_PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL); + pgd_t *pgd; + + pgd = (pgd_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT); + if (likely(pgd)) + memcpy(pgd + USER_PTRS_PER_PGD, + swapper_pg_dir + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + + return pgd; } static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) { - kfree(pgd); + free_page((unsigned long)pgd); } static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, From 5a4d5292779b6163aa41e594a56307e442fbe73c Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 14 Jan 2008 23:33:44 +0100 Subject: [PATCH 21/31] avr32: Use a quicklist for PGD allocation Use a quicklist to allocate process PGDs. This is expected to be slightly faster since we need to copy entries from swapper_pg_dir, which can stay around for pages on the PGD quick list. Signed-off-by: Haavard Skinnemoen --- arch/avr32/Kconfig | 3 +++ include/asm-avr32/pgalloc.h | 32 ++++++++++++++++++++------------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index 09ad7995080c..a5793c13f50c 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -147,6 +147,9 @@ config PHYS_OFFSET source "kernel/Kconfig.preempt" +config QUICKLIST + def_bool y + config HAVE_ARCH_BOOTMEM_NODE def_bool n diff --git a/include/asm-avr32/pgalloc.h b/include/asm-avr32/pgalloc.h index e9636d1f383f..a291f59659cf 100644 --- a/include/asm-avr32/pgalloc.h +++ b/include/asm-avr32/pgalloc.h @@ -8,7 +8,11 @@ #ifndef __ASM_AVR32_PGALLOC_H #define __ASM_AVR32_PGALLOC_H -#include +#include +#include +#include + +#define QUICK_PGD 0 /* Preserve kernel mappings over free */ static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) @@ -23,25 +27,26 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, } #define pmd_pgtable(pmd) pmd_page(pmd) +static inline void pgd_ctor(void *x) +{ + pgd_t *pgd = x; + + memcpy(pgd + USER_PTRS_PER_PGD, + swapper_pg_dir + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); +} + /* * Allocate and free page tables */ static inline pgd_t *pgd_alloc(struct mm_struct *mm) { - pgd_t *pgd; - - pgd = (pgd_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT); - if (likely(pgd)) - memcpy(pgd + USER_PTRS_PER_PGD, - swapper_pg_dir + USER_PTRS_PER_PGD, - (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); - - return pgd; + return quicklist_alloc(QUICK_PGD, GFP_KERNEL | __GFP_REPEAT, pgd_ctor); } static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) { - free_page((unsigned long)pgd); + quicklist_free(QUICK_PGD, NULL, pgd); } static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, @@ -83,6 +88,9 @@ do { \ tlb_remove_page((tlb), pte); \ } while (0) -#define check_pgt_cache() do { } while(0) +static inline void check_pgt_cache(void) +{ + quicklist_trim(QUICK_PGD, NULL, 25, 16); +} #endif /* __ASM_AVR32_PGALLOC_H */ From 38510754a50192a072210e24fdc4ae65592182f0 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 14 Jan 2008 23:35:32 +0100 Subject: [PATCH 22/31] avr32: Use a quicklist for PTE allocation as well Using a quicklist to allocate PTEs might be slightly faster than using the page allocator directly since we might avoid zeroing the page after each allocation. Signed-off-by: Haavard Skinnemoen --- include/asm-avr32/pgalloc.h | 28 +++++++++++++++------------- mm/Kconfig | 2 +- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/asm-avr32/pgalloc.h b/include/asm-avr32/pgalloc.h index a291f59659cf..640821323943 100644 --- a/include/asm-avr32/pgalloc.h +++ b/include/asm-avr32/pgalloc.h @@ -13,6 +13,7 @@ #include #define QUICK_PGD 0 /* Preserve kernel mappings over free */ +#define QUICK_PT 1 /* Zero on free */ static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) @@ -52,34 +53,34 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { - pte_t *pte; - - pte = (pte_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT); - - return pte; + return quicklist_alloc(QUICK_PT, GFP_KERNEL | __GFP_REPEAT, NULL); } -static inline struct page *pte_alloc_one(struct mm_struct *mm, +static inline pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { - struct page *pte; + struct page *page; + void *pg; - pte = alloc_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO); - if (!pte) + pg = quicklist_alloc(QUICK_PT, GFP_KERNEL | __GFP_REPEAT, NULL); + if (!pg) return NULL; - pgtable_page_ctor(pte); - return pte; + + page = virt_to_page(pg); + pgtable_page_ctor(page); + + return page; } static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) { - free_page((unsigned long)pte); + quicklist_free(QUICK_PT, NULL, pte); } static inline void pte_free(struct mm_struct *mm, pgtable_t pte) { pgtable_page_dtor(pte); - __free_page(pte); + quicklist_free_page(QUICK_PT, NULL, pte); } #define __pte_free_tlb(tlb,pte) \ @@ -91,6 +92,7 @@ do { \ static inline void check_pgt_cache(void) { quicklist_trim(QUICK_PGD, NULL, 25, 16); + quicklist_trim(QUICK_PT, NULL, 25, 16); } #endif /* __ASM_AVR32_PGALLOC_H */ diff --git a/mm/Kconfig b/mm/Kconfig index 3aa819d628c1..60b261952783 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -199,7 +199,7 @@ config BOUNCE config NR_QUICK int depends on QUICKLIST - default "2" if SUPERH + default "2" if SUPERH || AVR32 default "1" config VIRT_TO_BUS From f826caa44902ddbe93174f0b642d8abf9698c08e Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Sun, 24 Feb 2008 14:34:45 +0100 Subject: [PATCH 23/31] atmel_serial: Fix build on avr32 with CONFIG_PM enabled AVR32 doesn't have at91_suspend_entering_slow_clock(). Just assume the clock will keep running for now. David has a better solution for this, but this works for now. Leaving the USART clock running won't prevent the PM code from entering deep power-down modes anyway. Signed-off-by: Haavard Skinnemoen Cc: David Brownell Cc: Andrew Victor --- drivers/serial/atmel_serial.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 42be8b01a40f..5f0414fc1b10 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -1439,6 +1439,15 @@ static struct uart_driver atmel_uart = { }; #ifdef CONFIG_PM +static bool atmel_serial_clk_will_stop(void) +{ +#ifdef CONFIG_ARCH_AT91 + return at91_suspend_entering_slow_clock(); +#else + return false; +#endif +} + static int atmel_serial_suspend(struct platform_device *pdev, pm_message_t state) { @@ -1446,7 +1455,7 @@ static int atmel_serial_suspend(struct platform_device *pdev, struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); if (device_may_wakeup(&pdev->dev) - && !at91_suspend_entering_slow_clock()) + && !atmel_serial_clk_will_stop()) enable_irq_wake(port->irq); else { uart_suspend_port(&atmel_uart, port); From e1c609efbc0333840f2af2d875ca52ed8ee18587 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Fri, 14 Mar 2008 14:54:13 +0100 Subject: [PATCH 24/31] atmel_serial: Drain console TX shifter before suspending Funny things may happen if we stop the USART clock before the shifter is empty. Prevent this from happening by waiting until the shifter is completely drained before allowing suspend to continue. Signed-off-by: Haavard Skinnemoen Cc: Andrew Victor --- drivers/serial/atmel_serial.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 5f0414fc1b10..6aeef22bd203 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -1454,6 +1454,12 @@ static int atmel_serial_suspend(struct platform_device *pdev, struct uart_port *port = platform_get_drvdata(pdev); struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + if (atmel_is_console_port(port) && console_suspend_enabled) { + /* Drain the TX shifter */ + while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY)) + cpu_relax(); + } + if (device_may_wakeup(&pdev->dev) && !atmel_serial_clk_will_stop()) enable_irq_wake(port->irq); From c1f598fd71db6a971ee88311167c8003243ebff2 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 4 Mar 2008 13:39:29 +0100 Subject: [PATCH 25/31] macb: Basic suspend/resume support This implements suspend and resume callbacks for the macb driver. We may have to do some more to gracefully shut the MAC down, but this at least prevents the macb from waking the system when hooked up to a busy network. Signed-off-by: Haavard Skinnemoen Cc: Jeff Garzik Cc: Patrice Vilchez Cc: Nicolas FERRE --- drivers/net/macb.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 92dccd43bdca..0a5745a854c7 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -1277,8 +1277,45 @@ static int __exit macb_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int macb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *netdev = platform_get_drvdata(pdev); + struct macb *bp = netdev_priv(netdev); + + netif_device_detach(netdev); + +#ifndef CONFIG_ARCH_AT91 + clk_disable(bp->hclk); +#endif + clk_disable(bp->pclk); + + return 0; +} + +static int macb_resume(struct platform_device *pdev) +{ + struct net_device *netdev = platform_get_drvdata(pdev); + struct macb *bp = netdev_priv(netdev); + + clk_enable(bp->pclk); +#ifndef CONFIG_ARCH_AT91 + clk_enable(bp->hclk); +#endif + + netif_device_attach(netdev); + + return 0; +} +#else +#define macb_suspend NULL +#define macb_resume NULL +#endif + static struct platform_driver macb_driver = { .remove = __exit_p(macb_remove), + .suspend = macb_suspend, + .resume = macb_resume, .driver = { .name = "macb", .owner = THIS_MODULE, From f3a24e1e272f844a4d14a39731b4fa946ba36adc Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 30 Jun 2008 10:54:31 +0200 Subject: [PATCH 26/31] rtc-at32ap700x: Enable wakeup Call device_init_wakeup() to signal that the RTC is capable of waking the system. This is needed for rtcwake to work. Signed-off-by: Haavard Skinnemoen Cc: Hans-Christian Egtvedt Cc: Alessandro Zummo --- drivers/rtc/rtc-at32ap700x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c index 2ef8cdfda4a7..90b9a6503e15 100644 --- a/drivers/rtc/rtc-at32ap700x.c +++ b/drivers/rtc/rtc-at32ap700x.c @@ -265,6 +265,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, rtc); + device_init_wakeup(&pdev->dev, 1); dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n", (unsigned long)rtc->regs, rtc->irq); @@ -284,6 +285,8 @@ static int __exit at32_rtc_remove(struct platform_device *pdev) { struct rtc_at32ap700x *rtc = platform_get_drvdata(pdev); + device_init_wakeup(&pdev->dev, 0); + free_irq(rtc->irq, rtc); iounmap(rtc->regs); rtc_device_unregister(rtc->rtc); From 7951f188a0b7f9b2f181c692efb0d31082bec346 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 5 Mar 2008 15:08:27 +0100 Subject: [PATCH 27/31] avr32: Enable SDRAMC clock at startup The SDRAM controller needs a clock in order to respond to our commands, and suspend doesn't work very well without the SDRAM in self-refresh mode. Signed-off-by: Haavard Skinnemoen --- arch/avr32/mach-at32ap/at32ap700x.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index baf4680b4988..3ee5e7278790 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -675,6 +675,14 @@ static struct clk hramc_clk = { .users = 1, .index = 3, }; +static struct clk sdramc_clk = { + .name = "sdramc_clk", + .parent = &pbb_clk, + .mode = pbb_clk_mode, + .get_rate = pbb_clk_get_rate, + .users = 1, + .index = 14, +}; static struct resource smc0_resource[] = { PBMEM(0xfff03400), @@ -1998,6 +2006,7 @@ struct clk *at32_clock_list[] = { &hmatrix_clk, &ebi_clk, &hramc_clk, + &sdramc_clk, &smc0_pclk, &smc0_mck, &pdc_hclk, From b83d6ee17588f1a4fbfc8ef0451b0900a5ef5950 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 5 Mar 2008 10:00:28 +0100 Subject: [PATCH 28/31] avr32: Add simple SRAM allocator Add SRAM allocator for avr32, which is just a thin wrapper around genalloc. Signed-off-by: Haavard Skinnemoen --- arch/avr32/Kconfig | 1 + arch/avr32/mach-at32ap/at32ap700x.c | 26 ++++++++++++++++++++++++ include/asm-avr32/arch-at32ap/sram.h | 30 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 include/asm-avr32/arch-at32ap/sram.h diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index a5793c13f50c..e8ee5fa017b6 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -88,6 +88,7 @@ config PLATFORM_AT32AP select MMU select PERFORMANCE_COUNTERS select HAVE_GPIO_LIB + select GENERIC_ALLOCATOR # # CPU types diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 3ee5e7278790..07b21b121eef 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -20,6 +20,7 @@ #include #include #include +#include #include