diff options
Diffstat (limited to 'arch/avr32/mach-at32ap')
-rw-r--r-- | arch/avr32/mach-at32ap/Makefile | 3 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/at32ap700x.c | 243 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/intc.c | 1 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/pm-at32ap700x.S | 66 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/time-tc.c | 218 |
5 files changed, 291 insertions, 240 deletions
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile index 5e9f8217bef..e89009439e4 100644 --- a/arch/avr32/mach-at32ap/Makefile +++ b/arch/avr32/mach-at32ap/Makefile @@ -1,4 +1,3 @@ obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o -obj-$(CONFIG_CPU_AT32AP700X) += at32ap700x.o -obj-$(CONFIG_CPU_AT32AP700X) += time-tc.o +obj-$(CONFIG_CPU_AT32AP700X) += at32ap700x.o pm-at32ap700x.o obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 7678fee9a88..0f24b4f85c1 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -6,11 +6,13 @@ * published by the Free Software Foundation. */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/spi/spi.h> +#include <linux/usb/atmel_usba_udc.h> #include <asm/io.h> #include <asm/irq.h> @@ -98,6 +100,9 @@ unsigned long at32ap7000_osc_rates[3] = { [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]; @@ -107,9 +112,6 @@ static unsigned long pll_get_rate(struct clk *clk, unsigned long control) { unsigned long div, mul, rate; - if (!(control & PM_BIT(PLLEN))) - return 0; - div = PM_BFEXT(PLLDIV, control) + 1; mul = PM_BFEXT(PLLMUL, control) + 1; @@ -120,6 +122,71 @@ static unsigned long pll_get_rate(struct clk *clk, unsigned long control) return rate; } +static long pll_set_rate(struct clk *clk, unsigned long rate, + u32 *pll_ctrl) +{ + unsigned long mul; + unsigned long mul_best_fit = 0; + unsigned long div; + unsigned long div_min; + unsigned long div_max; + unsigned long div_best_fit = 0; + unsigned long base; + unsigned long pll_in; + unsigned long actual = 0; + unsigned long rate_error; + unsigned long rate_error_prev = ~0UL; + u32 ctrl; + + /* Rate must be between 80 MHz and 200 Mhz. */ + if (rate < 80000000UL || rate > 200000000UL) + return -EINVAL; + + ctrl = PM_BF(PLLOPT, 4); + base = clk->parent->get_rate(clk->parent); + + /* PLL input frequency must be between 6 MHz and 32 MHz. */ + div_min = DIV_ROUND_UP(base, 32000000UL); + div_max = base / 6000000UL; + + if (div_max < div_min) + return -EINVAL; + + for (div = div_min; div <= div_max; div++) { + pll_in = (base + div / 2) / div; + mul = (rate + pll_in / 2) / pll_in; + + if (mul == 0) + continue; + + actual = pll_in * mul; + rate_error = abs(actual - rate); + + if (rate_error < rate_error_prev) { + mul_best_fit = mul; + div_best_fit = div; + rate_error_prev = rate_error; + } + + if (rate_error == 0) + break; + } + + if (div_best_fit == 0) + return -EINVAL; + + ctrl |= PM_BF(PLLMUL, mul_best_fit - 1); + ctrl |= PM_BF(PLLDIV, div_best_fit - 1); + ctrl |= PM_BF(PLLCOUNT, 16); + + if (clk->parent == &osc1) + ctrl |= PM_BIT(PLLOSC); + + *pll_ctrl = ctrl; + + return actual; +} + static unsigned long pll0_get_rate(struct clk *clk) { u32 control; @@ -129,6 +196,41 @@ static unsigned long pll0_get_rate(struct clk *clk) return pll_get_rate(clk, control); } +static void pll1_mode(struct clk *clk, int enabled) +{ + unsigned long timeout; + u32 status; + u32 ctrl; + + ctrl = pm_readl(PLL1); + + if (enabled) { + if (!PM_BFEXT(PLLMUL, ctrl) && !PM_BFEXT(PLLDIV, ctrl)) { + pr_debug("clk %s: failed to enable, rate not set\n", + clk->name); + return; + } + + ctrl |= PM_BIT(PLLEN); + pm_writel(PLL1, ctrl); + + /* Wait for PLL lock. */ + for (timeout = 10000; timeout; timeout--) { + status = pm_readl(ISR); + if (status & PM_BIT(LOCK1)) + break; + udelay(10); + } + + if (!(status & PM_BIT(LOCK1))) + printk(KERN_ERR "clk %s: timeout waiting for lock\n", + clk->name); + } else { + ctrl &= ~PM_BIT(PLLEN); + pm_writel(PLL1, ctrl); + } +} + static unsigned long pll1_get_rate(struct clk *clk) { u32 control; @@ -138,6 +240,49 @@ static unsigned long pll1_get_rate(struct clk *clk) return pll_get_rate(clk, control); } +static long pll1_set_rate(struct clk *clk, unsigned long rate, int apply) +{ + u32 ctrl = 0; + unsigned long actual_rate; + + actual_rate = pll_set_rate(clk, rate, &ctrl); + + if (apply) { + if (actual_rate != rate) + return -EINVAL; + if (clk->users > 0) + return -EBUSY; + pr_debug(KERN_INFO "clk %s: new rate %lu (actual rate %lu)\n", + clk->name, rate, actual_rate); + pm_writel(PLL1, ctrl); + } + + return actual_rate; +} + +static int pll1_set_parent(struct clk *clk, struct clk *parent) +{ + u32 ctrl; + + if (clk->users > 0) + return -EBUSY; + + ctrl = pm_readl(PLL1); + WARN_ON(ctrl & PM_BIT(PLLEN)); + + if (parent == &osc0) + ctrl &= ~PM_BIT(PLLOSC); + else if (parent == &osc1) + ctrl |= PM_BIT(PLLOSC); + else + return -EINVAL; + + pm_writel(PLL1, ctrl); + clk->parent = parent; + + return 0; +} + /* * The AT32AP7000 has five primary clock sources: One 32kHz * oscillator, two crystal oscillators and two PLLs. @@ -166,7 +311,10 @@ static struct clk pll0 = { }; static struct clk pll1 = { .name = "pll1", + .mode = pll1_mode, .get_rate = pll1_get_rate, + .set_rate = pll1_set_rate, + .set_parent = pll1_set_parent, .parent = &osc0, }; @@ -605,19 +753,32 @@ static inline void set_ebi_sfr_bits(u32 mask) } /* -------------------------------------------------------------------- - * System Timer/Counter (TC) + * Timer/Counter (TC) * -------------------------------------------------------------------- */ -static struct resource at32_systc0_resource[] = { + +static struct resource at32_tcb0_resource[] = { PBMEM(0xfff00c00), IRQ(22), }; -struct platform_device at32_systc0_device = { - .name = "systc", +static struct platform_device at32_tcb0_device = { + .name = "atmel_tcb", .id = 0, - .resource = at32_systc0_resource, - .num_resources = ARRAY_SIZE(at32_systc0_resource), + .resource = at32_tcb0_resource, + .num_resources = ARRAY_SIZE(at32_tcb0_resource), }; -DEV_CLK(pclk, at32_systc0, pbb, 3); +DEV_CLK(t0_clk, at32_tcb0, pbb, 3); + +static struct resource at32_tcb1_resource[] = { + PBMEM(0xfff01000), + IRQ(23), +}; +static struct platform_device at32_tcb1_device = { + .name = "atmel_tcb", + .id = 1, + .resource = at32_tcb1_resource, + .num_resources = ARRAY_SIZE(at32_tcb1_resource), +}; +DEV_CLK(t0_clk, at32_tcb1, pbb, 4); /* -------------------------------------------------------------------- * PIO @@ -669,7 +830,8 @@ void __init at32_add_system_devices(void) platform_device_register(&pdc_device); platform_device_register(&dmaca0_device); - platform_device_register(&at32_systc0_device); + platform_device_register(&at32_tcb0_device); + platform_device_register(&at32_tcb1_device); platform_device_register(&pio0_device); platform_device_register(&pio1_device); @@ -989,7 +1151,9 @@ static struct clk atmel_twi0_pclk = { .index = 2, }; -struct platform_device *__init at32_add_device_twi(unsigned int id) +struct platform_device *__init at32_add_device_twi(unsigned int id, + struct i2c_board_info *b, + unsigned int n) { struct platform_device *pdev; @@ -1009,6 +1173,9 @@ struct platform_device *__init at32_add_device_twi(unsigned int id) atmel_twi0_pclk.dev = &pdev->dev; + if (b) + i2c_register_board_info(id, b, n); + platform_device_add(pdev); return pdev; @@ -1351,9 +1518,39 @@ static struct clk usba0_hclk = { .index = 6, }; +#define EP(nam, idx, maxpkt, maxbk, dma, isoc) \ + [idx] = { \ + .name = nam, \ + .index = idx, \ + .fifo_size = maxpkt, \ + .nr_banks = maxbk, \ + .can_dma = dma, \ + .can_isoc = isoc, \ + } + +static struct usba_ep_data at32_usba_ep[] __initdata = { + EP("ep0", 0, 64, 1, 0, 0), + EP("ep1", 1, 512, 2, 1, 1), + EP("ep2", 2, 512, 2, 1, 1), + EP("ep3-int", 3, 64, 3, 1, 0), + EP("ep4-int", 4, 64, 3, 1, 0), + EP("ep5", 5, 1024, 3, 1, 1), + EP("ep6", 6, 1024, 3, 1, 1), +}; + +#undef EP + struct platform_device *__init at32_add_device_usba(unsigned int id, struct usba_platform_data *data) { + /* + * pdata doesn't have room for any endpoints, so we need to + * append room for the ones we need right after it. + */ + struct { + struct usba_platform_data pdata; + struct usba_ep_data ep[7]; + } usba_data; struct platform_device *pdev; if (id != 0) @@ -1367,13 +1564,20 @@ at32_add_device_usba(unsigned int id, struct usba_platform_data *data) ARRAY_SIZE(usba0_resource))) goto out_free_pdev; - if (data) { - if (platform_device_add_data(pdev, data, sizeof(*data))) - goto out_free_pdev; + if (data) + usba_data.pdata.vbus_pin = data->vbus_pin; + else + usba_data.pdata.vbus_pin = -EINVAL; + + data = &usba_data.pdata; + data->num_ep = ARRAY_SIZE(at32_usba_ep); + memcpy(data->ep, at32_usba_ep, sizeof(at32_usba_ep)); - if (data->vbus_pin != GPIO_PIN_NONE) - at32_select_gpio(data->vbus_pin, 0); - } + if (platform_device_add_data(pdev, data, sizeof(usba_data))) + goto out_free_pdev; + + if (data->vbus_pin >= 0) + at32_select_gpio(data->vbus_pin, 0); usba0_pclk.dev = &pdev->dev; usba0_hclk.dev = &pdev->dev; @@ -1694,7 +1898,8 @@ struct clk *at32_clock_list[] = { &pio2_mck, &pio3_mck, &pio4_mck, - &at32_systc0_pclk, + &at32_tcb0_t0_clk, + &at32_tcb1_t0_clk, &atmel_usart0_usart, &atmel_usart1_usart, &atmel_usart2_usart, diff --git a/arch/avr32/mach-at32ap/intc.c b/arch/avr32/mach-at32ap/intc.c index 0b286cd5302..097cf4e8405 100644 --- a/arch/avr32/mach-at32ap/intc.c +++ b/arch/avr32/mach-at32ap/intc.c @@ -13,7 +13,6 @@ #include <linux/irq.h> #include <linux/platform_device.h> -#include <asm/intc.h> #include <asm/io.h> #include "intc.h" diff --git a/arch/avr32/mach-at32ap/pm-at32ap700x.S b/arch/avr32/mach-at32ap/pm-at32ap700x.S new file mode 100644 index 00000000000..949e2485e27 --- /dev/null +++ b/arch/avr32/mach-at32ap/pm-at32ap700x.S @@ -0,0 +1,66 @@ +/* + * Low-level Power Management code. + * + * Copyright (C) 2008 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 <asm/asm.h> +#include <asm/asm-offsets.h> +#include <asm/thread_info.h> +#include <asm/arch/pm.h> + + .section .bss, "wa", @nobits + .global disable_idle_sleep + .type disable_idle_sleep, @object +disable_idle_sleep: + .int 4 + .size disable_idle_sleep, . - disable_idle_sleep + + /* Keep this close to the irq handlers */ + .section .irq.text, "ax", @progbits + + /* + * void cpu_enter_idle(void) + * + * Put the CPU into "idle" mode, in which it will consume + * significantly less power. + * + * If an interrupt comes along in the window between + * unmask_interrupts and the sleep instruction below, the + * interrupt code will adjust the return address so that we + * never execute the sleep instruction. This is required + * because the AP7000 doesn't unmask interrupts when entering + * sleep modes; later CPUs may not need this workaround. + */ + .global cpu_enter_idle + .type cpu_enter_idle, @function +cpu_enter_idle: + mask_interrupts + get_thread_info r8 + ld.w r9, r8[TI_flags] + bld r9, TIF_NEED_RESCHED + brcs .Lret_from_sleep + sbr r9, TIF_CPU_GOING_TO_SLEEP + st.w r8[TI_flags], r9 + unmask_interrupts + sleep CPU_SLEEP_IDLE + .size cpu_idle_sleep, . - cpu_idle_sleep + + /* + * Common return path for PM functions that don't run from + * SRAM. + */ + .global cpu_idle_skip_sleep + .type cpu_idle_skip_sleep, @function +cpu_idle_skip_sleep: + mask_interrupts + ld.w r9, r8[TI_flags] + cbr r9, TIF_CPU_GOING_TO_SLEEP + st.w r8[TI_flags], r9 +.Lret_from_sleep: + unmask_interrupts + retal r12 + .size cpu_idle_skip_sleep, . - cpu_idle_skip_sleep diff --git a/arch/avr32/mach-at32ap/time-tc.c b/arch/avr32/mach-at32ap/time-tc.c deleted file mode 100644 index 10265863c98..00000000000 --- a/arch/avr32/mach-at32ap/time-tc.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2004-2007 Atmel Corporation - * - * Based on MIPS implementation arch/mips/kernel/time.c - * Copyright 2001 MontaVista Software Inc. - * - * 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 <linux/clk.h> -#include <linux/clocksource.h> -#include <linux/time.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/kernel_stat.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/profile.h> -#include <linux/sysdev.h> -#include <linux/err.h> - -#include <asm/div64.h> -#include <asm/sysreg.h> -#include <asm/io.h> -#include <asm/sections.h> - -#include <asm/arch/time.h> - -/* how many counter cycles in a jiffy? */ -static u32 cycles_per_jiffy; - -/* the count value for the next timer interrupt */ -static u32 expirelo; - -/* the I/O registers of the TC module */ -static void __iomem *ioregs; - -cycle_t read_cycle_count(void) -{ - return (cycle_t)timer_read(ioregs, 0, CV); -} - -struct clocksource clocksource_avr32 = { - .name = "avr32", - .rating = 342, - .read = read_cycle_count, - .mask = CLOCKSOURCE_MASK(16), - .shift = 16, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, -}; - -static void avr32_timer_ack(void) -{ - u16 count = expirelo; - - /* Ack this timer interrupt and set the next one, use a u16 - * variable so it will wrap around correctly */ - count += cycles_per_jiffy; - expirelo = count; - timer_write(ioregs, 0, RC, expirelo); - - /* Check to see if we have missed any timer interrupts */ - count = timer_read(ioregs, 0, CV); - if ((count - expirelo) < 0x7fff) { - expirelo = count + cycles_per_jiffy; - timer_write(ioregs, 0, RC, expirelo); - } -} - -u32 avr32_hpt_read(void) -{ - return timer_read(ioregs, 0, CV); -} - -static int avr32_timer_calc_div_and_set_jiffies(struct clk *pclk) -{ - unsigned int cycles_max = (clocksource_avr32.mask + 1) / 2; - unsigned int divs[] = { 4, 8, 16, 32 }; - int divs_size = ARRAY_SIZE(divs); - int i = 0; - unsigned long count_hz; - unsigned long shift; - unsigned long mult; - int clock_div = -1; - u64 tmp; - - shift = clocksource_avr32.shift; - - do { - count_hz = clk_get_rate(pclk) / divs[i]; - mult = clocksource_hz2mult(count_hz, shift); - clocksource_avr32.mult = mult; - - tmp = TICK_NSEC; - tmp <<= shift; - tmp += mult / 2; - do_div(tmp, mult); - - cycles_per_jiffy = tmp; - } while (cycles_per_jiffy > cycles_max && ++i < divs_size); - - clock_div = i + 1; - - if (clock_div > divs_size) { - pr_debug("timer: could not calculate clock divider\n"); - return -EFAULT; - } - - /* Set the clock divider */ - timer_write(ioregs, 0, CMR, TIMER_BF(CMR_TCCLKS, clock_div)); - - return 0; -} - -int avr32_hpt_init(unsigned int count) -{ - struct resource *regs; - struct clk *pclk; - int irq = -1; - int ret = 0; - - ret = -ENXIO; - - irq = platform_get_irq(&at32_systc0_device, 0); - if (irq < 0) { - pr_debug("timer: could not get irq\n"); - goto out_error; - } - - pclk = clk_get(&at32_systc0_device.dev, "pclk"); - if (IS_ERR(pclk)) { - pr_debug("timer: could not get clk: %ld\n", PTR_ERR(pclk)); - goto out_error; - } - clk_enable(pclk); - - regs = platform_get_resource(&at32_systc0_device, IORESOURCE_MEM, 0); - if (!regs) { - pr_debug("timer: could not get resource\n"); - goto out_error_clk; - } - - ioregs = ioremap(regs->start, regs->end - regs->start + 1); - if (!ioregs) { - pr_debug("timer: could not get ioregs\n"); - goto out_error_clk; - } - - ret = avr32_timer_calc_div_and_set_jiffies(pclk); - if (ret) - goto out_error_io; - - ret = setup_irq(irq, &timer_irqaction); - if (ret) { - pr_debug("timer: could not request irq %d: %d\n", - irq, ret); - goto out_error_io; - } - - expirelo = (timer_read(ioregs, 0, CV) / cycles_per_jiffy + 1) - * cycles_per_jiffy; - - /* Enable clock and interrupts on RC compare */ - timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_CLKEN)); - timer_write(ioregs, 0, IER, TIMER_BIT(IER_CPCS)); - /* Set cycles to first interrupt */ - timer_write(ioregs, 0, RC, expirelo); - - printk(KERN_INFO "timer: AT32AP system timer/counter at 0x%p irq %d\n", - ioregs, irq); - - return 0; - -out_error_io: - iounmap(ioregs); -out_error_clk: - clk_put(pclk); -out_error: - return ret; -} - -int avr32_hpt_start(void) -{ - timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_SWTRG)); - return 0; -} - -irqreturn_t timer_interrupt(int irq, void *dev_id) -{ - unsigned int sr = timer_read(ioregs, 0, SR); - - if (sr & TIMER_BIT(SR_CPCS)) { - /* ack timer interrupt and try to set next interrupt */ - avr32_timer_ack(); - - /* - * Call the generic timer interrupt handler - */ - write_seqlock(&xtime_lock); - do_timer(1); - write_sequnlock(&xtime_lock); - - /* - * In UP mode, we call local_timer_interrupt() to do profiling - * and process accounting. - * - * SMP is not supported yet. - */ - local_timer_interrupt(irq, dev_id); - - return IRQ_HANDLED; - } - - return IRQ_NONE; -} |