[PATCH 3.4 0/8] Core unification, dynamic memory reserve and cpu-freq patches v2

534 views
Skip to first unread message

Hans de Goede

unread,
Feb 9, 2013, 10:47:51 AM2/9/13
to linux...@googlegroups.com
Here is v2 of my core unification, dynamic memory reservation and
cpu-freq patches. Changes since the previous posting are:

1) Leave the fb at a fixed address when the mali driver is used, since it
has the address <beep> hardcoded.

2) Allow the disp/fb driver to still work when no fb memory is reserved by
falling back to kmalloc

3) Added a new version of my cpu-freq limits tweaking patch, which should make
everyone happy

Regards,

Hans

Hans de Goede

unread,
Feb 9, 2013, 10:47:52 AM2/9/13
to linux...@googlegroups.com, Hans de Goede
It is identical for sun4i and sun5i, so no merging was needed.

Note checkpatch very much dislikes the code, but this patch does not change it,
it just moves it around.

Signed-off-by: Hans de Goede <hdeg...@redhat.com>
---
arch/arm/mach-sun4i/clock/Makefile | 4 +-
arch/arm/mach-sun4i/clock/aw_clocksrc.c | 365 --------------------------------
arch/arm/mach-sun4i/clock/aw_clocksrc.h | 90 --------
arch/arm/mach-sun5i/clock/Makefile | 3 +-
arch/arm/mach-sun5i/clock/aw_clocksrc.c | 365 --------------------------------
arch/arm/mach-sun5i/clock/aw_clocksrc.h | 90 --------
arch/arm/plat-sunxi/Makefile | 1 +
arch/arm/plat-sunxi/clocksrc.c | 365 ++++++++++++++++++++++++++++++++
arch/arm/plat-sunxi/clocksrc.h | 90 ++++++++
9 files changed, 458 insertions(+), 915 deletions(-)
delete mode 100644 arch/arm/mach-sun4i/clock/aw_clocksrc.c
delete mode 100644 arch/arm/mach-sun4i/clock/aw_clocksrc.h
delete mode 100644 arch/arm/mach-sun5i/clock/aw_clocksrc.c
delete mode 100644 arch/arm/mach-sun5i/clock/aw_clocksrc.h
create mode 100644 arch/arm/plat-sunxi/clocksrc.c
create mode 100644 arch/arm/plat-sunxi/clocksrc.h

diff --git a/arch/arm/mach-sun4i/clock/Makefile b/arch/arm/mach-sun4i/clock/Makefile
index 81d1da3..f72519b 100644
--- a/arch/arm/mach-sun4i/clock/Makefile
+++ b/arch/arm/mach-sun4i/clock/Makefile
@@ -1,4 +1,2 @@
obj-y += ccmu/
-obj-y += clock.o \
- aw_clocksrc.o
-
+obj-y += clock.o
diff --git a/arch/arm/mach-sun4i/clock/aw_clocksrc.c b/arch/arm/mach-sun4i/clock/aw_clocksrc.c
deleted file mode 100644
index 8d50710..0000000
--- a/arch/arm/mach-sun4i/clock/aw_clocksrc.c
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * arch/arm/mach-sun4i/clock/aw_clocksrc.c
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Kevin Zhang <ke...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <mach/hardware.h>
-#include <mach/platform.h>
-#include <linux/init.h>
-#include <linux/clocksource.h>
-#include <linux/clockchips.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/delay.h>
-#include "aw_clocksrc.h"
-
-#undef CLKSRC_DBG
-#undef CLKSRC_ERR
-#if (0)
- #define CLKSRC_DBG(format,args...) printk("[CLKSRC] "format,##args)
- #define CLKSRC_ERR(format,args...) printk("[CLKSRC] "format,##args)
-#else
- #define CLKSRC_DBG(...)
- #define CLKSRC_ERR(...)
-#endif
-
-static cycle_t aw_clksrc_read(struct clocksource *cs);
-#ifdef CONFIG_HIGH_RES_TIMERS
-static irqreturn_t aw_clkevt_irq(int irq, void *handle);
-static spinlock_t timer1_spin_lock;
-static void aw_set_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev);
-static int aw_set_next_clkevt(unsigned long delta, struct clock_event_device *dev);
-#endif
-
-static struct clocksource aw_clocksrc =
-{
- .name = "aw 64bits couter",
- .list = {NULL, NULL},
- .rating = 300, /* perfect clock source */
- .read = aw_clksrc_read, /* read clock counter */
- .enable = 0, /* not define */
- .disable = 0, /* not define */
- .mask = CLOCKSOURCE_MASK(64), /* 64bits mask */
- .mult = 0, /* it will be calculated by shift */
- .shift = 10, /* 32bit shift for */
- .max_idle_ns = 1000000000000ULL,
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
-};
-
-#ifdef CONFIG_HIGH_RES_TIMERS
-static struct clock_event_device aw_clock_event =
-{
- .name = "aw clock event device",
- .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
- .max_delta_ns = 100000000000ULL,
- .min_delta_ns = (1000000000 + AW_HPET_CLOCK_EVENT_HZ - 1) / AW_HPET_CLOCK_EVENT_HZ,
- .mult = 100, /* will be calculate when init */
- .shift = 32,
- .rating = 300, /* clock event is perfect */
- .irq = SW_INT_IRQNO_TIMER1,
- .cpumask = 0, /* will be set when init */
- .set_next_event = aw_set_next_clkevt,
- .set_mode = aw_set_clkevt_mode, /* set clock event mode */
- .event_handler = 0, /* be alloced by system framework */
-};
-
-static struct irqaction aw_clkevt_irqact =
-{
- .handler = aw_clkevt_irq,
- .flags = IRQF_TIMER | IRQF_DISABLED,
- .name = "aw clock event irq",
- .dev_id = &aw_clock_event,
- .irq = SW_INT_IRQNO_TIMER1,
-};
-#endif
-
-
-/*
-*********************************************************************************************************
-* aw_clksrc_read
-*
-*Description: read cycle count of the clock source;
-*
-*Arguments : cs clock source handle.
-*
-*Return : cycle count;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static cycle_t aw_clksrc_read(struct clocksource *cs)
-{
- unsigned long flags;
- __u32 lower, upper;
-
- /* disable interrupt response */
- raw_local_irq_save(flags);
-
- /* latch 64bit counter and wait ready for read */
- TMR_REG_CNT64_CTL |= (1<<1);
- while(TMR_REG_CNT64_CTL & (1<<1));
-
- /* read the 64bits counter */
- lower = TMR_REG_CNT64_LO;
- upper = TMR_REG_CNT64_HI;
-
- /* restore interrupt response */
- raw_local_irq_restore(flags);
-
- return (((__u64)upper)<<32) | lower;
-}
-
-
-/*
-*********************************************************************************************************
-* aw_set_clkevt_mode
-*
-*Description: set clock event work mode.
-*
-*Arguments : mode mode for clock event work;
-* dev clock event device;
-*
-*Return : none
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-#ifdef CONFIG_HIGH_RES_TIMERS
-static void aw_set_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
-{
- CLKSRC_DBG("aw_set_clkevt_mode:%u\n", mode);
- switch (mode)
- {
- case CLOCK_EVT_MODE_PERIODIC:
- {
- /* set timer work with continueous mode */
- TMR_REG_TMR1_CTL &= ~(1<<0);
- /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
- __delay(50);
- TMR_REG_TMR1_CTL &= ~(1<<7);
- TMR_REG_TMR1_CTL |= (1<<0);
- break;
- }
-
- case CLOCK_EVT_MODE_ONESHOT:
- {
- /* set timer work with onshot mode */
- TMR_REG_TMR1_CTL &= ~(1<<0);
- /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
- __delay(50);
- TMR_REG_TMR1_CTL |= (1<<7);
- TMR_REG_TMR1_CTL |= (1<<0);
- break;
- }
-
- default:
- {
- /* disable clock event device */
- TMR_REG_TMR1_CTL &= ~(1<<0);
- /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
- __delay(50);
- break;
- }
- }
-}
-#endif
-
-
-/*
-*********************************************************************************************************
-* aw_set_next_clkevt
-*
-*Description: set next clock event.
-*
-*Arguments : delta cycle count for next clock event.
-* dev clock event device.
-*
-*Return : result,
-* 0, set next event successed;
-* !0, set next event failed;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-#ifdef CONFIG_HIGH_RES_TIMERS
-static int aw_set_next_clkevt(unsigned long delta, struct clock_event_device *dev)
-{
- unsigned long flags;
- CLKSRC_DBG("aw_set_next_clkevt: %u\n", (unsigned int)delta);
-
- spin_lock_irqsave(&timer1_spin_lock, flags);
- /* disable timer and clear pending first */
- TMR_REG_TMR1_CTL &= ~(1<<0);
- /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
- udelay(1);
-
- /* set timer intervalue */
- TMR_REG_TMR1_INTV = delta;
- /* reload the timer intervalue */
- TMR_REG_TMR1_CTL |= (1<<1);
-
- /* enable timer */
- TMR_REG_TMR1_CTL |= (1<<0);
- spin_unlock_irqrestore(&timer1_spin_lock, flags);
- return 0;
-}
-#endif
-
-/*
-*********************************************************************************************************
-* aw_clkevt_irq
-*
-*Description: clock event interrupt handler.
-*
-*Arguments : irq interrupt number of current processed;
-* handle device handle registered when setup irq;
-*
-*Return : result,
-* IRQ_HANDLED, irq is processed successed;
-* IRQ_NONE, irq is not setup by us;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-#ifdef CONFIG_HIGH_RES_TIMERS
-static irqreturn_t aw_clkevt_irq(int irq, void *handle)
-{
- if(TMR_REG_IRQ_STAT & (1<<1))
- {
- CLKSRC_DBG("aw_clkevt_irq!\n");
- /* clear pending */
- TMR_REG_IRQ_STAT = (1<<1);
-
- /* clock event interrupt handled */
- if(likely(aw_clock_event.event_handler != NULL))
- aw_clock_event.event_handler(&aw_clock_event);
-
- return IRQ_HANDLED;
- }
-
- return IRQ_NONE;
-}
-#endif
-
-
-/*
-*********************************************************************************************************
-* aw_clksrc_init
-*
-*Description: clock source initialise.
-*
-*Arguments : none
-*
-*Return : result,
-* 0, initiate successed;
-* !0, initiate failed;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int __init aw_clksrc_init(void)
-{
- CLKSRC_DBG("all-winners clock source init!\n");
- /* we use 64bits counter as HPET(High Precision Event Timer) */
- TMR_REG_CNT64_CTL = 0;
- __delay(50);
- /* config clock source for 64bits counter */
- #if(AW_HPET_CLK_SRC == TMR_CLK_SRC_24MHOSC)
- TMR_REG_CNT64_CTL |= (0<<2);
- #else
- TMR_REG_CNT64_CTL |= (1<<2);
- #endif
- __delay(50);
- /* clear 64bits counter */
- TMR_REG_CNT64_CTL |= (1<<0);
- __delay(50);
- CLKSRC_DBG("register all-winners clock source!\n");
- /* calculate the mult by shift */
- aw_clocksrc.mult = clocksource_hz2mult(AW_HPET_CLOCK_SOURCE_HZ, aw_clocksrc.shift);
- /* register clock source */
- clocksource_register(&aw_clocksrc);
-
- return 0;
-}
-
-/*
-*********************************************************************************************************
-* aw_clkevt_init
-*
-*Description: clock event initialise.
-*
-*Arguments : none
-*
-*Return : result,
-* 0, initiate successed;
-* !0, initiate failed;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-#ifdef CONFIG_HIGH_RES_TIMERS
-static int __init aw_clkevt_init(void)
-{
- /* register clock event irq */
- CLKSRC_DBG("set up all-winners clock event irq!\n");
- /* clear timer1 setting */
- TMR_REG_TMR1_CTL = 0;
- /* initialise timer inter value to 1 tick */
- TMR_REG_TMR1_INTV = AW_HPET_CLOCK_EVENT_HZ/HZ;
-
- /* config clock source for timer1 */
- #if(AW_HPET_CLK_EVT == TMR_CLK_SRC_24MHOSC)
- TMR_REG_TMR1_CTL |= (1<<2);
- #else
- TMR_REG_TMR1_CTL |= (0<<2);
- #endif
- /* reload inter value */
- TMR_REG_TMR1_CTL |= (1<<1);
- /* install timer irq */
- setup_irq(SW_INT_IRQNO_TIMER1, &aw_clkevt_irqact);
- /* enable timer1 irq */
- TMR_REG_IRQ_EN |= (1<<1);
-
- /* register clock event device */
- CLKSRC_DBG("register all-winners clock event device!\n");
- aw_clock_event.mult = div_sc(AW_HPET_CLOCK_EVENT_HZ, NSEC_PER_SEC, aw_clock_event.shift);
- aw_clock_event.max_delta_ns = clockevent_delta2ns((0x80000000), &aw_clock_event);
- /* time value timer must larger than 50 cycles at least, suggested by david 2011-5-25 11:41 */
- aw_clock_event.min_delta_ns = clockevent_delta2ns(1, &aw_clock_event) + 100000;
- aw_clock_event.cpumask = cpumask_of(0);
- clockevents_register_device(&aw_clock_event);
-
- return 0;
-}
-#endif
-
-arch_initcall(aw_clksrc_init);
-#ifdef CONFIG_HIGH_RES_TIMERS
-arch_initcall(aw_clkevt_init);
-#endif
diff --git a/arch/arm/mach-sun4i/clock/aw_clocksrc.h b/arch/arm/mach-sun4i/clock/aw_clocksrc.h
deleted file mode 100644
index 7def8ab..0000000
--- a/arch/arm/mach-sun4i/clock/aw_clocksrc.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * arch/arm/mach-sun4i/clock/aw_clocksrc.h
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Kevin Zhang <ke...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#ifndef __AW_CLOCKSRC_H__
-#define __AW_CLOCKSRC_H__
-
-#ifndef __tmr_reg
- #define __tmr_reg(x) (*(volatile __u32 *)(x))
-#endif /*#ifndef __tmr_reg */
-
-
-/* define timer io base on aw chips */
-#define AW_TMR_IO_BASE SW_VA_TIMERC_IO_BASE
-/* define timer io register address */
-#define TMR_REG_o_IRQ_EN (AW_TMR_IO_BASE + 0x0000)
-#define TMR_REG_o_IRQ_STAT (AW_TMR_IO_BASE + 0x0004)
-#define TMR_REG_o_TMR1_CTL (AW_TMR_IO_BASE + 0x0020)
-#define TMR_REG_o_TMR1_INTV (AW_TMR_IO_BASE + 0x0024)
-#define TMR_REG_o_TMR1_CUR (AW_TMR_IO_BASE + 0x0028)
-#define TMR_REG_o_CNT64_CTL (AW_TMR_IO_BASE + 0x00A0)
-#define TMR_REG_o_CNT64_LO (AW_TMR_IO_BASE + 0x00A4)
-#define TMR_REG_o_CNT64_HI (AW_TMR_IO_BASE + 0x00A8)
-/* define timer io register value */
-#define TMR_REG_IRQ_EN __tmr_reg(TMR_REG_o_IRQ_EN )
-#define TMR_REG_IRQ_STAT __tmr_reg(TMR_REG_o_IRQ_STAT )
-#define TMR_REG_TMR1_CTL __tmr_reg(TMR_REG_o_TMR1_CTL )
-#define TMR_REG_TMR1_INTV __tmr_reg(TMR_REG_o_TMR1_INTV)
-#define TMR_REG_TMR1_CUR __tmr_reg(TMR_REG_o_TMR1_CUR )
-#define TMR_REG_CNT64_CTL __tmr_reg(TMR_REG_o_CNT64_CTL)
-#define TMR_REG_CNT64_LO __tmr_reg(TMR_REG_o_CNT64_LO )
-#define TMR_REG_CNT64_HI __tmr_reg(TMR_REG_o_CNT64_HI )
-
-
-/* define timer clock source */
-#define TMR_CLK_SRC_32KLOSC (0)
-#define TMR_CLK_SRC_24MHOSC (1)
-#define TMR_CLK_SRC_PLL (2)
-
-
-/* config clock frequency */
-#define AW_HPET_CLK_SRC TMR_CLK_SRC_24MHOSC
-#define AW_HPET_CLK_EVT TMR_CLK_SRC_24MHOSC
-
-
-/* aw HPET clock source frequency */
-#ifndef AW_HPET_CLK_SRC
- #error "AW_HPET_CLK_SRC is not define!!"
-#endif
-#if(AW_HPET_CLK_SRC == TMR_CLK_SRC_24MHOSC)
- #define AW_HPET_CLOCK_SOURCE_HZ (24000000)
-#else
- #error "AW_HPET_CLK_SRC config is invalid!!"
-#endif
-
-
-/* aw HPET clock eventy frequency */
-#ifndef AW_HPET_CLK_EVT
- #error "AW_HPET_CLK_EVT is not define!!"
-#endif
-#if(AW_HPET_CLK_EVT == TMR_CLK_SRC_32KLOSC)
- #define AW_HPET_CLOCK_EVENT_HZ (32768)
-#elif(AW_HPET_CLK_EVT == TMR_CLK_SRC_24MHOSC)
- #define AW_HPET_CLOCK_EVENT_HZ (24000000)
-#else
- #error "AW_HPET_CLK_EVT config is invalid!!"
-#endif
-
-
-#endif /* #ifndef __AW_CLOCKSRC_H__ */
-
diff --git a/arch/arm/mach-sun5i/clock/Makefile b/arch/arm/mach-sun5i/clock/Makefile
index e3a35e9..f72519b 100644
--- a/arch/arm/mach-sun5i/clock/Makefile
+++ b/arch/arm/mach-sun5i/clock/Makefile
@@ -1,3 +1,2 @@
obj-y += ccmu/
-obj-y += clock.o \
- aw_clocksrc.o
+obj-y += clock.o
diff --git a/arch/arm/mach-sun5i/clock/aw_clocksrc.c b/arch/arm/mach-sun5i/clock/aw_clocksrc.c
deleted file mode 100644
index 46ee885..0000000
--- a/arch/arm/mach-sun5i/clock/aw_clocksrc.c
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * arch/arm/mach-sun5i/clock/ccmu/aw_clocksrc.c
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Kevin Zhang <ke...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <mach/hardware.h>
-#include <mach/platform.h>
-#include <linux/init.h>
-#include <linux/clocksource.h>
-#include <linux/clockchips.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/delay.h>
-#include "aw_clocksrc.h"
-
-#undef CLKSRC_DBG
-#undef CLKSRC_ERR
-#if (0)
- #define CLKSRC_DBG(format,args...) printk("[CLKSRC] "format,##args)
- #define CLKSRC_ERR(format,args...) printk("[CLKSRC] "format,##args)
-#else
- #define CLKSRC_DBG(...)
- #define CLKSRC_ERR(...)
-#endif
-
-static cycle_t aw_clksrc_read(struct clocksource *cs);
-#ifdef CONFIG_HIGH_RES_TIMERS
-static irqreturn_t aw_clkevt_irq(int irq, void *handle);
-static spinlock_t timer1_spin_lock;
-static void aw_set_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev);
-static int aw_set_next_clkevt(unsigned long delta, struct clock_event_device *dev);
-#endif
-
-static struct clocksource aw_clocksrc =
-{
- .name = "aw 64bits couter",
- .list = {NULL, NULL},
- .rating = 300, /* perfect clock source */
- .read = aw_clksrc_read, /* read clock counter */
- .enable = 0, /* not define */
- .disable = 0, /* not define */
- .mask = CLOCKSOURCE_MASK(64), /* 64bits mask */
- .mult = 0, /* it will be calculated by shift */
- .shift = 10, /* 32bit shift for */
- .max_idle_ns = 1000000000000ULL,
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
-};
-
-#ifdef CONFIG_HIGH_RES_TIMERS
-static struct clock_event_device aw_clock_event =
-{
- .name = "aw clock event device",
- .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
- .max_delta_ns = 100000000000ULL,
- .min_delta_ns = (1000000000 + AW_HPET_CLOCK_EVENT_HZ - 1) / AW_HPET_CLOCK_EVENT_HZ,
- .mult = 100, /* will be calculate when init */
- .shift = 32,
- .rating = 300, /* clock event is perfect */
- .irq = SW_INT_IRQNO_TIMER1,
- .cpumask = 0, /* will be set when init */
- .set_next_event = aw_set_next_clkevt,
- .set_mode = aw_set_clkevt_mode, /* set clock event mode */
- .event_handler = 0, /* be alloced by system framework */
-};
-
-static struct irqaction aw_clkevt_irqact =
-{
- .handler = aw_clkevt_irq,
- .flags = IRQF_TIMER | IRQF_DISABLED,
- .name = "aw clock event irq",
- .dev_id = &aw_clock_event,
- .irq = SW_INT_IRQNO_TIMER1,
-};
-#endif
-
-
-/*
-*********************************************************************************************************
-* aw_clksrc_read
-*
-*Description: read cycle count of the clock source;
-*
-*Arguments : cs clock source handle.
-*
-*Return : cycle count;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static cycle_t aw_clksrc_read(struct clocksource *cs)
-{
- unsigned long flags;
- __u32 lower, upper;
-
- /* disable interrupt response */
- raw_local_irq_save(flags);
-
- /* latch 64bit counter and wait ready for read */
- TMR_REG_CNT64_CTL |= (1<<1);
- while(TMR_REG_CNT64_CTL & (1<<1));
-
- /* read the 64bits counter */
- lower = TMR_REG_CNT64_LO;
- upper = TMR_REG_CNT64_HI;
-
- /* restore interrupt response */
- raw_local_irq_restore(flags);
-
- return (((__u64)upper)<<32) | lower;
-}
-
-
-/*
-*********************************************************************************************************
-* aw_set_clkevt_mode
-*
-*Description: set clock event work mode.
-*
-*Arguments : mode mode for clock event work;
-* dev clock event device;
-*
-*Return : none
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-#ifdef CONFIG_HIGH_RES_TIMERS
-static void aw_set_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
-{
- CLKSRC_DBG("aw_set_clkevt_mode:%u\n", mode);
- switch (mode)
- {
- case CLOCK_EVT_MODE_PERIODIC:
- {
- /* set timer work with continueous mode */
- TMR_REG_TMR1_CTL &= ~(1<<0);
- /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
- __delay(50);
- TMR_REG_TMR1_CTL &= ~(1<<7);
- TMR_REG_TMR1_CTL |= (1<<0);
- break;
- }
-
- case CLOCK_EVT_MODE_ONESHOT:
- {
- /* set timer work with onshot mode */
- TMR_REG_TMR1_CTL &= ~(1<<0);
- /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
- __delay(50);
- TMR_REG_TMR1_CTL |= (1<<7);
- TMR_REG_TMR1_CTL |= (1<<0);
- break;
- }
-
- default:
- {
- /* disable clock event device */
- TMR_REG_TMR1_CTL &= ~(1<<0);
- /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
- __delay(50);
- break;
- }
- }
-}
-#endif
-
-
-/*
-*********************************************************************************************************
-* aw_set_next_clkevt
-*
-*Description: set next clock event.
-*
-*Arguments : delta cycle count for next clock event.
-* dev clock event device.
-*
-*Return : result,
-* 0, set next event successed;
-* !0, set next event failed;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-#ifdef CONFIG_HIGH_RES_TIMERS
-static int aw_set_next_clkevt(unsigned long delta, struct clock_event_device *dev)
-{
- unsigned long flags;
- CLKSRC_DBG("aw_set_next_clkevt: %u\n", (unsigned int)delta);
-
- spin_lock_irqsave(&timer1_spin_lock, flags);
- /* disable timer and clear pending first */
- TMR_REG_TMR1_CTL &= ~(1<<0);
- /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
- udelay(1);
-
- /* set timer intervalue */
- TMR_REG_TMR1_INTV = delta;
- /* reload the timer intervalue */
- TMR_REG_TMR1_CTL |= (1<<1);
-
- /* enable timer */
- TMR_REG_TMR1_CTL |= (1<<0);
- spin_unlock_irqrestore(&timer1_spin_lock, flags);
- return 0;
-}
-#endif
-
-/*
-*********************************************************************************************************
-* aw_clkevt_irq
-*
-*Description: clock event interrupt handler.
-*
-*Arguments : irq interrupt number of current processed;
-* handle device handle registered when setup irq;
-*
-*Return : result,
-* IRQ_HANDLED, irq is processed successed;
-* IRQ_NONE, irq is not setup by us;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-#ifdef CONFIG_HIGH_RES_TIMERS
-static irqreturn_t aw_clkevt_irq(int irq, void *handle)
-{
- if(TMR_REG_IRQ_STAT & (1<<1))
- {
- CLKSRC_DBG("aw_clkevt_irq!\n");
- /* clear pending */
- TMR_REG_IRQ_STAT = (1<<1);
-
- /* clock event interrupt handled */
- if(likely(aw_clock_event.event_handler != NULL))
- aw_clock_event.event_handler(&aw_clock_event);
-
- return IRQ_HANDLED;
- }
-
- return IRQ_NONE;
-}
-#endif
-
-
-/*
-*********************************************************************************************************
-* aw_clksrc_init
-*
-*Description: clock source initialise.
-*
-*Arguments : none
-*
-*Return : result,
-* 0, initiate successed;
-* !0, initiate failed;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int __init aw_clksrc_init(void)
-{
- CLKSRC_DBG("all-winners clock source init!\n");
- /* we use 64bits counter as HPET(High Precision Event Timer) */
- TMR_REG_CNT64_CTL = 0;
- __delay(50);
- /* config clock source for 64bits counter */
- #if(AW_HPET_CLK_SRC == TMR_CLK_SRC_24MHOSC)
- TMR_REG_CNT64_CTL |= (0<<2);
- #else
- TMR_REG_CNT64_CTL |= (1<<2);
- #endif
- __delay(50);
- /* clear 64bits counter */
- TMR_REG_CNT64_CTL |= (1<<0);
- __delay(50);
- CLKSRC_DBG("register all-winners clock source!\n");
- /* calculate the mult by shift */
- aw_clocksrc.mult = clocksource_hz2mult(AW_HPET_CLOCK_SOURCE_HZ, aw_clocksrc.shift);
- /* register clock source */
- clocksource_register(&aw_clocksrc);
-
- return 0;
-}
-
-/*
-*********************************************************************************************************
-* aw_clkevt_init
-*
-*Description: clock event initialise.
-*
-*Arguments : none
-*
-*Return : result,
-* 0, initiate successed;
-* !0, initiate failed;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-#ifdef CONFIG_HIGH_RES_TIMERS
-static int __init aw_clkevt_init(void)
-{
- /* register clock event irq */
- CLKSRC_DBG("set up all-winners clock event irq!\n");
- /* clear timer1 setting */
- TMR_REG_TMR1_CTL = 0;
- /* initialise timer inter value to 1 tick */
- TMR_REG_TMR1_INTV = AW_HPET_CLOCK_EVENT_HZ/HZ;
-
- /* config clock source for timer1 */
- #if(AW_HPET_CLK_EVT == TMR_CLK_SRC_24MHOSC)
- TMR_REG_TMR1_CTL |= (1<<2);
- #else
- TMR_REG_TMR1_CTL |= (0<<2);
- #endif
- /* reload inter value */
- TMR_REG_TMR1_CTL |= (1<<1);
- /* install timer irq */
- setup_irq(SW_INT_IRQNO_TIMER1, &aw_clkevt_irqact);
- /* enable timer1 irq */
- TMR_REG_IRQ_EN |= (1<<1);
-
- /* register clock event device */
- CLKSRC_DBG("register all-winners clock event device!\n");
- aw_clock_event.mult = div_sc(AW_HPET_CLOCK_EVENT_HZ, NSEC_PER_SEC, aw_clock_event.shift);
- aw_clock_event.max_delta_ns = clockevent_delta2ns((0x80000000), &aw_clock_event);
- /* time value timer must larger than 50 cycles at least, suggested by david 2011-5-25 11:41 */
- aw_clock_event.min_delta_ns = clockevent_delta2ns(1, &aw_clock_event) + 100000;
- aw_clock_event.cpumask = cpumask_of(0);
- clockevents_register_device(&aw_clock_event);
-
- return 0;
-}
-#endif
-
-arch_initcall(aw_clksrc_init);
-#ifdef CONFIG_HIGH_RES_TIMERS
-arch_initcall(aw_clkevt_init);
-#endif
diff --git a/arch/arm/mach-sun5i/clock/aw_clocksrc.h b/arch/arm/mach-sun5i/clock/aw_clocksrc.h
deleted file mode 100644
index 8622ef6..0000000
--- a/arch/arm/mach-sun5i/clock/aw_clocksrc.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * arch/arm/mach-sun5i/clock/ccmu/aw_clocksrc.h
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Kevin Zhang <ke...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#ifndef __AW_CLOCKSRC_H__
-#define __AW_CLOCKSRC_H__
-
-#ifndef __tmr_reg
- #define __tmr_reg(x) (*(volatile __u32 *)(x))
-#endif /*#ifndef __tmr_reg */
-
-
-/* define timer io base on aw chips */
-#define AW_TMR_IO_BASE SW_VA_TIMERC_IO_BASE
-/* define timer io register address */
-#define TMR_REG_o_IRQ_EN (AW_TMR_IO_BASE + 0x0000)
-#define TMR_REG_o_IRQ_STAT (AW_TMR_IO_BASE + 0x0004)
-#define TMR_REG_o_TMR1_CTL (AW_TMR_IO_BASE + 0x0020)
-#define TMR_REG_o_TMR1_INTV (AW_TMR_IO_BASE + 0x0024)
-#define TMR_REG_o_TMR1_CUR (AW_TMR_IO_BASE + 0x0028)
-#define TMR_REG_o_CNT64_CTL (AW_TMR_IO_BASE + 0x00A0)
-#define TMR_REG_o_CNT64_LO (AW_TMR_IO_BASE + 0x00A4)
-#define TMR_REG_o_CNT64_HI (AW_TMR_IO_BASE + 0x00A8)
-/* define timer io register value */
-#define TMR_REG_IRQ_EN __tmr_reg(TMR_REG_o_IRQ_EN )
-#define TMR_REG_IRQ_STAT __tmr_reg(TMR_REG_o_IRQ_STAT )
-#define TMR_REG_TMR1_CTL __tmr_reg(TMR_REG_o_TMR1_CTL )
-#define TMR_REG_TMR1_INTV __tmr_reg(TMR_REG_o_TMR1_INTV)
-#define TMR_REG_TMR1_CUR __tmr_reg(TMR_REG_o_TMR1_CUR )
-#define TMR_REG_CNT64_CTL __tmr_reg(TMR_REG_o_CNT64_CTL)
-#define TMR_REG_CNT64_LO __tmr_reg(TMR_REG_o_CNT64_LO )
-#define TMR_REG_CNT64_HI __tmr_reg(TMR_REG_o_CNT64_HI )
-
-
-/* define timer clock source */
-#define TMR_CLK_SRC_32KLOSC (0)
-#define TMR_CLK_SRC_24MHOSC (1)
-#define TMR_CLK_SRC_PLL (2)
-
-
-/* config clock frequency */
-#define AW_HPET_CLK_SRC TMR_CLK_SRC_24MHOSC
-#define AW_HPET_CLK_EVT TMR_CLK_SRC_24MHOSC
-
-
-/* aw HPET clock source frequency */
-#ifndef AW_HPET_CLK_SRC
- #error "AW_HPET_CLK_SRC is not define!!"
-#endif
-#if(AW_HPET_CLK_SRC == TMR_CLK_SRC_24MHOSC)
- #define AW_HPET_CLOCK_SOURCE_HZ (24000000)
-#else
- #error "AW_HPET_CLK_SRC config is invalid!!"
-#endif
-
-
-/* aw HPET clock eventy frequency */
-#ifndef AW_HPET_CLK_EVT
- #error "AW_HPET_CLK_EVT is not define!!"
-#endif
-#if(AW_HPET_CLK_EVT == TMR_CLK_SRC_32KLOSC)
- #define AW_HPET_CLOCK_EVENT_HZ (32768)
-#elif(AW_HPET_CLK_EVT == TMR_CLK_SRC_24MHOSC)
- #define AW_HPET_CLOCK_EVENT_HZ (24000000)
-#else
- #error "AW_HPET_CLK_EVT config is invalid!!"
-#endif
-
-
-#endif /* #ifndef __AW_CLOCKSRC_H__ */
-
diff --git a/arch/arm/plat-sunxi/Makefile b/arch/arm/plat-sunxi/Makefile
index edc7795..936a3de 100644
--- a/arch/arm/plat-sunxi/Makefile
+++ b/arch/arm/plat-sunxi/Makefile
@@ -1,3 +1,4 @@
obj-y += sys_config.o
obj-y += core.o
obj-y += script.o
+obj-y += clocksrc.o
diff --git a/arch/arm/plat-sunxi/clocksrc.c b/arch/arm/plat-sunxi/clocksrc.c
new file mode 100644
index 0000000..6dcfff9
--- /dev/null
+++ b/arch/arm/plat-sunxi/clocksrc.c
@@ -0,0 +1,365 @@
+/*
+ * arch/arm/plat-sunxi/clocksrc.c
+ *
+ * (C) Copyright 2007-2012
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Kevin Zhang <ke...@allwinnertech.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <mach/hardware.h>
+#include <mach/platform.h>
+#include <linux/init.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include "clocksrc.h"
+
+#undef CLKSRC_DBG
+#undef CLKSRC_ERR
+#if (0)
+ #define CLKSRC_DBG(format,args...) printk("[CLKSRC] "format,##args)
+ #define CLKSRC_ERR(format,args...) printk("[CLKSRC] "format,##args)
+#else
+ #define CLKSRC_DBG(...)
+ #define CLKSRC_ERR(...)
+#endif
+
+static cycle_t aw_clksrc_read(struct clocksource *cs);
+#ifdef CONFIG_HIGH_RES_TIMERS
+static irqreturn_t aw_clkevt_irq(int irq, void *handle);
+static spinlock_t timer1_spin_lock;
+static void aw_set_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev);
+static int aw_set_next_clkevt(unsigned long delta, struct clock_event_device *dev);
+#endif
+
+static struct clocksource aw_clocksrc =
+{
+ .name = "aw 64bits couter",
+ .list = {NULL, NULL},
+ .rating = 300, /* perfect clock source */
+ .read = aw_clksrc_read, /* read clock counter */
+ .enable = 0, /* not define */
+ .disable = 0, /* not define */
+ .mask = CLOCKSOURCE_MASK(64), /* 64bits mask */
+ .mult = 0, /* it will be calculated by shift */
+ .shift = 10, /* 32bit shift for */
+ .max_idle_ns = 1000000000000ULL,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+static struct clock_event_device aw_clock_event =
+{
+ .name = "aw clock event device",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .max_delta_ns = 100000000000ULL,
+ .min_delta_ns = (1000000000 + AW_HPET_CLOCK_EVENT_HZ - 1) / AW_HPET_CLOCK_EVENT_HZ,
+ .mult = 100, /* will be calculate when init */
+ .shift = 32,
+ .rating = 300, /* clock event is perfect */
+ .irq = SW_INT_IRQNO_TIMER1,
+ .cpumask = 0, /* will be set when init */
+ .set_next_event = aw_set_next_clkevt,
+ .set_mode = aw_set_clkevt_mode, /* set clock event mode */
+ .event_handler = 0, /* be alloced by system framework */
+};
+
+static struct irqaction aw_clkevt_irqact =
+{
+ .handler = aw_clkevt_irq,
+ .flags = IRQF_TIMER | IRQF_DISABLED,
+ .name = "aw clock event irq",
+ .dev_id = &aw_clock_event,
+ .irq = SW_INT_IRQNO_TIMER1,
+};
+#endif
+
+
+/*
+*********************************************************************************************************
+* aw_clksrc_read
+*
+*Description: read cycle count of the clock source;
+*
+*Arguments : cs clock source handle.
+*
+*Return : cycle count;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static cycle_t aw_clksrc_read(struct clocksource *cs)
+{
+ unsigned long flags;
+ __u32 lower, upper;
+
+ /* disable interrupt response */
+ raw_local_irq_save(flags);
+
+ /* latch 64bit counter and wait ready for read */
+ TMR_REG_CNT64_CTL |= (1<<1);
+ while(TMR_REG_CNT64_CTL & (1<<1));
+
+ /* read the 64bits counter */
+ lower = TMR_REG_CNT64_LO;
+ upper = TMR_REG_CNT64_HI;
+
+ /* restore interrupt response */
+ raw_local_irq_restore(flags);
+
+ return (((__u64)upper)<<32) | lower;
+}
+
+
+/*
+*********************************************************************************************************
+* aw_set_clkevt_mode
+*
+*Description: set clock event work mode.
+*
+*Arguments : mode mode for clock event work;
+* dev clock event device;
+*
+*Return : none
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+#ifdef CONFIG_HIGH_RES_TIMERS
+static void aw_set_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
+{
+ CLKSRC_DBG("aw_set_clkevt_mode:%u\n", mode);
+ switch (mode)
+ {
+ case CLOCK_EVT_MODE_PERIODIC:
+ {
+ /* set timer work with continueous mode */
+ TMR_REG_TMR1_CTL &= ~(1<<0);
+ /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
+ __delay(50);
+ TMR_REG_TMR1_CTL &= ~(1<<7);
+ TMR_REG_TMR1_CTL |= (1<<0);
+ break;
+ }
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ {
+ /* set timer work with onshot mode */
+ TMR_REG_TMR1_CTL &= ~(1<<0);
+ /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
+ __delay(50);
+ TMR_REG_TMR1_CTL |= (1<<7);
+ TMR_REG_TMR1_CTL |= (1<<0);
+ break;
+ }
+
+ default:
+ {
+ /* disable clock event device */
+ TMR_REG_TMR1_CTL &= ~(1<<0);
+ /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
+ __delay(50);
+ break;
+ }
+ }
+}
+#endif
+
+
+/*
+*********************************************************************************************************
+* aw_set_next_clkevt
+*
+*Description: set next clock event.
+*
+*Arguments : delta cycle count for next clock event.
+* dev clock event device.
+*
+*Return : result,
+* 0, set next event successed;
+* !0, set next event failed;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+#ifdef CONFIG_HIGH_RES_TIMERS
+static int aw_set_next_clkevt(unsigned long delta, struct clock_event_device *dev)
+{
+ unsigned long flags;
+ CLKSRC_DBG("aw_set_next_clkevt: %u\n", (unsigned int)delta);
+
+ spin_lock_irqsave(&timer1_spin_lock, flags);
+ /* disable timer and clear pending first */
+ TMR_REG_TMR1_CTL &= ~(1<<0);
+ /* wait hardware synchronization, 2 cycles of the hardware work clock at least */
+ udelay(1);
+
+ /* set timer intervalue */
+ TMR_REG_TMR1_INTV = delta;
+ /* reload the timer intervalue */
+ TMR_REG_TMR1_CTL |= (1<<1);
+
+ /* enable timer */
+ TMR_REG_TMR1_CTL |= (1<<0);
+ spin_unlock_irqrestore(&timer1_spin_lock, flags);
+ return 0;
+}
+#endif
+
+/*
+*********************************************************************************************************
+* aw_clkevt_irq
+*
+*Description: clock event interrupt handler.
+*
+*Arguments : irq interrupt number of current processed;
+* handle device handle registered when setup irq;
+*
+*Return : result,
+* IRQ_HANDLED, irq is processed successed;
+* IRQ_NONE, irq is not setup by us;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+#ifdef CONFIG_HIGH_RES_TIMERS
+static irqreturn_t aw_clkevt_irq(int irq, void *handle)
+{
+ if(TMR_REG_IRQ_STAT & (1<<1))
+ {
+ CLKSRC_DBG("aw_clkevt_irq!\n");
+ /* clear pending */
+ TMR_REG_IRQ_STAT = (1<<1);
+
+ /* clock event interrupt handled */
+ if(likely(aw_clock_event.event_handler != NULL))
+ aw_clock_event.event_handler(&aw_clock_event);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+#endif
+
+
+/*
+*********************************************************************************************************
+* aw_clksrc_init
+*
+*Description: clock source initialise.
+*
+*Arguments : none
+*
+*Return : result,
+* 0, initiate successed;
+* !0, initiate failed;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static int __init aw_clksrc_init(void)
+{
+ CLKSRC_DBG("all-winners clock source init!\n");
+ /* we use 64bits counter as HPET(High Precision Event Timer) */
+ TMR_REG_CNT64_CTL = 0;
+ __delay(50);
+ /* config clock source for 64bits counter */
+ #if(AW_HPET_CLK_SRC == TMR_CLK_SRC_24MHOSC)
+ TMR_REG_CNT64_CTL |= (0<<2);
+ #else
+ TMR_REG_CNT64_CTL |= (1<<2);
+ #endif
+ __delay(50);
+ /* clear 64bits counter */
+ TMR_REG_CNT64_CTL |= (1<<0);
+ __delay(50);
+ CLKSRC_DBG("register all-winners clock source!\n");
+ /* calculate the mult by shift */
+ aw_clocksrc.mult = clocksource_hz2mult(AW_HPET_CLOCK_SOURCE_HZ, aw_clocksrc.shift);
+ /* register clock source */
+ clocksource_register(&aw_clocksrc);
+
+ return 0;
+}
+
+/*
+*********************************************************************************************************
+* aw_clkevt_init
+*
+*Description: clock event initialise.
+*
+*Arguments : none
+*
+*Return : result,
+* 0, initiate successed;
+* !0, initiate failed;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+#ifdef CONFIG_HIGH_RES_TIMERS
+static int __init aw_clkevt_init(void)
+{
+ /* register clock event irq */
+ CLKSRC_DBG("set up all-winners clock event irq!\n");
+ /* clear timer1 setting */
+ TMR_REG_TMR1_CTL = 0;
+ /* initialise timer inter value to 1 tick */
+ TMR_REG_TMR1_INTV = AW_HPET_CLOCK_EVENT_HZ/HZ;
+
+ /* config clock source for timer1 */
+ #if(AW_HPET_CLK_EVT == TMR_CLK_SRC_24MHOSC)
+ TMR_REG_TMR1_CTL |= (1<<2);
+ #else
+ TMR_REG_TMR1_CTL |= (0<<2);
+ #endif
+ /* reload inter value */
+ TMR_REG_TMR1_CTL |= (1<<1);
+ /* install timer irq */
+ setup_irq(SW_INT_IRQNO_TIMER1, &aw_clkevt_irqact);
+ /* enable timer1 irq */
+ TMR_REG_IRQ_EN |= (1<<1);
+
+ /* register clock event device */
+ CLKSRC_DBG("register all-winners clock event device!\n");
+ aw_clock_event.mult = div_sc(AW_HPET_CLOCK_EVENT_HZ, NSEC_PER_SEC, aw_clock_event.shift);
+ aw_clock_event.max_delta_ns = clockevent_delta2ns((0x80000000), &aw_clock_event);
+ /* time value timer must larger than 50 cycles at least, suggested by david 2011-5-25 11:41 */
+ aw_clock_event.min_delta_ns = clockevent_delta2ns(1, &aw_clock_event) + 100000;
+ aw_clock_event.cpumask = cpumask_of(0);
+ clockevents_register_device(&aw_clock_event);
+
+ return 0;
+}
+#endif
+
+arch_initcall(aw_clksrc_init);
+#ifdef CONFIG_HIGH_RES_TIMERS
+arch_initcall(aw_clkevt_init);
+#endif
diff --git a/arch/arm/plat-sunxi/clocksrc.h b/arch/arm/plat-sunxi/clocksrc.h
new file mode 100644
index 0000000..6a4398e
--- /dev/null
+++ b/arch/arm/plat-sunxi/clocksrc.h
@@ -0,0 +1,90 @@
+/*
+ * arch/arm/plat-sunxi/clocksrc.h
+ *
+ * (C) Copyright 2007-2012
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Kevin Zhang <ke...@allwinnertech.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __AW_CLOCKSRC_H__
+#define __AW_CLOCKSRC_H__
+
+#ifndef __tmr_reg
+ #define __tmr_reg(x) (*(volatile __u32 *)(x))
+#endif /*#ifndef __tmr_reg */
+
+
+/* define timer io base on aw chips */
+#define AW_TMR_IO_BASE SW_VA_TIMERC_IO_BASE
+/* define timer io register address */
+#define TMR_REG_o_IRQ_EN (AW_TMR_IO_BASE + 0x0000)
+#define TMR_REG_o_IRQ_STAT (AW_TMR_IO_BASE + 0x0004)
+#define TMR_REG_o_TMR1_CTL (AW_TMR_IO_BASE + 0x0020)
+#define TMR_REG_o_TMR1_INTV (AW_TMR_IO_BASE + 0x0024)
+#define TMR_REG_o_TMR1_CUR (AW_TMR_IO_BASE + 0x0028)
+#define TMR_REG_o_CNT64_CTL (AW_TMR_IO_BASE + 0x00A0)
+#define TMR_REG_o_CNT64_LO (AW_TMR_IO_BASE + 0x00A4)
+#define TMR_REG_o_CNT64_HI (AW_TMR_IO_BASE + 0x00A8)
+/* define timer io register value */
+#define TMR_REG_IRQ_EN __tmr_reg(TMR_REG_o_IRQ_EN )
+#define TMR_REG_IRQ_STAT __tmr_reg(TMR_REG_o_IRQ_STAT )
+#define TMR_REG_TMR1_CTL __tmr_reg(TMR_REG_o_TMR1_CTL )
+#define TMR_REG_TMR1_INTV __tmr_reg(TMR_REG_o_TMR1_INTV)
+#define TMR_REG_TMR1_CUR __tmr_reg(TMR_REG_o_TMR1_CUR )
+#define TMR_REG_CNT64_CTL __tmr_reg(TMR_REG_o_CNT64_CTL)
+#define TMR_REG_CNT64_LO __tmr_reg(TMR_REG_o_CNT64_LO )
+#define TMR_REG_CNT64_HI __tmr_reg(TMR_REG_o_CNT64_HI )
+
+
+/* define timer clock source */
+#define TMR_CLK_SRC_32KLOSC (0)
+#define TMR_CLK_SRC_24MHOSC (1)
+#define TMR_CLK_SRC_PLL (2)
+
+
+/* config clock frequency */
+#define AW_HPET_CLK_SRC TMR_CLK_SRC_24MHOSC
+#define AW_HPET_CLK_EVT TMR_CLK_SRC_24MHOSC
+
+
+/* aw HPET clock source frequency */
+#ifndef AW_HPET_CLK_SRC
+ #error "AW_HPET_CLK_SRC is not define!!"
+#endif
+#if(AW_HPET_CLK_SRC == TMR_CLK_SRC_24MHOSC)
+ #define AW_HPET_CLOCK_SOURCE_HZ (24000000)
+#else
+ #error "AW_HPET_CLK_SRC config is invalid!!"
+#endif
+
+
+/* aw HPET clock eventy frequency */
+#ifndef AW_HPET_CLK_EVT
+ #error "AW_HPET_CLK_EVT is not define!!"
+#endif
+#if(AW_HPET_CLK_EVT == TMR_CLK_SRC_32KLOSC)
+ #define AW_HPET_CLOCK_EVENT_HZ (32768)
+#elif(AW_HPET_CLK_EVT == TMR_CLK_SRC_24MHOSC)
+ #define AW_HPET_CLOCK_EVENT_HZ (24000000)
+#else
+ #error "AW_HPET_CLK_EVT config is invalid!!"
+#endif
+
+
+#endif /* #ifndef __AW_CLOCKSRC_H__ */
+
--
1.8.1.2

Hans de Goede

unread,
Feb 9, 2013, 10:47:53 AM2/9/13
to linux...@googlegroups.com, Hans de Goede
Note that:
1) This patch also includes one functionaly change, the clock_event_device
declared in core.c had a rating of 100 in sun4i and of 300 for sun5i. This
patch keeps the sun4i setting, thereby working around the timer0 code being
broken in periodic mode (something to fix another day), as the working
"highres" clock_event_device from clocksrc.c has a rating of 300 and thus
gets used instead *if* CONFIG_HIGH_RES_TIMERS is set!

This subtle change fixes the sun5i_defconfig kernel hanging within 10 - 30
seconds after boot due to the timer stopping.

2) checkpatch dislikes parts of this patch, but this patch is just moving
(ugly) code around not introducing it.

Signed-off-by: Hans de Goede <hdeg...@redhat.com>
---
arch/arm/mach-sun4i/Makefile | 2 +-
arch/arm/mach-sun4i/core.c | 449 -------------------------------------------
arch/arm/mach-sun5i/Makefile | 2 +-
arch/arm/mach-sun5i/core.c | 423 ----------------------------------------
arch/arm/plat-sunxi/core.c | 444 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 446 insertions(+), 874 deletions(-)
delete mode 100644 arch/arm/mach-sun4i/core.c
delete mode 100644 arch/arm/mach-sun5i/core.c

diff --git a/arch/arm/mach-sun4i/Makefile b/arch/arm/mach-sun4i/Makefile
index fc78f2b..b1d6960 100644
--- a/arch/arm/mach-sun4i/Makefile
+++ b/arch/arm/mach-sun4i/Makefile
@@ -1,5 +1,5 @@
obj-y += clock/
obj-y += dma/
-obj-y += core.o devices.o
+obj-y += devices.o
obj-$(CONFIG_PM) += pm/
obj-$(CONFIG_CPU_FREQ) += cpu-freq/
diff --git a/arch/arm/mach-sun4i/core.c b/arch/arm/mach-sun4i/core.c
deleted file mode 100644
index 071110f..0000000
--- a/arch/arm/mach-sun4i/core.c
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * arch/arm/mach-sun4i/core.c
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Benn Huang <be...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/amba/bus.h>
-#include <linux/amba/clcd.h>
-#include <linux/amba/pl061.h>
-#include <linux/amba/mmci.h>
-#include <linux/amba/pl022.h>
-#include <linux/io.h>
-#include <linux/gfp.h>
-#include <linux/clockchips.h>
-#include <linux/memblock.h>
-#include <linux/bootmem.h>
-#include <linux/export.h>
-#include <linux/clkdev.h>
-
-#include <asm/system.h>
-#include <asm/irq.h>
-#include <asm/leds.h>
-#include <asm/hardware/arm_timer.h>
-#include <asm/hardware/icst.h>
-#include <asm/hardware/vic.h>
-#include <asm/mach-types.h>
-#include <asm/setup.h>
-#include <asm/memory.h>
-#include <asm/delay.h>
-
-#include <asm/mach/arch.h>
-#include <asm/mach/flash.h>
-#include <asm/mach/irq.h>
-#include <asm/mach/time.h>
-#include <asm/mach/map.h>
-#include <mach/memory.h>
-#include <mach/system.h>
-#include <mach/timex.h>
-
-#include <plat/core.h>
-#include <plat/sys_config.h>
-
-/**
- * Machine Implementations
- *
- */
-
-static struct map_desc sw_io_desc[] __initdata = {
- { SW_VA_SRAM_BASE, __phys_to_pfn(SW_PA_SRAM_BASE), (SZ_128K + SZ_64K), MT_MEMORY_ITCM },
- { SW_VA_IO_BASE, __phys_to_pfn(SW_PA_IO_BASE), (SZ_1M + SZ_2M), MT_DEVICE },
- { SW_VA_BROM_BASE, __phys_to_pfn(SW_PA_BROM_BASE), (SZ_64K), MT_MEMORY_ITCM },
-};
-
-void __init sw_core_map_io(void)
-{
- iotable_init(sw_io_desc, ARRAY_SIZE(sw_io_desc));
-
- sunxi_pr_chip_id();
-}
-
-/* Only reserve certain important memory blocks if there are actually
- * drivers which use them.
- */
-
-#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
-/* The FB block is used by:
- *
- * - the sun4i framebuffer driver, drivers/video/sun4i/disp.
- *
- * fb_start, fb_size are used in a vast number of other places but for
- * for platform-specific drivers, so we don't have to worry about them.
- *
- * The block will only be allocated if the disp_init/disp_init_enabled
- * script key is set.
- */
-
-unsigned long fb_start = (PLAT_PHYS_OFFSET + SZ_512M - SZ_64M - SZ_32M);
-unsigned long fb_size = SZ_32M;
-EXPORT_SYMBOL(fb_start);
-EXPORT_SYMBOL(fb_size);
-
-static void __init reserve_fb(void)
-{
- memblock_reserve(fb_start, fb_size);
- pr_reserve_info("LCD ", fb_start, fb_size);
-}
-
-#else
-static void __init reserve_fb(void) {}
-#endif
-
-#if defined CONFIG_SUN4I_G2D || defined CONFIG_SUN4I_G2D_MODULE
-/* The G2D block is used by:
- *
- * - the G2D engine, drivers/char/sun4i_g2d
- *
- * The block will only be allocated if the g2d_para/g2d_used
- * script key is set.
- */
-
-unsigned long g2d_start = (PLAT_PHYS_OFFSET + SZ_512M - SZ_128M);
-unsigned long g2d_size = SZ_1M * 16;
-EXPORT_SYMBOL(g2d_start);
-EXPORT_SYMBOL(g2d_size);
-
-static void __init reserve_g2d(void)
-{
- memblock_reserve(g2d_start, g2d_size);
- pr_reserve_info("G2D ", g2d_start, g2d_size);
-}
-#else
-static void __init reserve_g2d(void) {}
-#endif
-
-#if defined CONFIG_VIDEO_DECODER_SUN4I || defined CONFIG_VIDEO_DECODER_SUN4I_MODULE
-/* The VE block is used by:
- *
- * - the Cedar video engine, drivers/media/video/sun4i
- *
- * ve_start, ve_size are also used by the contiguous-DMA module in
- * drivers/media/video/videobuf-dma-contig.c, but that's SH-specific
- * so we don't have to worry about it here.
- */
-
-unsigned long ve_start = (PLAT_PHYS_OFFSET + SZ_64M);
-unsigned long ve_size = (SZ_64M + SZ_16M);
-EXPORT_SYMBOL(ve_start);
-EXPORT_SYMBOL(ve_size);
-
-static void __init reserve_ve(void)
-{
- /* The users of the VE block aren't enabled via script flags, so if their
- * driver gets compiled in we have to unconditionally reserve memory for
- * them.
- */
- memblock_reserve(ve_start, SZ_64M);
- memblock_reserve(ve_start + SZ_64M, SZ_16M);
-
- pr_reserve_info("VE ", ve_start, ve_size);
-}
-
-#else
-static void __init reserve_ve(void) {}
-#endif
-
-static void reserve_sys(void)
-{
- memblock_reserve(SYS_CONFIG_MEMBASE, SYS_CONFIG_MEMSIZE);
- pr_reserve_info("SYS ", SYS_CONFIG_MEMBASE, SYS_CONFIG_MEMSIZE);
-}
-
-static void __init sw_core_reserve(void)
-{
- pr_info("Memory Reserved:\n");
- reserve_sys();
- reserve_ve();
- reserve_g2d();
- reserve_fb();
-}
-
-void sw_irq_ack(struct irq_data *irqd)
-{
- unsigned int irq = irqd->irq;
-
- if (irq < 32){
- writel(readl(SW_INT_ENABLE_REG0) & ~(1<<irq), SW_INT_ENABLE_REG0);
- writel(readl(SW_INT_MASK_REG0) | (1 << irq), SW_INT_MASK_REG0);
- writel(readl(SW_INT_IRQ_PENDING_REG0) | (1<<irq), SW_INT_IRQ_PENDING_REG0);
- } else if(irq < 64){
- irq -= 32;
- writel(readl(SW_INT_ENABLE_REG1) & ~(1<<irq), SW_INT_ENABLE_REG1);
- writel(readl(SW_INT_MASK_REG1) | (1 << irq), SW_INT_MASK_REG1);
- writel(readl(SW_INT_IRQ_PENDING_REG1) | (1<<irq), SW_INT_IRQ_PENDING_REG1);
- } else if(irq < 96){
- irq -= 64;
- writel(readl(SW_INT_ENABLE_REG2) & ~(1<<irq), SW_INT_ENABLE_REG2);
- writel(readl(SW_INT_MASK_REG2) | (1 << irq), SW_INT_MASK_REG2);
- writel(readl(SW_INT_IRQ_PENDING_REG2) | (1<<irq), SW_INT_IRQ_PENDING_REG2);
- }
-}
-
-/* Mask an IRQ line, which means disabling the IRQ line */
-static void sw_irq_mask(struct irq_data *irqd)
-{
- unsigned int irq = irqd->irq;
-
- if(irq < 32){
- writel(readl(SW_INT_ENABLE_REG0) & ~(1<<irq), SW_INT_ENABLE_REG0);
- writel(readl(SW_INT_MASK_REG0) | (1 << irq), SW_INT_MASK_REG0);
- } else if(irq < 64){
- irq -= 32;
- writel(readl(SW_INT_ENABLE_REG1) & ~(1<<irq), SW_INT_ENABLE_REG1);
- writel(readl(SW_INT_MASK_REG1) | (1 << irq), SW_INT_MASK_REG1);
- } else if(irq < 96){
- irq -= 64;
- writel(readl(SW_INT_ENABLE_REG2) & ~(1<<irq), SW_INT_ENABLE_REG2);
- writel(readl(SW_INT_MASK_REG2) | (1 << irq), SW_INT_MASK_REG2);
- }
-}
-
-static void sw_irq_unmask(struct irq_data *irqd)
-{
- unsigned int irq = irqd->irq;
-
- if(irq < 32){
- writel(readl(SW_INT_ENABLE_REG0) | (1<<irq), SW_INT_ENABLE_REG0);
- writel(readl(SW_INT_MASK_REG0) & ~(1 << irq), SW_INT_MASK_REG0);
- if(irq == SW_INT_IRQNO_ENMI) /* must clear pending bit when enabled */
- writel((1 << SW_INT_IRQNO_ENMI), SW_INT_IRQ_PENDING_REG0);
- } else if(irq < 64){
- irq -= 32;
- writel(readl(SW_INT_ENABLE_REG1) | (1<<irq), SW_INT_ENABLE_REG1);
- writel(readl(SW_INT_MASK_REG1) & ~(1 << irq), SW_INT_MASK_REG1);
- } else if(irq < 96){
- irq -= 64;
- writel(readl(SW_INT_ENABLE_REG2) | (1<<irq), SW_INT_ENABLE_REG2);
- writel(readl(SW_INT_MASK_REG2) & ~(1 << irq), SW_INT_MASK_REG2);
- }
-}
-
-static struct irq_chip sw_vic_chip = {
- .name = "sw_vic",
- .irq_ack = sw_irq_ack,
- .irq_mask = sw_irq_mask,
- .irq_unmask = sw_irq_unmask,
-};
-
-void __init sw_core_init_irq(void)
-{
- u32 i = 0;
-
- /* Disable & clear all interrupts */
- writel(0, SW_INT_ENABLE_REG0);
- writel(0, SW_INT_ENABLE_REG1);
- writel(0, SW_INT_ENABLE_REG2);
-
- writel(0xffffffff, SW_INT_MASK_REG0);
- writel(0xffffffff, SW_INT_MASK_REG1);
- writel(0xffffffff, SW_INT_MASK_REG2);
-
- writel(0xffffffff, SW_INT_IRQ_PENDING_REG0);
- writel(0xffffffff, SW_INT_IRQ_PENDING_REG1);
- writel(0xffffffff, SW_INT_IRQ_PENDING_REG2);
- writel(0xffffffff, SW_INT_FIQ_PENDING_REG0);
- writel(0xffffffff, SW_INT_FIQ_PENDING_REG1);
- writel(0xffffffff, SW_INT_FIQ_PENDING_REG2);
-
- /*enable protection mode*/
- writel(0x01, SW_INT_PROTECTION_REG);
- /*config the external interrupt source type*/
- writel(0x00, SW_INT_NMI_CTRL_REG);
-
- for (i = SW_INT_START; i < SW_INT_END; i++) {
- irq_set_chip(i, &sw_vic_chip);
- irq_set_handler(i, handle_level_irq);
- set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
- }
-}
-
-
-
-/*
- * Global vars definitions
- *
- */
-static void sun4i_restart(char mode, const char *cmd)
-{
- /* use watch-dog to reset system */
- #define WATCH_DOG_CTRL_REG (SW_VA_TIMERC_IO_BASE + 0x0094)
- *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 0;
- __delay(100000);
- *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 3;
- while(1);
-}
-
-static void timer_set_mode(enum clock_event_mode mode, struct clock_event_device *clk)
-{
- volatile u32 ctrl;
-
- switch (mode) {
- case CLOCK_EVT_MODE_PERIODIC:
- pr_info("timer0: Periodic Mode\n");
- writel(TMR_INTER_VAL, SW_TIMER0_INTVAL_REG); /* interval (999+1) */
- ctrl = readl(SW_TIMER0_CTL_REG);
- ctrl &= ~(1<<7); /* Continuous mode */
- ctrl |= 1; /* Enable this timer */
- break;
-
- case CLOCK_EVT_MODE_ONESHOT:
- pr_info("timer0: Oneshot Mode\n");
- ctrl = readl(SW_TIMER0_CTL_REG);
- ctrl |= (1<<7); /* Single mode */
- break;
- case CLOCK_EVT_MODE_UNUSED:
- case CLOCK_EVT_MODE_SHUTDOWN:
- default:
- ctrl = readl(SW_TIMER0_CTL_REG);
- ctrl &= ~(1<<0); /* Disable timer0 */
- break;
- }
-
- writel(ctrl, SW_TIMER0_CTL_REG);
-}
-
-/* Useless when periodic mode */
-static int timer_set_next_event(unsigned long evt, struct clock_event_device *unused)
-{
- volatile u32 ctrl;
-
- /* clear any pending before continue */
- ctrl = readl(SW_TIMER0_CTL_REG);
- writel(evt, SW_TIMER0_CNTVAL_REG);
- ctrl |= (1<<1);
- writel(ctrl, SW_TIMER0_CTL_REG);
- writel(ctrl | 0x1, SW_TIMER0_CTL_REG);
-
- return 0;
-}
-
-static struct clock_event_device timer0_clockevent = {
- .name = "timer0",
- .shift = 32,
- .rating = 100,
- .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
- .set_mode = timer_set_mode,
- .set_next_event = timer_set_next_event,
-};
-
-
-static irqreturn_t sw_timer_interrupt(int irq, void *dev_id)
-{
- struct clock_event_device *evt = (struct clock_event_device *)dev_id;
-
- writel(0x1, SW_TIMER_INT_STA_REG);
-
- /*
- * timer_set_next_event will be called only in ONESHOT mode
- */
- evt->event_handler(evt);
-
- return IRQ_HANDLED;
-}
-
-static struct irqaction sw_timer_irq = {
- .name = "timer0",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
- .handler = sw_timer_interrupt,
- .dev_id = &timer0_clockevent,
- .irq = SW_INT_IRQNO_TIMER0,
-};
-
-
-static void __init sw_timer_init(void)
-{
- int ret;
- volatile u32 val = 0;
-
- writel(TMR_INTER_VAL, SW_TIMER0_INTVAL_REG);
- /* set clock sourch to HOSC, 16 pre-division */
- val = readl(SW_TIMER0_CTL_REG);
- val &= ~(0x07<<4);
- val &= ~(0x03<<2);
- val |= (4<<4) | (1<<2);
- writel(val, SW_TIMER0_CTL_REG);
- /* set mode to auto reload */
- val = readl(SW_TIMER0_CTL_REG);
- val |= (1<<1);
- writel(val, SW_TIMER0_CTL_REG);
-
- ret = setup_irq(SW_INT_IRQNO_TIMER0, &sw_timer_irq);
- if (ret) {
- pr_warning("failed to setup irq %d\n", SW_INT_IRQNO_TIMER0);
- }
-
- /* Enable time0 interrupt */
- val = readl(SW_TIMER_INT_CTL_REG);
- val |= (1<<0);
- writel(val, SW_TIMER_INT_CTL_REG);
-
- timer0_clockevent.mult = div_sc(SYS_TIMER_CLKSRC/SYS_TIMER_SCAL, NSEC_PER_SEC, timer0_clockevent.shift);
- timer0_clockevent.max_delta_ns = clockevent_delta2ns(0xff, &timer0_clockevent);
- timer0_clockevent.min_delta_ns = clockevent_delta2ns(0x1, &timer0_clockevent);
- timer0_clockevent.cpumask = cpumask_of(0);
- timer0_clockevent.irq = sw_timer_irq.irq;
- clockevents_register_device(&timer0_clockevent);
-}
-
-struct sys_timer sw_sys_timer = {
- .init = sw_timer_init,
-};
-
-extern void __init sw_pdev_init(void);
-void __init sw_core_init(void)
-{
- sw_pdev_init();
-}
-enum sw_ic_ver sw_get_ic_ver(void)
-{
- volatile u32 val = readl(SW_VA_TIMERC_IO_BASE + 0x13c);
-
- val = (val >> 6) & 0x3;
-
- if (val == 0x00) {
- return MAGIC_VER_A;
- }
- else if(val == 0x03) {
- return MAGIC_VER_B;
- }
-
- return MAGIC_VER_C;
-}
-EXPORT_SYMBOL(sw_get_ic_ver);
-
-MACHINE_START(SUN4I, "sun4i")
- .atag_offset = 0x100,
- .timer = &sw_sys_timer,
-#ifdef CONFIG_SUNXI_MALI_RESERVED_MEM
- .fixup = sunxi_mali_core_fixup,
-#endif
- .map_io = sw_core_map_io,
- .init_early = NULL,
- .init_irq = sw_core_init_irq,
- .init_machine = sw_core_init,
- .reserve = sw_core_reserve,
- .restart = sun4i_restart,
-MACHINE_END
diff --git a/arch/arm/mach-sun5i/Makefile b/arch/arm/mach-sun5i/Makefile
index fc78f2b..b1d6960 100644
--- a/arch/arm/mach-sun5i/Makefile
+++ b/arch/arm/mach-sun5i/Makefile
@@ -1,5 +1,5 @@
obj-y += clock/
obj-y += dma/
-obj-y += core.o devices.o
+obj-y += devices.o
obj-$(CONFIG_PM) += pm/
obj-$(CONFIG_CPU_FREQ) += cpu-freq/
diff --git a/arch/arm/mach-sun5i/core.c b/arch/arm/mach-sun5i/core.c
deleted file mode 100644
index 6c49c78..0000000
--- a/arch/arm/mach-sun5i/core.c
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * arch/arm/mach-sun5i/core.c
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Benn Huang <be...@allwinnertech.com>
- *
- * SUN5I machine core implementations
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/amba/bus.h>
-#include <linux/amba/clcd.h>
-#include <linux/amba/pl061.h>
-#include <linux/amba/mmci.h>
-#include <linux/amba/pl022.h>
-#include <linux/io.h>
-#include <linux/gfp.h>
-#include <linux/clockchips.h>
-#include <linux/memblock.h>
-#include <linux/bootmem.h>
-#include <linux/export.h>
-#include <linux/clkdev.h>
-
-#include <asm/system.h>
-#include <asm/irq.h>
-#include <asm/leds.h>
-#include <asm/hardware/arm_timer.h>
-#include <asm/hardware/icst.h>
-#include <asm/hardware/vic.h>
-#include <asm/mach-types.h>
-#include <asm/setup.h>
-#include <asm/delay.h>
-
-#include <asm/mach/arch.h>
-#include <asm/mach/flash.h>
-#include <asm/mach/irq.h>
-#include <asm/mach/time.h>
-#include <asm/mach/map.h>
-#include <mach/memory.h>
-#include <mach/system.h>
-#include <mach/timex.h>
-
-#include <plat/core.h>
-#include <plat/sys_config.h>
-
-/**
- * Machine Implementations
- *
- */
-
-static struct map_desc sw_io_desc[] __initdata = {
- { SW_VA_SRAM_BASE, __phys_to_pfn(SW_PA_SRAM_BASE), (SZ_128K + SZ_64K), MT_MEMORY_ITCM },
- { SW_VA_IO_BASE, __phys_to_pfn(SW_PA_IO_BASE), (SZ_1M + SZ_2M), MT_DEVICE },
- { SW_VA_BROM_BASE, __phys_to_pfn(SW_PA_BROM_BASE), (SZ_64K), MT_MEMORY_ITCM },
-};
-
-void __init sw_core_map_io(void)
-{
- iotable_init(sw_io_desc, ARRAY_SIZE(sw_io_desc));
-
- sunxi_pr_chip_id();
-}
-
-#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
-unsigned long fb_start = (PLAT_PHYS_OFFSET + SZ_512M - SZ_64M - SZ_32M);
-unsigned long fb_size = SZ_32M;
-EXPORT_SYMBOL(fb_start);
-EXPORT_SYMBOL(fb_size);
-
-static inline void reserve_fb(void)
-{
- memblock_reserve(fb_start, fb_size);
- pr_reserve_info("LCD ", fb_start, fb_size);
-}
-#else
-static inline void reserve_fb(void) {}
-#endif
-
-#if 0
-unsigned long g2d_start = (PLAT_PHYS_OFFSET + SZ_512M - SZ_128M);
-unsigned long g2d_size = SZ_1M * 16;
-EXPORT_SYMBOL(g2d_start);
-EXPORT_SYMBOL(g2d_size);
-
-static inline void reserve_g2d(void)
-{
- int g2d_used = 0;
- char *script_base = (char *)(PAGE_OFFSET + 0x3000000);
-
- g2d_used = sw_cfg_get_int(script_base, "g2d_para", "g2d_used");
- if (g2d_used > 0) {
- int size = sw_cfg_get_int(script_base, "g2d_para", "g2d_size");
- if (size < 0 || size > SW_G2D_MEM_MAX)
- g2d_size = SW_G2D_MEM_MAX;
- else
- g2d_size = size;
-
- memblock_reserve(g2d_start, g2d_size);
- pr_reserve_info("G2D ", g2d_start, g2d_size);
- }
-}
-#else
-static inline void reserve_g2d(void) {}
-#endif
-
-#if defined CONFIG_VIDEO_DECODER_SUN5I || defined CONFIG_VIDEO_DECODER_SUN5I_MODULE
-unsigned long ve_start = (PLAT_PHYS_OFFSET + SZ_64M);
-unsigned long ve_size = (SZ_64M + SZ_16M);
-EXPORT_SYMBOL(ve_start);
-EXPORT_SYMBOL(ve_size);
-
-static inline void reserve_ve(void)
-{
- memblock_reserve(ve_start, SZ_64M);
- memblock_reserve(ve_start + SZ_64M, SZ_16M);
- pr_reserve_info("VE ", ve_start, ve_size);
-}
-#else
-static inline void reserve_ve(void) {}
-#endif
-
-static inline void reserve_sys(void)
-{
- memblock_reserve(SYS_CONFIG_MEMBASE, SYS_CONFIG_MEMSIZE);
- pr_reserve_info("SYS ", SYS_CONFIG_MEMBASE, SYS_CONFIG_MEMSIZE);
-}
-
-static void __init sw_core_reserve(void)
-{
- pr_info("Memory Reserved:\n");
- reserve_sys();
- reserve_fb();
- reserve_g2d();
- reserve_ve();
-}
-
-void sw_irq_ack(struct irq_data *irqd)
-{
- unsigned int irq = irqd->irq;
-
- if (irq < 32){
- writel(readl(SW_INT_ENABLE_REG0) & ~(1<<irq), SW_INT_ENABLE_REG0);
- writel(readl(SW_INT_MASK_REG0) | (1 << irq), SW_INT_MASK_REG0);
- writel(readl(SW_INT_IRQ_PENDING_REG0) | (1<<irq), SW_INT_IRQ_PENDING_REG0);
- } else if(irq < 64){
- irq -= 32;
- writel(readl(SW_INT_ENABLE_REG1) & ~(1<<irq), SW_INT_ENABLE_REG1);
- writel(readl(SW_INT_MASK_REG1) | (1 << irq), SW_INT_MASK_REG1);
- writel(readl(SW_INT_IRQ_PENDING_REG1) | (1<<irq), SW_INT_IRQ_PENDING_REG1);
- } else if(irq < 96){
- irq -= 64;
- writel(readl(SW_INT_ENABLE_REG2) & ~(1<<irq), SW_INT_ENABLE_REG2);
- writel(readl(SW_INT_MASK_REG2) | (1 << irq), SW_INT_MASK_REG2);
- writel(readl(SW_INT_IRQ_PENDING_REG2) | (1<<irq), SW_INT_IRQ_PENDING_REG2);
- }
-}
-
-/* Mask an IRQ line, which means disabling the IRQ line */
-static void sw_irq_mask(struct irq_data *irqd)
-{
- unsigned int irq = irqd->irq;
-
- if(irq < 32){
- writel(readl(SW_INT_ENABLE_REG0) & ~(1<<irq), SW_INT_ENABLE_REG0);
- writel(readl(SW_INT_MASK_REG0) | (1 << irq), SW_INT_MASK_REG0);
- } else if(irq < 64){
- irq -= 32;
- writel(readl(SW_INT_ENABLE_REG1) & ~(1<<irq), SW_INT_ENABLE_REG1);
- writel(readl(SW_INT_MASK_REG1) | (1 << irq), SW_INT_MASK_REG1);
- } else if(irq < 96){
- irq -= 64;
- writel(readl(SW_INT_ENABLE_REG2) & ~(1<<irq), SW_INT_ENABLE_REG2);
- writel(readl(SW_INT_MASK_REG2) | (1 << irq), SW_INT_MASK_REG2);
- }
-}
-
-static void sw_irq_unmask(struct irq_data *irqd)
-{
- unsigned int irq = irqd->irq;
-
- if(irq < 32){
- writel(readl(SW_INT_ENABLE_REG0) | (1<<irq), SW_INT_ENABLE_REG0);
- writel(readl(SW_INT_MASK_REG0) & ~(1 << irq), SW_INT_MASK_REG0);
- if(irq == SW_INT_IRQNO_ENMI) /* must clear pending bit when enabled */
- writel((1 << SW_INT_IRQNO_ENMI), SW_INT_IRQ_PENDING_REG0);
- } else if(irq < 64){
- irq -= 32;
- writel(readl(SW_INT_ENABLE_REG1) | (1<<irq), SW_INT_ENABLE_REG1);
- writel(readl(SW_INT_MASK_REG1) & ~(1 << irq), SW_INT_MASK_REG1);
- } else if(irq < 96){
- irq -= 64;
- writel(readl(SW_INT_ENABLE_REG2) | (1<<irq), SW_INT_ENABLE_REG2);
- writel(readl(SW_INT_MASK_REG2) & ~(1 << irq), SW_INT_MASK_REG2);
- }
-}
-
-static struct irq_chip sw_vic_chip = {
- .name = "sw_vic",
- .irq_ack = sw_irq_ack,
- .irq_mask = sw_irq_mask,
- .irq_unmask = sw_irq_unmask,
-};
-
-void __init sw_core_init_irq(void)
-{
- u32 i = 0;
-
- /* Disable & clear all interrupts */
- writel(0, SW_INT_ENABLE_REG0);
- writel(0, SW_INT_ENABLE_REG1);
- writel(0, SW_INT_ENABLE_REG2);
-
- writel(0xffffffff, SW_INT_MASK_REG0);
- writel(0xffffffff, SW_INT_MASK_REG1);
- writel(0xffffffff, SW_INT_MASK_REG2);
-
- writel(0xffffffff, SW_INT_IRQ_PENDING_REG0);
- writel(0xffffffff, SW_INT_IRQ_PENDING_REG1);
- writel(0xffffffff, SW_INT_IRQ_PENDING_REG2);
- writel(0xffffffff, SW_INT_FIQ_PENDING_REG0);
- writel(0xffffffff, SW_INT_FIQ_PENDING_REG1);
- writel(0xffffffff, SW_INT_FIQ_PENDING_REG2);
-
- /*enable protection mode*/
- writel(0x01, SW_INT_PROTECTION_REG);
- /*config the external interrupt source type*/
- writel(0x00, SW_INT_NMI_CTRL_REG);
-
- for (i = SW_INT_START; i < SW_INT_END; i++) {
- irq_set_chip(i, &sw_vic_chip);
- irq_set_handler(i, handle_level_irq);
- set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
- }
-}
-
-
-
-/**
- * Global vars definitions
- *
- */
-static void sun5i_restart(char mode, const char *cmd)
-{
- /* use watch-dog to reset system */
- #define WATCH_DOG_CTRL_REG (SW_VA_TIMERC_IO_BASE + 0x0094)
- *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 0;
- __delay(100000);
- *(volatile unsigned int *)WATCH_DOG_CTRL_REG |= 2;
- while(1) {
- __delay(100);
- *(volatile unsigned int *)WATCH_DOG_CTRL_REG |= 1;
- }
-}
-
-static void timer_set_mode(enum clock_event_mode mode, struct clock_event_device *clk)
-{
- volatile u32 ctrl;
-
- switch (mode) {
- case CLOCK_EVT_MODE_PERIODIC:
- pr_info("timer0: Periodic Mode\n");
- writel(TMR_INTER_VAL, SW_TIMER0_INTVAL_REG); /* interval (999+1) */
- ctrl = readl(SW_TIMER0_CTL_REG);
- ctrl &= ~(1<<7); /* Continuous mode */
- ctrl |= 1; /* Enable this timer */
- break;
-
- case CLOCK_EVT_MODE_ONESHOT:
- pr_info("timer0: Oneshot Mode\n");
- ctrl = readl(SW_TIMER0_CTL_REG);
- ctrl |= (1<<7); /* Single mode */
- break;
- case CLOCK_EVT_MODE_UNUSED:
- case CLOCK_EVT_MODE_SHUTDOWN:
- default:
- ctrl = readl(SW_TIMER0_CTL_REG);
- ctrl &= ~(1<<0); /* Disable timer0 */
- break;
- }
-
- writel(ctrl, SW_TIMER0_CTL_REG);
-}
-
-/* Useless when periodic mode */
-static int timer_set_next_event(unsigned long evt, struct clock_event_device *unused)
-{
- volatile u32 ctrl;
-
- /* clear any pending before continue */
- ctrl = readl(SW_TIMER0_CTL_REG);
- writel(evt, SW_TIMER0_CNTVAL_REG);
- ctrl |= (1<<1);
- writel(ctrl, SW_TIMER0_CTL_REG);
- writel(ctrl | 0x1, SW_TIMER0_CTL_REG);
-
- return 0;
-}
-
-static struct clock_event_device timer0_clockevent = {
- .name = "timer0",
- .shift = 32,
- .rating = 300,
- .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
- .set_mode = timer_set_mode,
- .set_next_event = timer_set_next_event,
-};
-
-
-static irqreturn_t sw_timer_interrupt(int irq, void *dev_id)
-{
- struct clock_event_device *evt = (struct clock_event_device *)dev_id;
-
- writel(0x1, SW_TIMER_INT_STA_REG);
-
- /*
- * timer_set_next_event will be called only in ONESHOT mode
- */
- evt->event_handler(evt);
-
- return IRQ_HANDLED;
-}
-
-static struct irqaction sw_timer_irq = {
- .name = "timer0",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
- .handler = sw_timer_interrupt,
- .dev_id = &timer0_clockevent,
- .irq = SW_INT_IRQNO_TIMER0,
-};
-
-
-static void __init sw_timer_init(void)
-{
- int ret;
- volatile u32 val = 0;
-
- writel(TMR_INTER_VAL, SW_TIMER0_INTVAL_REG);
- /* set clock sourch to HOSC, 16 pre-division */
- val = readl(SW_TIMER0_CTL_REG);
- val &= ~(0x07<<4);
- val &= ~(0x03<<2);
- val |= (4<<4) | (1<<2);
- writel(val, SW_TIMER0_CTL_REG);
- /* set mode to auto reload */
- val = readl(SW_TIMER0_CTL_REG);
- val |= (1<<1);
- writel(val, SW_TIMER0_CTL_REG);
-
- ret = setup_irq(SW_INT_IRQNO_TIMER0, &sw_timer_irq);
- if (ret) {
- pr_warning("failed to setup irq %d\n", SW_INT_IRQNO_TIMER0);
- }
-
- /* Enable time0 interrupt */
- val = readl(SW_TIMER_INT_CTL_REG);
- val |= (1<<0);
- writel(val, SW_TIMER_INT_CTL_REG);
-
- timer0_clockevent.mult = div_sc(SYS_TIMER_CLKSRC/SYS_TIMER_SCAL, NSEC_PER_SEC, timer0_clockevent.shift);
- timer0_clockevent.max_delta_ns = clockevent_delta2ns(0xff, &timer0_clockevent);
- timer0_clockevent.min_delta_ns = clockevent_delta2ns(0x1, &timer0_clockevent);
- timer0_clockevent.cpumask = cpumask_of(0);
- timer0_clockevent.irq = sw_timer_irq.irq;
- clockevents_register_device(&timer0_clockevent);
-}
-
-struct sys_timer sw_sys_timer = {
- .init = sw_timer_init,
-};
-
-extern void __init sw_pdev_init(void);
-void __init sw_core_init(void)
-{
- sw_pdev_init();
-}
-enum sw_ic_ver sw_get_ic_ver(void)
-{
- volatile u32 val = readl(SW_VA_TIMERC_IO_BASE + 0x13c);
-
- val = (val >> 6) & 0x3;
-
- if (val == 0x3) {
- return MAGIC_VER_B;
- }
-
- return MAGIC_VER_A;
-}
-EXPORT_SYMBOL(sw_get_ic_ver);
-
-MACHINE_START(SUN5I, "sun5i")
- .atag_offset = 0x100,
- .timer = &sw_sys_timer,
-#ifdef CONFIG_SUNXI_MALI_RESERVED_MEM
- .fixup = sunxi_mali_core_fixup,
-#endif
- .map_io = sw_core_map_io,
- .init_early = NULL,
- .init_irq = sw_core_init_irq,
- .init_machine = sw_core_init,
- .reserve = sw_core_reserve,
- .restart = sun5i_restart,
-MACHINE_END
diff --git a/arch/arm/plat-sunxi/core.c b/arch/arm/plat-sunxi/core.c
index 3420178..6a89286 100644
--- a/arch/arm/plat-sunxi/core.c
+++ b/arch/arm/plat-sunxi/core.c
@@ -18,14 +18,47 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/amba/pl061.h>
+#include <linux/amba/mmci.h>
+#include <linux/amba/pl022.h>
+#include <linux/io.h>
+#include <linux/gfp.h>
+#include <linux/clockchips.h>
+#include <linux/memblock.h>
+#include <linux/bootmem.h>
+#include <linux/export.h>
+#include <linux/clkdev.h>

#include <asm/setup.h>
#include <asm/sizes.h>
#include <asm/mach-types.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/leds.h>
+#include <asm/setup.h>
+#include <asm/memory.h>
+#include <asm/delay.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+#include <asm/hardware/arm_timer.h>
+#include <asm/hardware/icst.h>
+#include <asm/hardware/vic.h>

#include <plat/core.h>
+#include <plat/memory.h>
#include <plat/platform.h>
#include <plat/system.h>
+#include <plat/sys_config.h>

struct brom_header {
u32 jump_instruction; /* one intruction jumping to real code */
@@ -134,3 +167,414 @@ void __init sunxi_mali_core_fixup(struct tag *t, char **cmdline,
}
}
#endif
+
+/**
+ * Machine Implementations
+ *
+ */
+
+static struct map_desc sw_io_desc[] __initdata = {
+ { SW_VA_SRAM_BASE, __phys_to_pfn(SW_PA_SRAM_BASE), (SZ_128K + SZ_64K), MT_MEMORY_ITCM },
+ { SW_VA_IO_BASE, __phys_to_pfn(SW_PA_IO_BASE), (SZ_1M + SZ_2M), MT_DEVICE },
+ { SW_VA_BROM_BASE, __phys_to_pfn(SW_PA_BROM_BASE), (SZ_64K), MT_MEMORY_ITCM },
+};
+
+void __init sw_core_map_io(void)
+{
+ iotable_init(sw_io_desc, ARRAY_SIZE(sw_io_desc));
+
+ sunxi_pr_chip_id();
+}
+
+/* Only reserve certain important memory blocks if there are actually
+ * drivers which use them.
+ */
+
+#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
+/* The FB block is used by:
+ *
+ * - the sun4i framebuffer driver, drivers/video/sun4i/disp.
+ *
+ * fb_start, fb_size are used in a vast number of other places but for
+ * for platform-specific drivers, so we don't have to worry about them.
+ *
+ * The block will only be allocated if the disp_init/disp_init_enabled
+ * script key is set.
+ */
+
+unsigned long fb_start = (PLAT_PHYS_OFFSET + SZ_512M - SZ_64M - SZ_32M);
+unsigned long fb_size = SZ_32M;
+EXPORT_SYMBOL(fb_start);
+EXPORT_SYMBOL(fb_size);
+
+static void __init reserve_fb(void)
+{
+ memblock_reserve(fb_start, fb_size);
+ pr_reserve_info("LCD ", fb_start, fb_size);
+}
+
+#else
+static void __init reserve_fb(void) {}
+#endif
+
+#if defined CONFIG_SUN4I_G2D || defined CONFIG_SUN4I_G2D_MODULE
+/* The G2D block is used by:
+ *
+ * - the G2D engine, drivers/char/sun4i_g2d
+ *
+ * The block will only be allocated if the g2d_para/g2d_used
+ * script key is set.
+ */
+
+unsigned long g2d_start = (PLAT_PHYS_OFFSET + SZ_512M - SZ_128M);
+unsigned long g2d_size = SZ_1M * 16;
+EXPORT_SYMBOL(g2d_start);
+EXPORT_SYMBOL(g2d_size);
+
+static void __init reserve_g2d(void)
+{
+ memblock_reserve(g2d_start, g2d_size);
+ pr_reserve_info("G2D ", g2d_start, g2d_size);
+}
+#else
+static void __init reserve_g2d(void) {}
+#endif
+
+#if defined CONFIG_VIDEO_DECODER_SUN4I || \
+ defined CONFIG_VIDEO_DECODER_SUN4I_MODULE || \
+ defined CONFIG_VIDEO_DECODER_SUN5I || \
+ defined CONFIG_VIDEO_DECODER_SUN5I_MODULE
+/* The VE block is used by:
+ *
+ * - the Cedar video engine, drivers/media/video/sun4i
+ *
+ * ve_start, ve_size are also used by the contiguous-DMA module in
+ * drivers/media/video/videobuf-dma-contig.c, but that's SH-specific
+ * so we don't have to worry about it here.
+ */
+
+unsigned long ve_start = (PLAT_PHYS_OFFSET + SZ_64M);
+unsigned long ve_size = (SZ_64M + SZ_16M);
+EXPORT_SYMBOL(ve_start);
+EXPORT_SYMBOL(ve_size);
+
+static void __init reserve_ve(void)
+{
+ /* The users of the VE block aren't enabled via script flags, so if their
+ * driver gets compiled in we have to unconditionally reserve memory for
+ * them.
+ */
+ memblock_reserve(ve_start, SZ_64M);
+ memblock_reserve(ve_start + SZ_64M, SZ_16M);
+
+ pr_reserve_info("VE ", ve_start, ve_size);
+}
+
+#else
+static void __init reserve_ve(void) {}
+#endif
+
+static void reserve_sys(void)
+{
+ memblock_reserve(SYS_CONFIG_MEMBASE, SYS_CONFIG_MEMSIZE);
+ pr_reserve_info("SYS ", SYS_CONFIG_MEMBASE, SYS_CONFIG_MEMSIZE);
+}
+
+static void __init sw_core_reserve(void)
+{
+ pr_info("Memory Reserved:\n");
+ reserve_sys();
+ reserve_ve();
+ reserve_g2d();
+ reserve_fb();
+}
+
+void sw_irq_ack(struct irq_data *irqd)
+{
+ unsigned int irq = irqd->irq;
+
+ if (irq < 32){
+ writel(readl(SW_INT_ENABLE_REG0) & ~(1<<irq), SW_INT_ENABLE_REG0);
+ writel(readl(SW_INT_MASK_REG0) | (1 << irq), SW_INT_MASK_REG0);
+ writel(readl(SW_INT_IRQ_PENDING_REG0) | (1<<irq), SW_INT_IRQ_PENDING_REG0);
+ } else if(irq < 64){
+ irq -= 32;
+ writel(readl(SW_INT_ENABLE_REG1) & ~(1<<irq), SW_INT_ENABLE_REG1);
+ writel(readl(SW_INT_MASK_REG1) | (1 << irq), SW_INT_MASK_REG1);
+ writel(readl(SW_INT_IRQ_PENDING_REG1) | (1<<irq), SW_INT_IRQ_PENDING_REG1);
+ } else if(irq < 96){
+ irq -= 64;
+ writel(readl(SW_INT_ENABLE_REG2) & ~(1<<irq), SW_INT_ENABLE_REG2);
+ writel(readl(SW_INT_MASK_REG2) | (1 << irq), SW_INT_MASK_REG2);
+ writel(readl(SW_INT_IRQ_PENDING_REG2) | (1<<irq), SW_INT_IRQ_PENDING_REG2);
+ }
+}
+
+/* Mask an IRQ line, which means disabling the IRQ line */
+static void sw_irq_mask(struct irq_data *irqd)
+{
+ unsigned int irq = irqd->irq;
+
+ if(irq < 32){
+ writel(readl(SW_INT_ENABLE_REG0) & ~(1<<irq), SW_INT_ENABLE_REG0);
+ writel(readl(SW_INT_MASK_REG0) | (1 << irq), SW_INT_MASK_REG0);
+ } else if(irq < 64){
+ irq -= 32;
+ writel(readl(SW_INT_ENABLE_REG1) & ~(1<<irq), SW_INT_ENABLE_REG1);
+ writel(readl(SW_INT_MASK_REG1) | (1 << irq), SW_INT_MASK_REG1);
+ } else if(irq < 96){
+ irq -= 64;
+ writel(readl(SW_INT_ENABLE_REG2) & ~(1<<irq), SW_INT_ENABLE_REG2);
+ writel(readl(SW_INT_MASK_REG2) | (1 << irq), SW_INT_MASK_REG2);
+ }
+}
+
+static void sw_irq_unmask(struct irq_data *irqd)
+{
+ unsigned int irq = irqd->irq;
+
+ if(irq < 32){
+ writel(readl(SW_INT_ENABLE_REG0) | (1<<irq), SW_INT_ENABLE_REG0);
+ writel(readl(SW_INT_MASK_REG0) & ~(1 << irq), SW_INT_MASK_REG0);
+ if(irq == SW_INT_IRQNO_ENMI) /* must clear pending bit when enabled */
+ writel((1 << SW_INT_IRQNO_ENMI), SW_INT_IRQ_PENDING_REG0);
+ } else if(irq < 64){
+ irq -= 32;
+ writel(readl(SW_INT_ENABLE_REG1) | (1<<irq), SW_INT_ENABLE_REG1);
+ writel(readl(SW_INT_MASK_REG1) & ~(1 << irq), SW_INT_MASK_REG1);
+ } else if(irq < 96){
+ irq -= 64;
+ writel(readl(SW_INT_ENABLE_REG2) | (1<<irq), SW_INT_ENABLE_REG2);
+ writel(readl(SW_INT_MASK_REG2) & ~(1 << irq), SW_INT_MASK_REG2);
+ }
+}
+
+static struct irq_chip sw_vic_chip = {
+ .name = "sw_vic",
+ .irq_ack = sw_irq_ack,
+ .irq_mask = sw_irq_mask,
+ .irq_unmask = sw_irq_unmask,
+};
+
+void __init sw_core_init_irq(void)
+{
+ u32 i = 0;
+
+ /* Disable & clear all interrupts */
+ writel(0, SW_INT_ENABLE_REG0);
+ writel(0, SW_INT_ENABLE_REG1);
+ writel(0, SW_INT_ENABLE_REG2);
+
+ writel(0xffffffff, SW_INT_MASK_REG0);
+ writel(0xffffffff, SW_INT_MASK_REG1);
+ writel(0xffffffff, SW_INT_MASK_REG2);
+
+ writel(0xffffffff, SW_INT_IRQ_PENDING_REG0);
+ writel(0xffffffff, SW_INT_IRQ_PENDING_REG1);
+ writel(0xffffffff, SW_INT_IRQ_PENDING_REG2);
+ writel(0xffffffff, SW_INT_FIQ_PENDING_REG0);
+ writel(0xffffffff, SW_INT_FIQ_PENDING_REG1);
+ writel(0xffffffff, SW_INT_FIQ_PENDING_REG2);
+
+ /*enable protection mode*/
+ writel(0x01, SW_INT_PROTECTION_REG);
+ /*config the external interrupt source type*/
+ writel(0x00, SW_INT_NMI_CTRL_REG);
+
+ for (i = SW_INT_START; i < SW_INT_END; i++) {
+ irq_set_chip(i, &sw_vic_chip);
+ irq_set_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+ }
+}
+
+
+
+/*
+ * Global vars definitions
+ *
+ */
+static void sun4i_restart(char mode, const char *cmd)
+{
+ /* use watch-dog to reset system */
+ #define WATCH_DOG_CTRL_REG (SW_VA_TIMERC_IO_BASE + 0x0094)
+ *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 0;
+ __delay(100000);
+ *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 3;
+ while(1);
+}
+
+static void timer_set_mode(enum clock_event_mode mode, struct clock_event_device *clk)
+{
+ volatile u32 ctrl;
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ pr_info("timer0: Periodic Mode\n");
+ writel(TMR_INTER_VAL, SW_TIMER0_INTVAL_REG); /* interval (999+1) */
+ ctrl = readl(SW_TIMER0_CTL_REG);
+ ctrl &= ~(1<<7); /* Continuous mode */
+ ctrl |= 1; /* Enable this timer */
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ pr_info("timer0: Oneshot Mode\n");
+ ctrl = readl(SW_TIMER0_CTL_REG);
+ ctrl |= (1<<7); /* Single mode */
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ default:
+ ctrl = readl(SW_TIMER0_CTL_REG);
+ ctrl &= ~(1<<0); /* Disable timer0 */
+ break;
+ }
+
+ writel(ctrl, SW_TIMER0_CTL_REG);
+}
+
+/* Useless when periodic mode */
+static int timer_set_next_event(unsigned long evt, struct clock_event_device *unused)
+{
+ volatile u32 ctrl;
+
+ /* clear any pending before continue */
+ ctrl = readl(SW_TIMER0_CTL_REG);
+ writel(evt, SW_TIMER0_CNTVAL_REG);
+ ctrl |= (1<<1);
+ writel(ctrl, SW_TIMER0_CTL_REG);
+ writel(ctrl | 0x1, SW_TIMER0_CTL_REG);
+
+ return 0;
+}
+
+static struct clock_event_device timer0_clockevent = {
+ .name = "timer0",
+ .shift = 32,
+ .rating = 100,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = timer_set_mode,
+ .set_next_event = timer_set_next_event,
+};
+
+
+static irqreturn_t sw_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = (struct clock_event_device *)dev_id;
+
+ writel(0x1, SW_TIMER_INT_STA_REG);
+
+ /*
+ * timer_set_next_event will be called only in ONESHOT mode
+ */
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction sw_timer_irq = {
+ .name = "timer0",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = sw_timer_interrupt,
+ .dev_id = &timer0_clockevent,
+ .irq = SW_INT_IRQNO_TIMER0,
+};
+
+
+static void __init sw_timer_init(void)
+{
+ int ret;
+ volatile u32 val = 0;
+
+ writel(TMR_INTER_VAL, SW_TIMER0_INTVAL_REG);
+ /* set clock sourch to HOSC, 16 pre-division */
+ val = readl(SW_TIMER0_CTL_REG);
+ val &= ~(0x07<<4);
+ val &= ~(0x03<<2);
+ val |= (4<<4) | (1<<2);
+ writel(val, SW_TIMER0_CTL_REG);
+ /* set mode to auto reload */
+ val = readl(SW_TIMER0_CTL_REG);
+ val |= (1<<1);
+ writel(val, SW_TIMER0_CTL_REG);
+
+ ret = setup_irq(SW_INT_IRQNO_TIMER0, &sw_timer_irq);
+ if (ret) {
+ pr_warning("failed to setup irq %d\n", SW_INT_IRQNO_TIMER0);
+ }
+
+ /* Enable time0 interrupt */
+ val = readl(SW_TIMER_INT_CTL_REG);
+ val |= (1<<0);
+ writel(val, SW_TIMER_INT_CTL_REG);
+
+ timer0_clockevent.mult = div_sc(SYS_TIMER_CLKSRC/SYS_TIMER_SCAL, NSEC_PER_SEC, timer0_clockevent.shift);
+ timer0_clockevent.max_delta_ns = clockevent_delta2ns(0xff, &timer0_clockevent);
+ timer0_clockevent.min_delta_ns = clockevent_delta2ns(0x1, &timer0_clockevent);
+ timer0_clockevent.cpumask = cpumask_of(0);
+ timer0_clockevent.irq = sw_timer_irq.irq;
+ clockevents_register_device(&timer0_clockevent);
+}
+
+struct sys_timer sw_sys_timer = {
+ .init = sw_timer_init,
+};
+
+extern void __init sw_pdev_init(void);
+void __init sw_core_init(void)
+{
+ sw_pdev_init();
+}
+enum sw_ic_ver sw_get_ic_ver(void)
+{
+ volatile u32 val = readl(SW_VA_TIMERC_IO_BASE + 0x13c);
+
+ val = (val >> 6) & 0x3;
+
+ if (machine_is_sun4i()) {
+ switch (val) {
+ case 0x00:
+ return MAGIC_VER_A;
+ case 0x03:
+ return MAGIC_VER_B;
+ default:
+ return MAGIC_VER_C;
+ }
+ } else {
+ switch (val) {
+ case 0x03:
+ return MAGIC_VER_B;
+ default:
+ return MAGIC_VER_A;
+ }
+ }
+}
+EXPORT_SYMBOL(sw_get_ic_ver);
+
+MACHINE_START(SUN4I, "sun4i")
+ .atag_offset = 0x100,
+ .timer = &sw_sys_timer,
+#ifdef CONFIG_SUNXI_MALI_RESERVED_MEM
+ .fixup = sunxi_mali_core_fixup,
+#endif
+ .map_io = sw_core_map_io,
+ .init_early = NULL,
+ .init_irq = sw_core_init_irq,
+ .init_machine = sw_core_init,
+ .reserve = sw_core_reserve,
+ .restart = sun4i_restart,
+MACHINE_END
+
+MACHINE_START(SUN5I, "sun5i")
+ .atag_offset = 0x100,
+ .timer = &sw_sys_timer,
+#ifdef CONFIG_SUNXI_MALI_RESERVED_MEM
+ .fixup = sunxi_mali_core_fixup,
+#endif
+ .map_io = sw_core_map_io,
+ .init_early = NULL,
+ .init_irq = sw_core_init_irq,
+ .init_machine = sw_core_init,
+ .reserve = sw_core_reserve,
+ .restart = sun4i_restart,
+MACHINE_END
--
1.8.1.2

Hans de Goede

unread,
Feb 9, 2013, 10:47:54 AM2/9/13
to linux...@googlegroups.com, Hans de Goede
This patch adds the following kernel commandline options:
sunxi_g2d_mem_reserve
sunxi_ve_mem_reserve
sunxi_fb_mem_reserve

These can be set to configure the amount of RAM (in MB) to reserve for the
named sunxi drivers. This is useful for kernels for ready-to-use disk images,
so that an user can choose to use various binary only drivers to use the
hardware blocks in question, while not making the memory unavailable for
general use for users who choose not to use these drivers, without requiring
a kernel recompile.

IE I'm currently using the following since I don't use any binary drivers:
sunxi_g2d_mem_reserve=0 sunxi_ve_mem_reserve=0 sunxi_fb_mem_reserve=16

Giving me an additional 112 MB of extra memory.

Note that:
1) This patch also allows making the reservations larger in an easy way :)
2) This patch removes the comments stating that memory won't be reserved if
the block in question is disabled in the fex file, since this was not true
before this patch, and still is not true after this patch.

Signed-off-by: Hans de Goede <hdeg...@redhat.com>
---
arch/arm/plat-sunxi/core.c | 129 +++++++++++++++++++-----------
arch/arm/plat-sunxi/include/plat/core.h | 2 +-
drivers/media/video/sun4i/sun4i_cedar.c | 4 +
drivers/media/video/sun5i/sun5i_cedar.c | 4 +
drivers/media/video/videobuf-dma-contig.c | 36 +++++----
drivers/video/sunxi/disp/dev_fb.c | 8 ++
6 files changed, 120 insertions(+), 63 deletions(-)

diff --git a/arch/arm/plat-sunxi/core.c b/arch/arm/plat-sunxi/core.c
index 6a89286..ff03205 100644
--- a/arch/arm/plat-sunxi/core.c
+++ b/arch/arm/plat-sunxi/core.c
@@ -130,6 +130,14 @@ int sunxi_pr_chip_id()
return (chip_id != 0);
}

+/*
+ * Only reserve certain important memory blocks if there are actually
+ * drivers which use them.
+ */
+static int reserved_mali_mem;
+static unsigned long reserved_start;
+static unsigned long reserved_max;
+
#ifdef CONFIG_SUNXI_MALI_RESERVED_MEM
void __init sunxi_mali_core_fixup(struct tag *t, char **cmdline,
struct meminfo *mi)
@@ -150,6 +158,7 @@ void __init sunxi_mali_core_fixup(struct tag *t, char **cmdline,
pr_err("MALI: not enough memory in first bank to make reserve.\n");
} else {
mi->bank[0].size = SZ_1M * (512 - 64);
+ reserved_mali_mem = 1;
size -= 512;
if (size) {
bank++;
@@ -186,10 +195,6 @@ void __init sw_core_map_io(void)
sunxi_pr_chip_id();
}

-/* Only reserve certain important memory blocks if there are actually
- * drivers which use them.
- */
-
#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
/* The FB block is used by:
*
@@ -197,81 +202,68 @@ void __init sw_core_map_io(void)
*
* fb_start, fb_size are used in a vast number of other places but for
* for platform-specific drivers, so we don't have to worry about them.
- *
- * The block will only be allocated if the disp_init/disp_init_enabled
- * script key is set.
*/

-unsigned long fb_start = (PLAT_PHYS_OFFSET + SZ_512M - SZ_64M - SZ_32M);
+unsigned long fb_start;
unsigned long fb_size = SZ_32M;
EXPORT_SYMBOL(fb_start);
EXPORT_SYMBOL(fb_size);

-static void __init reserve_fb(void)
+static int __init reserve_fb_param(char *s)
{
- memblock_reserve(fb_start, fb_size);
- pr_reserve_info("LCD ", fb_start, fb_size);
+ unsigned long size;
+ if (kstrtoul(s, 0, &size) == 0)
+ fb_size = size * SZ_1M;
+ return 0;
}
-
-#else
-static void __init reserve_fb(void) {}
+early_param("sunxi_fb_mem_reserve", reserve_fb_param);
#endif

#if defined CONFIG_SUN4I_G2D || defined CONFIG_SUN4I_G2D_MODULE
/* The G2D block is used by:
*
* - the G2D engine, drivers/char/sun4i_g2d
- *
- * The block will only be allocated if the g2d_para/g2d_used
- * script key is set.
*/

-unsigned long g2d_start = (PLAT_PHYS_OFFSET + SZ_512M - SZ_128M);
+unsigned long g2d_start;
unsigned long g2d_size = SZ_1M * 16;
EXPORT_SYMBOL(g2d_start);
EXPORT_SYMBOL(g2d_size);

-static void __init reserve_g2d(void)
+static int __init reserve_g2d_param(char *s)
{
- memblock_reserve(g2d_start, g2d_size);
- pr_reserve_info("G2D ", g2d_start, g2d_size);
+ unsigned long size;
+ if (kstrtoul(s, 0, &size) == 0)
+ g2d_size = size * SZ_1M;
+ return 0;
}
-#else
-static void __init reserve_g2d(void) {}
+early_param("sunxi_g2d_mem_reserve", reserve_g2d_param);
#endif

#if defined CONFIG_VIDEO_DECODER_SUN4I || \
- defined CONFIG_VIDEO_DECODER_SUN4I_MODULE || \
- defined CONFIG_VIDEO_DECODER_SUN5I || \
- defined CONFIG_VIDEO_DECODER_SUN5I_MODULE
+ defined CONFIG_VIDEO_DECODER_SUN4I_MODULE || \
+ defined CONFIG_VIDEO_DECODER_SUN5I || \
+ defined CONFIG_VIDEO_DECODER_SUN5I_MODULE
/* The VE block is used by:
*
* - the Cedar video engine, drivers/media/video/sun4i
- *
- * ve_start, ve_size are also used by the contiguous-DMA module in
- * drivers/media/video/videobuf-dma-contig.c, but that's SH-specific
- * so we don't have to worry about it here.
*/

-unsigned long ve_start = (PLAT_PHYS_OFFSET + SZ_64M);
+#define RESERVE_VE_MEM 1
+
+unsigned long ve_start;
unsigned long ve_size = (SZ_64M + SZ_16M);
EXPORT_SYMBOL(ve_start);
EXPORT_SYMBOL(ve_size);

-static void __init reserve_ve(void)
+static int __init reserve_ve_param(char *s)
{
- /* The users of the VE block aren't enabled via script flags, so if their
- * driver gets compiled in we have to unconditionally reserve memory for
- * them.
- */
- memblock_reserve(ve_start, SZ_64M);
- memblock_reserve(ve_start + SZ_64M, SZ_16M);
-
- pr_reserve_info("VE ", ve_start, ve_size);
+ unsigned long size;
+ if (kstrtoul(s, 0, &size) == 0)
+ ve_size = size * SZ_1M;
+ return 0;
}
-
-#else
-static void __init reserve_ve(void) {}
+early_param("sunxi_ve_mem_reserve", reserve_ve_param);
#endif

static void reserve_sys(void)
@@ -280,13 +272,58 @@ static void reserve_sys(void)
pr_reserve_info("SYS ", SYS_CONFIG_MEMBASE, SYS_CONFIG_MEMSIZE);
}

+#if defined RESERVE_VE_MEM || defined CONFIG_SUN4I_G2D || \
+ defined CONFIG_SUN4I_G2D_MODULE || defined CONFIG_FB_SUNXI_RESERVED_MEM
+static void reserve_mem(unsigned long *start, unsigned long *size,
+ const char *desc)
+{
+ if (*size == 0) {
+ *start = 0;
+ return;
+ }
+
+ if ((reserved_start + *size) > reserved_max) {
+ pr_warn("Not enough memory to reserve memory for %s\n", desc);
+ *start = 0;
+ *size = 0;
+ return;
+ }
+ *start = reserved_start;
+ memblock_reserve(*start, *size);
+ pr_reserve_info(desc, *start, *size);
+ reserved_start += *size;
+}
+#endif
+
static void __init sw_core_reserve(void)
{
pr_info("Memory Reserved:\n");
reserve_sys();
- reserve_ve();
- reserve_g2d();
- reserve_fb();
+ /* 0 - 64M is used by reserve_sys */
+ reserved_start = meminfo.bank[0].start + SZ_64M;
+ reserved_max = meminfo.bank[0].start + meminfo.bank[0].size;
+#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
+ if (reserved_mali_mem) {
+ /* The stupid mali blob expects the fb at a fixed address :( */
+ fb_start = meminfo.bank[0].start + SZ_512M - SZ_64M - SZ_32M;
+ if (fb_start < reserved_max)
+ reserved_max = fb_start;
+ }
+#endif
+#ifdef RESERVE_VE_MEM
+ reserve_mem(&ve_start, &ve_size, "VE ");
+#endif
+#if defined CONFIG_SUN4I_G2D || defined CONFIG_SUN4I_G2D_MODULE
+ reserve_mem(&g2d_start, &g2d_size, "G2D ");
+#endif
+#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
+ if (reserved_mali_mem) {
+ /* Give the mali blob the fb at its expected address */
+ reserved_start = fb_start;
+ reserved_max = meminfo.bank[0].start + meminfo.bank[0].size;
+ }
+ reserve_mem(&fb_start, &fb_size, "LCD ");
+#endif
}

void sw_irq_ack(struct irq_data *irqd)
diff --git a/arch/arm/plat-sunxi/include/plat/core.h b/arch/arm/plat-sunxi/include/plat/core.h
index 1e7467f..0c36717 100644
--- a/arch/arm/plat-sunxi/include/plat/core.h
+++ b/arch/arm/plat-sunxi/include/plat/core.h
@@ -18,7 +18,7 @@
#define _SUNXI_CORE_H

#define pr_reserve_info(L, START, SIZE) \
- pr_info("\t" L " : 0x%08x - 0x%08x (%4d %s)\n", \
+ pr_info("\t%s : 0x%08x - 0x%08x (%4d %s)\n", L, \
(u32)(START), (u32)((START) + (SIZE) - 1), \
(u32)((SIZE) < SZ_1M ? (SIZE) / SZ_1K : (SIZE) / SZ_1M), \
(SIZE) < SZ_1M ? "kB" : "MB")
diff --git a/drivers/media/video/sun4i/sun4i_cedar.c b/drivers/media/video/sun4i/sun4i_cedar.c
index 733ecb2..ab2b396 100644
--- a/drivers/media/video/sun4i/sun4i_cedar.c
+++ b/drivers/media/video/sun4i/sun4i_cedar.c
@@ -963,6 +963,10 @@ static int __init cedardev_init(void)
int devno;
unsigned int val;
dev_t dev = 0;
+
+ if (ve_size == 0)
+ return -ENODEV;
+
printk("[cedar dev]: install start!!!\n");
if((platform_device_register(&sw_device_cedar))<0)
return err;
diff --git a/drivers/media/video/sun5i/sun5i_cedar.c b/drivers/media/video/sun5i/sun5i_cedar.c
index f4c2c7d..d3ffc4d 100644
--- a/drivers/media/video/sun5i/sun5i_cedar.c
+++ b/drivers/media/video/sun5i/sun5i_cedar.c
@@ -957,6 +957,10 @@ static int __init cedardev_init(void)
int devno;
unsigned int val;
dev_t dev = 0;
+
+ if (ve_size == 0)
+ return -ENODEV;
+
printk("[cedar dev]: install start!!!\n");
if((platform_device_register(&sw_device_cedar))<0)
return err;
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index 7920585..97429a4 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -37,9 +37,11 @@ struct videobuf_dma_contig_memory {
BUG(); \
}

-// #define USE_DMA_CONTIG
-
-#ifndef USE_DMA_CONTIG
+#if defined CONFIG_VIDEO_DECODER_SUN4I || \
+ defined CONFIG_VIDEO_DECODER_SUN4I_MODULE || \
+ defined CONFIG_VIDEO_DECODER_SUN5I || \
+ defined CONFIG_VIDEO_DECODER_SUN5I_MODULE
+#define RESERVE_VE_MEM 1
extern unsigned long ve_start;
extern unsigned long ve_size;
#endif
@@ -98,10 +100,12 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
dev_dbg(q->dev, "buf[%d] freeing %p\n",
i, mem->vaddr);

-#ifdef USE_DMA_CONTIG
- dma_free_coherent(q->dev, mem->size,
+#ifdef RESERVE_VE_MEM
+ if (ve_size == 0)
+#endif
+ dma_free_coherent(q->dev, mem->size,
mem->vaddr, mem->dma_handle);
-#endif // USE_DMA_CONTIG
+
mem->vaddr = NULL;
}

@@ -293,16 +297,16 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,

mem->size = PAGE_ALIGN(buf->bsize);

-#ifdef USE_DMA_CONTIG
- mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
- &mem->dma_handle, GFP_KERNEL);
-#else
-
- mem->dma_handle = (ve_start + ve_size - 24*1024*1024 + buf->i * mem->size + 4095) & (~(4095)); //4k aligned
- mem->vaddr = (void *)(mem->dma_handle + 0x80000000); // not used
-
-#endif
-
+#ifdef RESERVE_VE_MEM
+ if (ve_size) {
+ mem->dma_handle = (ve_start + ve_size - 24 * 1024 * 1024 +
+ buf->i * mem->size + 4095) & (~(4095)); /* 4k aligned */
+ mem->vaddr = (void *)(mem->dma_handle + 0x80000000); /* NA */
+ } else
+#endif
+ mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
+ &mem->dma_handle, GFP_KERNEL);
+
if (!mem->vaddr) {
dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
mem->size);
diff --git a/drivers/video/sunxi/disp/dev_fb.c b/drivers/video/sunxi/disp/dev_fb.c
index e8ea392..08b5fa5 100644
--- a/drivers/video/sunxi/disp/dev_fb.c
+++ b/drivers/video/sunxi/disp/dev_fb.c
@@ -170,6 +170,12 @@ parser_disp_init_para(__disp_init_t *init_para)
return -1;
}
init_para->b_init = value;
+#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
+ if (init_para->b_init && fb_size == 0) {
+ pr_warn("0 bytes reserved for fb mem, disabling display\n");
+ init_para->b_init = 0;
+ }
+#endif

if (script_parser_fetch("disp_init", "disp_mode", &value, 1) < 0) {
__wrn("fetch script data disp_init.disp_mode fail\n");
@@ -483,6 +489,8 @@ static int __init Fb_map_video_memory(__u32 fb_id, struct fb_info *info)
}
#else
g_fbi.malloc_screen_base[fb_id] = disp_malloc(info->fix.smem_len);
+ if (g_fbi.malloc_screen_base[fb_id] == NULL)
+ return -ENOMEM;
info->fix.smem_start = (unsigned long)
__pa(g_fbi.malloc_screen_base[fb_id]);
info->screen_base = ioremap_wc(info->fix.smem_start,
--
1.8.1.2

Hans de Goede

unread,
Feb 9, 2013, 10:47:55 AM2/9/13
to linux...@googlegroups.com, Hans de Goede
This patch adds a new "sunxi_no_mali_mem_reserve" kernel commandline options
to disable the memory reservation for the mali driver, makign the 64MB of
RAM in question usable for other purposes.

Signed-off-by: Hans de Goede <hdeg...@redhat.com>
---
arch/arm/plat-sunxi/core.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/arch/arm/plat-sunxi/core.c b/arch/arm/plat-sunxi/core.c
index ff03205..ada7a93 100644
--- a/arch/arm/plat-sunxi/core.c
+++ b/arch/arm/plat-sunxi/core.c
@@ -139,11 +139,22 @@ static unsigned long reserved_start;
static unsigned long reserved_max;

#ifdef CONFIG_SUNXI_MALI_RESERVED_MEM
-void __init sunxi_mali_core_fixup(struct tag *t, char **cmdline,
+void __init sunxi_mali_core_fixup(struct tag *tags, char **cmdline,
struct meminfo *mi)
{
+ struct tag *t;
u32 bank = 0;
- for (; t->hdr.size; t = tag_next(t)) if (t->hdr.tag == ATAG_MEM) {
+
+ for (t = tags; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag != ATAG_CMDLINE)
+ continue;
+ if (strstr(t->u.cmdline.cmdline, "sunxi_no_mali_mem_reserve"))
+ return;
+ }
+
+ for (t = tags; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag != ATAG_MEM)
+ continue;
if (bank) {
mi->nr_banks++;
mi->bank[bank].start = t->u.mem.start;
--
1.8.1.2

Hans de Goede

unread,
Feb 9, 2013, 10:47:56 AM2/9/13
to linux...@googlegroups.com, Hans de Goede
Since it is now possible to disable fb-mem reservation from the cmdline,
enable the kmalloc fallback path even if CONFIG_FB_SUNXI_RESERVED_MEM is set.

Signed-off-by: Hans de Goede <hdeg...@redhat.com>
---
drivers/video/sunxi/disp/dev_disp.c | 58 ++++++++++++++++---------------------
drivers/video/sunxi/disp/dev_fb.c | 40 ++++++++++++-------------
2 files changed, 45 insertions(+), 53 deletions(-)

diff --git a/drivers/video/sunxi/disp/dev_disp.c b/drivers/video/sunxi/disp/dev_disp.c
index 567882f..dd08066 100644
--- a/drivers/video/sunxi/disp/dev_disp.c
+++ b/drivers/video/sunxi/disp/dev_disp.c
@@ -313,13 +313,30 @@ __s32 DRV_DISP_Exit(void)
static int
disp_mem_request(int sel, __u32 size)
{
-#ifndef CONFIG_FB_SUNXI_RESERVED_MEM
unsigned map_size = 0;
struct page *page;

if (g_disp_mm[sel].info_base != NULL)
return -EINVAL;

+#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
+ if (fb_size) {
+ void *ret = disp_malloc(size);
+ if (ret) {
+ g_disp_mm[sel].info_base = ret;
+ g_disp_mm[sel].mem_start =
+ virt_to_phys(g_disp_mm[sel].info_base);
+ memset(g_disp_mm[sel].info_base, 0, size);
+ __inf("pa=0x%08lx va=0x%p size:0x%x\n",
+ g_disp_mm[sel].mem_start,
+ g_disp_mm[sel].info_base, size);
+ return 0;
+ } else {
+ __wrn("disp_malloc fail!\n");
+ return -ENOMEM;
+ }
+ }
+#endif
g_disp_mm[sel].mem_len = size;
map_size = PAGE_ALIGN(g_disp_mm[sel].mem_len);

@@ -342,50 +359,25 @@ disp_mem_request(int sel, __u32 size)
__wrn("alloc_pages fail!\n");
return -ENOMEM;
}
-#else
- void *ret;
-
- ret = disp_malloc(size);
- if (ret) {
- g_disp_mm[sel].info_base = ret;
- g_disp_mm[sel].mem_start =
- virt_to_phys(g_disp_mm[sel].info_base);
- memset(g_disp_mm[sel].info_base, 0, size);
- __inf("pa=0x%08lx va=0x%p size:0x%x\n",
- g_disp_mm[sel].mem_start, g_disp_mm[sel].info_base, size);
-
- return 0;
- } else {
- __wrn("disp_malloc fail!\n");
- return -ENOMEM;
- }
-#endif
-
}

static int
disp_mem_release(int sel)
{
-#ifndef CONFIG_FB_SUNXI_RESERVED_MEM
unsigned map_size = PAGE_ALIGN(g_disp_mm[sel].mem_len);
- unsigned page_size = map_size;
-
- if (g_disp_mm[sel].info_base == 0)
- return -EINVAL;

- free_pages((unsigned long)(g_disp_mm[sel].info_base),
- get_order(page_size));
- memset(&g_disp_mm[sel], 0, sizeof(struct info_mm));
-#else
if (g_disp_mm[sel].info_base == NULL)
return -EINVAL;

- disp_free((void *)g_disp_mm[sel].info_base);
- memset(&g_disp_mm[sel], 0, sizeof(struct info_mm));
+#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
+ if (fb_size)
+ disp_free((void *)g_disp_mm[sel].info_base);
+ else
#endif
-
+ free_pages((unsigned long)(g_disp_mm[sel].info_base),
+ get_order(map_size));
+ memset(&g_disp_mm[sel], 0, sizeof(struct info_mm));
return 0;
-
}

int disp_mmap(struct file *filp, struct vm_area_struct *vma)
diff --git a/drivers/video/sunxi/disp/dev_fb.c b/drivers/video/sunxi/disp/dev_fb.c
index 08b5fa5..d5f4850 100644
--- a/drivers/video/sunxi/disp/dev_fb.c
+++ b/drivers/video/sunxi/disp/dev_fb.c
@@ -170,12 +170,6 @@ parser_disp_init_para(__disp_init_t *init_para)
return -1;
}
init_para->b_init = value;
-#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
- if (init_para->b_init && fb_size == 0) {
- pr_warn("0 bytes reserved for fb mem, disabling display\n");
- init_para->b_init = 0;
- }
-#endif

if (script_parser_fetch("disp_init", "disp_mode", &value, 1) < 0) {
__wrn("fetch script data disp_init.disp_mode fail\n");
@@ -471,10 +465,13 @@ fb_draw_gray_pictures(__u32 base, __u32 width, __u32 height,

static int __init Fb_map_video_memory(__u32 fb_id, struct fb_info *info)
{
-#ifndef CONFIG_FB_SUNXI_RESERVED_MEM
unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
struct page *page;

+#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
+ if (fb_size)
+ goto use_reserved_mem;
+#endif
page = alloc_pages(GFP_KERNEL, get_order(map_size));
if (page != NULL) {
info->screen_base = page_address(page);
@@ -487,7 +484,8 @@ static int __init Fb_map_video_memory(__u32 fb_id, struct fb_info *info)
__wrn("alloc_pages fail!\n");
return -ENOMEM;
}
-#else
+#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
+use_reserved_mem:
g_fbi.malloc_screen_base[fb_id] = disp_malloc(info->fix.smem_len);
if (g_fbi.malloc_screen_base[fb_id] == NULL)
return -ENOMEM;
@@ -513,20 +511,22 @@ static int __init Fb_map_video_memory(__u32 fb_id, struct fb_info *info)

static inline void Fb_unmap_video_memory(__u32 fb_id, struct fb_info *info)
{
-#ifndef CONFIG_FB_SUNXI_RESERVED_MEM
unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
-
- free_pages((unsigned long)info->screen_base, get_order(map_size));
-#else
- if ((void *)info->screen_base != g_fbi.malloc_screen_base[fb_id]) {
- __inf("Fb_unmap_video_memory: fb_id=%d, iounmap(%p)\n",
- fb_id, info->screen_base);
- iounmap(info->screen_base);
- }
- __inf("Fb_unmap_video_memory: fb_id=%d, disp_free(%p)\n",
- fb_id, g_fbi.malloc_screen_base[fb_id]);
- disp_free(g_fbi.malloc_screen_base[fb_id]);
+#ifdef CONFIG_FB_SUNXI_RESERVED_MEM
+ if (fb_size) {
+ if ((void *)info->screen_base !=
+ g_fbi.malloc_screen_base[fb_id]) {
+ __inf("Fb_unmap_video_memory: fb_id=%d, iounmap(%p)\n",
+ fb_id, info->screen_base);
+ iounmap(info->screen_base);
+ }
+ __inf("Fb_unmap_video_memory: fb_id=%d, disp_free(%p)\n",
+ fb_id, g_fbi.malloc_screen_base[fb_id]);
+ disp_free(g_fbi.malloc_screen_base[fb_id]);
+ } else
#endif
+ free_pages((unsigned long)info->screen_base,
+ get_order(map_size));
}

/*
--
1.8.1.2

Hans de Goede

unread,
Feb 9, 2013, 10:47:57 AM2/9/13
to linux...@googlegroups.com, Hans de Goede
This stops dmesg showing these:
[ 86.720000] platform reg-20-cs-buck3: Driver reg-20-cs-buck3 requests probe deferral
[ 86.720000] platform reg-20-cs-buck2: Driver reg-20-cs-buck2 requests probe deferral
[ 86.740000] platform reg-20-cs-ldo4: Driver reg-20-cs-ldo4 requests probe def
<snip more of the same>

Each time a usb device gets (un)plugged when using a generic kernel build
(so with axp20 enabled) on a board without a pmu, such as the mk802, or
the a13_olinuxino_micro.

Signed-off-by: Hans de Goede <hdeg...@redhat.com>
---
drivers/power/axp_power/virtual20_dev.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/power/axp_power/virtual20_dev.c b/drivers/power/axp_power/virtual20_dev.c
index 27edcf0..933c9cd 100644
--- a/drivers/power/axp_power/virtual20_dev.c
+++ b/drivers/power/axp_power/virtual20_dev.c
@@ -29,6 +29,7 @@
#include <mach/irqs.h>
#include <linux/power_supply.h>
#include <linux/mfd/axp-mfd.h>
+#include <plat/sys_config.h>

#include "axp-cfg.h"

@@ -74,9 +75,14 @@ static struct platform_device virt[]={



- static int __init virtual_init(void)
+static int __init virtual_init(void)
{
- int j,ret;
+ int j, ret, used = 0;
+
+ ret = script_parser_fetch("pmu_para", "pmu_used", &used, sizeof(int));
+ if (ret || !used)
+ return -ENODEV;
+
for (j = 0; j < ARRAY_SIZE(virt); j++){
ret = platform_device_register(&virt[j]);
if (ret)
--
1.8.1.2

Hans de Goede

unread,
Feb 9, 2013, 10:47:58 AM2/9/13
to linux...@googlegroups.com, Hans de Goede
Note if you do a diff between the 2, there are some differences, where as
this patch just moves the mach-sun4i code to plat-sunxi. The reason for this
is that the sun5i code does the same as the sun4i code just in a more
complicated manner.

Signed-off-by: Hans de Goede <hdeg...@redhat.com>
---
arch/arm/mach-sun4i/Makefile | 1 -
arch/arm/mach-sun4i/cpu-freq/Makefile | 2 -
arch/arm/mach-sun4i/cpu-freq/cpu-freq-table.c | 127 -----
arch/arm/mach-sun4i/cpu-freq/cpu-freq.c | 730 ------------------------
arch/arm/mach-sun4i/cpu-freq/cpu-freq.h | 87 ---
arch/arm/mach-sun5i/Makefile | 1 -
arch/arm/mach-sun5i/cpu-freq/Makefile | 2 -
arch/arm/mach-sun5i/cpu-freq/cpu-freq-table.c | 87 ---
arch/arm/mach-sun5i/cpu-freq/cpu-freq.c | 779 --------------------------
arch/arm/mach-sun5i/cpu-freq/cpu-freq.h | 71 ---
arch/arm/plat-sunxi/Makefile | 1 +
arch/arm/plat-sunxi/cpu-freq/Makefile | 2 +
arch/arm/plat-sunxi/cpu-freq/cpu-freq-table.c | 127 +++++
arch/arm/plat-sunxi/cpu-freq/cpu-freq.c | 730 ++++++++++++++++++++++++
arch/arm/plat-sunxi/cpu-freq/cpu-freq.h | 87 +++
15 files changed, 947 insertions(+), 1887 deletions(-)
delete mode 100644 arch/arm/mach-sun4i/cpu-freq/Makefile
delete mode 100644 arch/arm/mach-sun4i/cpu-freq/cpu-freq-table.c
delete mode 100644 arch/arm/mach-sun4i/cpu-freq/cpu-freq.c
delete mode 100644 arch/arm/mach-sun4i/cpu-freq/cpu-freq.h
delete mode 100644 arch/arm/mach-sun5i/cpu-freq/Makefile
delete mode 100644 arch/arm/mach-sun5i/cpu-freq/cpu-freq-table.c
delete mode 100644 arch/arm/mach-sun5i/cpu-freq/cpu-freq.c
delete mode 100644 arch/arm/mach-sun5i/cpu-freq/cpu-freq.h
create mode 100644 arch/arm/plat-sunxi/cpu-freq/Makefile
create mode 100644 arch/arm/plat-sunxi/cpu-freq/cpu-freq-table.c
create mode 100644 arch/arm/plat-sunxi/cpu-freq/cpu-freq.c
create mode 100644 arch/arm/plat-sunxi/cpu-freq/cpu-freq.h

diff --git a/arch/arm/mach-sun4i/Makefile b/arch/arm/mach-sun4i/Makefile
index b1d6960..edfae11 100644
--- a/arch/arm/mach-sun4i/Makefile
+++ b/arch/arm/mach-sun4i/Makefile
@@ -2,4 +2,3 @@ obj-y += clock/
obj-y += dma/
obj-y += devices.o
obj-$(CONFIG_PM) += pm/
-obj-$(CONFIG_CPU_FREQ) += cpu-freq/
diff --git a/arch/arm/mach-sun4i/cpu-freq/Makefile b/arch/arm/mach-sun4i/cpu-freq/Makefile
deleted file mode 100644
index dc5d307..0000000
--- a/arch/arm/mach-sun4i/cpu-freq/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-obj-$(CONFIG_CPU_FREQ) += cpu-freq.o cpu-freq-table.o
-
diff --git a/arch/arm/mach-sun4i/cpu-freq/cpu-freq-table.c b/arch/arm/mach-sun4i/cpu-freq/cpu-freq-table.c
deleted file mode 100644
index 04228bf..0000000
--- a/arch/arm/mach-sun4i/cpu-freq/cpu-freq-table.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * arch/arm/mach-sun4i/cpu-freq/cpu-freq-table.c
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Kevin Zhang <ke...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-
-#include <linux/types.h>
-#include <linux/clk.h>
-#include <linux/cpufreq.h>
-#include "cpu-freq.h"
-
-static struct cpufreq_frequency_table sun4i_freq_tbl[] = {
-
- { .frequency = 30000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 48000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 60000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 72000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 84000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 96000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 108000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 120000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 132000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 144000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 156000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 168000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 180000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 192000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 204000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 216000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 240000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 264000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 288000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 300000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 336000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 360000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 384000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 408000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 432000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 480000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 528000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 576000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 600000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 648000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 672000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 696000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 720000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 744000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 768000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 816000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 864000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 912000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 960000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 1008000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- #if(1)
- { .frequency = 1056000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 1104000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 1152000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 1200000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 1248000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- { .frequency = 1296000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- { .frequency = 1344000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- { .frequency = 1392000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- { .frequency = 1440000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- { .frequency = 1488000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- #endif
-
- /* table end */
- { .frequency = CPUFREQ_TABLE_END, .index = 0, },
-};
-
-/* div, pll (Hz) table */
-static struct cpufreq_div_order sun4i_div_order_tbl[] = {
- { .div = SUN4I_CLK_DIV(1, 1, 1, 2), .pll = 204000000, },
- { .div = SUN4I_CLK_DIV(1, 1, 2, 2), .pll = 408000000, },
- { .div = SUN4I_CLK_DIV(1, 2, 2, 2), .pll = 816000000, },
- { .div = SUN4I_CLK_DIV(1, 3, 2, 2), .pll = 1200000000, },
- { .div = SUN4I_CLK_DIV(1, 4, 2, 2), .pll = 1248000000, },
-};
-
-#ifdef CONFIG_CPU_FREQ_DVFS
-static struct cpufreq_dvfs sun4i_dvfs_table[] = {
- {.freq = 1056000000, .volt = 1500}, /* core vdd is 1.50v if cpu frequency is (1008Mhz, xxxxMhz] */
- {.freq = 1008000000, .volt = 1400}, /* core vdd is 1.40v if cpu frequency is (960Mhz, 1008Mhz] */
- {.freq = 960000000, .volt = 1400}, /* core vdd is 1.40v if cpu frequency is (912Mhz, 960Mhz] */
- {.freq = 912000000, .volt = 1350}, /* core vdd is 1.35v if cpu frequency is (864Mhz, 912Mhz] */
- {.freq = 864000000, .volt = 1300}, /* core vdd is 1.30v if cpu frequency is (624Mhz, 864Mhz] */
- {.freq = 624000000, .volt = 1250}, /* core vdd is 1.25v if cpu frequency is (432Mhz, 624Mhz] */
- {.freq = 432000000, .volt = 1250}, /* core vdd is 1.25v if cpu frequency is (0, 432Mhz] */
- {.freq = 0, .volt = 1000}, /* end of cpu dvfs table */
-};
-#endif
-
-struct cpufreq_frequency_table * sunxi_cpufreq_table(void) {
- /* TODO: improve to handle A13 and others */
- return sun4i_freq_tbl;
-}
-
-struct cpufreq_div_order * sunxi_div_order_table(int *length) {
- /* TODO: improve to handle A13 and others */
- *length = ARRAY_SIZE(sun4i_div_order_tbl);
- return sun4i_div_order_tbl;
-}
-
-#ifdef CONFIG_CPU_FREQ_DVFS
-struct cpufreq_dvfs * sunxi_dvfs_table(void) {
- /* TODO: improve to handle A13 and others */
- return sun4i_dvfs_table;
-}
-#endif
diff --git a/arch/arm/mach-sun4i/cpu-freq/cpu-freq.c b/arch/arm/mach-sun4i/cpu-freq/cpu-freq.c
deleted file mode 100644
index 099ea80..0000000
--- a/arch/arm/mach-sun4i/cpu-freq/cpu-freq.c
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * arch/arm/mach-sun4i/cpu-freq/cpu-freq.c
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Kevin Zhang <ke...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/cpufreq.h>
-#include <linux/cpu.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/regulator/consumer.h>
-#include "cpu-freq.h"
-
-
-static struct sun4i_cpu_freq_t cpu_cur; /* current cpu frequency configuration */
-static unsigned int last_target = ~0; /* backup last target frequency */
-
-static struct clk *clk_pll; /* pll clock handler */
-static struct clk *clk_cpu; /* cpu clock handler */
-static struct clk *clk_axi; /* axi clock handler */
-static struct clk *clk_ahb; /* ahb clock handler */
-static struct clk *clk_apb; /* apb clock handler */
-
-
-#ifdef CONFIG_CPU_FREQ_DVFS
-static struct regulator *corevdd;
-static unsigned int last_vdd = 1400; /* backup last target voltage, default is 1.4v */
-#endif
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_verify
-*
-*Description: check if the cpu frequency policy is valid;
-*
-*Arguments : policy cpu frequency policy;
-*
-*Return : result, return if verify ok, else return -EINVAL;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_verify(struct cpufreq_policy *policy)
-{
- if (policy->cpu != 0)
- return -EINVAL;
-
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_show
-*
-*Description: show cpu frequency information;
-*
-*Arguments : pfx name;
-*
-*
-*Return :
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static void sun4i_cpufreq_show(const char *pfx, struct sun4i_cpu_freq_t *cfg)
-{
- CPUFREQ_DBG("%s: pll=%u, cpudiv=%u, axidiv=%u, ahbdiv=%u, apb=%u\n",
- pfx, cfg->pll, cfg->div.s.cpu_div, cfg->div.s.axi_div, cfg->div.s.ahb_div, cfg->div.s.apb_div);
-}
-
-
-#ifdef CONFIG_CPU_FREQ_DVFS
-/*
-*********************************************************************************************************
-* __get_vdd_value
-*
-*Description: get vdd with cpu frequency.
-*
-*Arguments : freq cpu frequency;
-*
-*Return : vdd value;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static inline unsigned int __get_vdd_value(unsigned int freq)
-{
- static struct cpufreq_dvfs *dvfs = NULL;
- struct cpufreq_dvfs *dvfs_inf;
-
- if (unlikely(dvfs == NULL))
- dvfs = sunxi_dvfs_table();
-
- dvfs_inf = dvfs;
- while((dvfs_inf+1)->freq >= freq) dvfs_inf++;
-
- return dvfs_inf->volt;
-}
-#endif
-
-
-/*
-*********************************************************************************************************
-* __set_cpufreq_hw
-*
-*Description: set cpu frequency configuration to hardware.
-*
-*Arguments : freq frequency configuration;
-*
-*Return : result
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static inline int __set_cpufreq_hw(struct sun4i_cpu_freq_t *freq)
-{
- int ret;
- unsigned int frequency;
-
- /* try to adjust pll frequency */
- ret = clk_set_rate(clk_pll, freq->pll);
- /* try to adjust cpu frequency */
- frequency = freq->pll / freq->div.s.cpu_div;
- ret |= clk_set_rate(clk_cpu, frequency);
- /* try to adjuxt axi frequency */
- frequency /= freq->div.s.axi_div;
- ret |= clk_set_rate(clk_axi, frequency);
- /* try to adjust ahb frequency */
- frequency /= freq->div.s.ahb_div;
- ret |= clk_set_rate(clk_ahb, frequency);
- /* try to adjust apb frequency */
- frequency /= freq->div.s.apb_div;
- ret |= clk_set_rate(clk_apb, frequency);
-
- return ret;
-}
-
-
-/*
-*********************************************************************************************************
-* __set_cpufreq_target
-*
-*Description: set target frequency, the frequency limitation of axi is 450Mhz, the frequency
-* limitation of ahb is 250Mhz, and the limitation of apb is 150Mhz. for usb connecting,
-* the frequency of ahb must not lower than 60Mhz.
-*
-*Arguments : old cpu/axi/ahb/apb frequency old configuration.
-* new cpu/axi/ahb/apb frequency new configuration.
-*
-*Return : result, 0 - set frequency successed, !0 - set frequency failed;
-*
-*Notes : we check two frequency point: 204Mhz, 408Mhz, 816Mhz and 1200Mhz.
-* if increase cpu frequency, the flow should be:
-* low(1:1:1:2) -> 204Mhz(1:1:1:2) -> 204Mhz(1:1:2:2) -> 408Mhz(1:1:2:2)
-* -> 408Mhz(1:2:2:2) -> 816Mhz(1:2:2:2) -> 816Mhz(1:3:2:2) -> 1200Mhz(1:3:2:2)
-* -> 1200Mhz(1:4:2:2) -> target(1:4:2:2) -> target(x:x:x:x)
-* if decrease cpu frequency, the flow should be:
-* high(x:x:x:x) -> target(1:4:2:2) -> 1200Mhz(1:4:2:2) -> 1200Mhz(1:3:2:2)
-* -> 816Mhz(1:3:2:2) -> 816Mhz(1:2:2:2) -> 408Mhz(1:2:2:2) -> 408Mhz(1:1:2:2)
-* -> 204Mhz(1:1:2:2) -> 204Mhz(1:1:1:2) -> target(1:1:1:2)
-*********************************************************************************************************
-*/
-static int __set_cpufreq_target(struct sun4i_cpu_freq_t *old, struct sun4i_cpu_freq_t *new)
-{
- int ret = 0;
- int div_table_len;
- unsigned int i = 0;
- unsigned int j = 0;
- struct sun4i_cpu_freq_t old_freq, new_freq;
- static struct cpufreq_div_order *div_order_tbl = NULL;
-
- if (unlikely(div_order_tbl == NULL))
- div_order_tbl = sunxi_div_order_table(&div_table_len);
-
- if (!old || !new)
- return -EINVAL;
-
- old_freq = *old;
- new_freq = *new;
-
- CPUFREQ_INF("cpu: %dMhz->%dMhz\n", old_freq.pll/1000000, new_freq.pll/1000000);
-
- /* We're raising our clock */
- if (new_freq.pll > old_freq.pll) {
- /* We have a div table, the old and the new divs,
- * let's change them in order */
-
- /* Figure out old one */
- while (i < div_table_len-1 &&
- div_order_tbl[i].pll < old_freq.pll) i++;
-
- /* Figure out new one */
- j = i; /* it's either the same or bigger */
- while (j < div_table_len-1 &&
- div_order_tbl[j].pll < new_freq.pll) j++;
-
- for (; i < div_table_len-1 && i < j; i++) {
- old_freq.pll = div_order_tbl[i].pll;
- old_freq.div.i = div_order_tbl[i].div;
- ret |= __set_cpufreq_hw(&old_freq);
-
- old_freq.div.i = div_order_tbl[i+1].div;
- ret |= __set_cpufreq_hw(&old_freq);
- }
- /* We're lowering our clock */
- } else if (new_freq.pll < old_freq.pll) {
- /* We have a div table, the old and the new divs, let's change them in order */
-
- /* Figure out new one*/
- while (i < div_table_len-1 &&
- div_order_tbl[i].pll < new_freq.pll) i++;
-
- /* Figure out old one */
- j = i; /* it's either the same or bigger */
- while (j < div_table_len-1 &&
- div_order_tbl[j].pll < old_freq.pll) j++;
-
- for (; j > 0 && i < j; j--) {
- old_freq.pll = div_order_tbl[j-1].pll;
- old_freq.div.i = div_order_tbl[j].div;
- ret |= __set_cpufreq_hw(&old_freq);
-
- old_freq.div.i = div_order_tbl[j-1].div;
- ret |= __set_cpufreq_hw(&old_freq);
- }
- }
-
- /* adjust to target frequency */
- ret |= __set_cpufreq_hw(&new_freq);
-
- if (ret) {
- unsigned int frequency;
-
- CPUFREQ_ERR("try to set target frequency failed!\n");
-
- /* try to restore frequency configuration */
- frequency = clk_get_rate(clk_cpu);
- frequency /= 4;
- clk_set_rate(clk_axi, frequency);
- frequency /= 2;
- clk_set_rate(clk_ahb, frequency);
- frequency /= 2;
- clk_set_rate(clk_apb, frequency);
-
- clk_set_rate(clk_pll, old->pll);
- frequency = old->pll / old->div.s.cpu_div;
- clk_set_rate(clk_cpu, frequency);
- frequency /= old->div.s.axi_div;
- clk_set_rate(clk_axi, frequency);
- frequency /= old->div.s.ahb_div;
- clk_set_rate(clk_ahb, frequency);
- frequency /= old->div.s.apb_div;
- clk_set_rate(clk_apb, frequency);
-
- CPUFREQ_ERR(KERN_ERR "no compatible settings cpu freq for %d\n", new_freq.pll);
- return -1;
- }
-
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_settarget
-*
-*Description: adjust cpu frequency;
-*
-*Arguments : policy cpu frequency policy, to mark if need notify;
-* cpu_freq new cpu frequency configuration;
-*
-*Return : return 0 if set successed, otherwise, return -EINVAL
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_settarget(struct cpufreq_policy *policy, struct sun4i_cpu_freq_t *cpu_freq)
-{
- struct cpufreq_freqs freqs;
- struct sun4i_cpu_freq_t cpu_new;
-
- #ifdef CONFIG_CPU_FREQ_DVFS
- unsigned int new_vdd;
- #endif
-
- /* show current cpu frequency configuration, just for debug */
- sun4i_cpufreq_show("cur", &cpu_cur);
-
- /* get new cpu frequency configuration */
- cpu_new = *cpu_freq;
- sun4i_cpufreq_show("new", &cpu_new);
-
- /* notify that cpu clock will be adjust if needed */
- if (policy) {
- freqs.cpu = 0;
- freqs.old = cpu_cur.pll / 1000;
- freqs.new = cpu_new.pll / 1000;
- cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
- }
-
- #ifdef CONFIG_CPU_FREQ_DVFS
- /* get vdd value for new frequency */
- new_vdd = __get_vdd_value(cpu_new.pll);
-
- if(corevdd && (new_vdd > last_vdd)) {
- CPUFREQ_INF("set core vdd to %d\n", new_vdd);
- if(regulator_set_voltage(corevdd, new_vdd*1000, new_vdd*1000)) {
- CPUFREQ_INF("try to set voltage failed!\n");
-
- /* notify everyone that clock transition finish */
- if (policy) {
- freqs.cpu = 0;
- freqs.old = freqs.new;
- freqs.new = cpu_cur.pll / 1000;
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- }
- return -EINVAL;
- }
- }
- #endif
-
- if(__set_cpufreq_target(&cpu_cur, &cpu_new)){
-
- /* try to set cpu frequency failed */
-
- #ifdef CONFIG_CPU_FREQ_DVFS
- if(corevdd && (new_vdd > last_vdd)) {
- CPUFREQ_INF("set core vdd to %d\n", last_vdd);
- if(regulator_set_voltage(corevdd, last_vdd*1000, last_vdd*1000)){
- CPUFREQ_INF("try to set voltage failed!\n");
- last_vdd = new_vdd;
- }
- }
- #endif
-
- /* notify everyone that clock transition finish */
- if (policy) {
- freqs.cpu = 0;
- freqs.old = freqs.new;
- freqs.new = cpu_cur.pll / 1000;
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- }
-
- return -EINVAL;
- }
-
- #ifdef CONFIG_CPU_FREQ_DVFS
- if(corevdd && (new_vdd < last_vdd)) {
- CPUFREQ_INF("set core vdd to %d\n", new_vdd);
- if(regulator_set_voltage(corevdd, new_vdd*1000, new_vdd*1000)) {
- CPUFREQ_INF("try to set voltage failed!\n");
- new_vdd = last_vdd;
- }
- }
- last_vdd = new_vdd;
- #endif
-
- /* update our current settings */
- cpu_cur = cpu_new;
-
- /* notify everyone we've done this */
- if (policy) {
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- }
-
- CPUFREQ_DBG("%s: finished\n", __func__);
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_target
-*
-*Description: adjust the frequency that cpu is currently running;
-*
-*Arguments : policy cpu frequency policy;
-* freq target frequency to be set, based on khz;
-* relation method for selecting the target requency;
-*
-*Return : result, return 0 if set target frequency successed,
-* else, return -EINVAL;
-*
-*Notes : this function is called by the cpufreq core;
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_target(struct cpufreq_policy *policy, __u32 freq, __u32 relation)
-{
- int ret;
- unsigned int index;
- struct sun4i_cpu_freq_t freq_cfg;
- static struct cpufreq_frequency_table *sun4i_freq_tbl = NULL;
-
- if (unlikely(sun4i_freq_tbl == NULL))
- sun4i_freq_tbl = sunxi_cpufreq_table();
-
- /* avoid repeated calls which cause a needless amout of duplicated
- * logging output (and CPU time as the calculation process is
- * done) */
- if (freq == last_target) {
- return 0;
- }
-
- /* try to look for a valid frequency value from cpu frequency table */
- if (cpufreq_frequency_table_target(policy, sun4i_freq_tbl, freq, relation, &index)) {
- CPUFREQ_ERR("%s: try to look for a valid frequency for %u failed!\n", __func__, freq);
- return -EINVAL;
- }
-
- if (sun4i_freq_tbl[index].frequency == last_target) {
- /* frequency is same as the value last set, need not adjust */
- return 0;
- }
- freq = sun4i_freq_tbl[index].frequency;
-
- /* update the target frequency */
- freq_cfg.pll = sun4i_freq_tbl[index].frequency * 1000;
- freq_cfg.div.i = sun4i_freq_tbl[index].index;
- CPUFREQ_DBG("%s: target frequency find is %u, entry %u\n", __func__, freq_cfg.pll, index);
-
- /* try to set target frequency */
- ret = sun4i_cpufreq_settarget(policy, &freq_cfg);
- if(!ret) {
- last_target = freq;
- }
-
- return ret;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_get
-*
-*Description: get the frequency that cpu currently is running;
-*
-*Arguments : cpu cpu number;
-*
-*Return : cpu frequency, based on khz;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static unsigned int sun4i_cpufreq_get(unsigned int cpu)
-{
- return clk_get_rate(clk_cpu) / 1000;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_init
-*
-*Description: cpu frequency initialise a policy;
-*
-*Arguments : policy cpu frequency policy;
-*
-*Return : result, return 0 if init ok, else, return -EINVAL;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_init(struct cpufreq_policy *policy)
-{
- CPUFREQ_DBG(KERN_INFO "%s: initialising policy %p\n", __func__, policy);
-
- if (policy->cpu != 0)
- return -EINVAL;
-
- policy->cur = sun4i_cpufreq_get(0);
- policy->min = policy->cpuinfo.min_freq = SUN4I_CPUFREQ_MIN / 1000;
- policy->max = policy->cpuinfo.max_freq = SUN4I_CPUFREQ_MAX / 1000;
- policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
-
- /* feed the latency information from the cpu driver */
- policy->cpuinfo.transition_latency = SUN4I_FREQTRANS_LATENCY;
-
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_getcur
-*
-*Description: get current cpu frequency configuration;
-*
-*Arguments : cfg cpu frequency cofniguration;
-*
-*Return : result;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_getcur(struct sun4i_cpu_freq_t *cfg)
-{
- unsigned int freq, freq0;
-
- if(!cfg) {
- return -EINVAL;
- }
-
- cfg->pll = clk_get_rate(clk_pll);
- freq = clk_get_rate(clk_cpu);
- cfg->div.s.cpu_div = cfg->pll / freq;
- freq0 = clk_get_rate(clk_axi);
- cfg->div.s.axi_div = freq / freq0;
- freq = clk_get_rate(clk_ahb);
- cfg->div.s.ahb_div = freq0 / freq;
- freq0 = clk_get_rate(clk_apb);
- cfg->div.s.apb_div = freq /freq0;
-
- return 0;
-}
-
-
-
-#ifdef CONFIG_PM
-
-/* variable for backup cpu frequency configuration */
-static struct sun4i_cpu_freq_t suspend_freq;
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_suspend
-*
-*Description: back up cpu frequency configuration for suspend;
-*
-*Arguments : policy cpu frequency policy;
-* pmsg power management message;
-*
-*Return : return 0,
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_suspend(struct cpufreq_policy *policy)
-{
- struct sun4i_cpu_freq_t suspend;
-
- CPUFREQ_DBG("%s, set cpu frequency to 60Mhz to prepare enter standby\n", __func__);
-
- sun4i_cpufreq_getcur(&suspend_freq);
-
- /* set cpu frequency to 60M hz for standby */
- suspend.pll = 60000000;
- suspend.div.s.cpu_div = 1;
- suspend.div.s.axi_div = 1;
- suspend.div.s.ahb_div = 1;
- suspend.div.s.apb_div = 2;
- __set_cpufreq_target(&suspend_freq, &suspend);
-
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_resume
-*
-*Description: cpu frequency configuration resume;
-*
-*Arguments : policy cpu frequency policy;
-*
-*Return : result;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_resume(struct cpufreq_policy *policy)
-{
- struct sun4i_cpu_freq_t suspend;
-
- /* invalidate last_target setting */
- last_target = ~0;
-
- CPUFREQ_DBG("%s: resuming with policy %p\n", __func__, policy);
- sun4i_cpufreq_getcur(&suspend);
-
- /* restore cpu frequency configuration */
- __set_cpufreq_target(&suspend, &suspend_freq);
-
- CPUFREQ_DBG("%s: resuming done\n", __func__);
- return 0;
-}
-
-
-#else /* #ifdef CONFIG_PM */
-
-#define sun4i_cpufreq_suspend NULL
-#define sun4i_cpufreq_resume NULL
-
-#endif /* #ifdef CONFIG_PM */
-
-static struct freq_attr *sun4i_cpufreq_attr[] = {
- &cpufreq_freq_attr_scaling_available_freqs,
- NULL,
-};
-
-static struct cpufreq_driver sun4i_cpufreq_driver = {
- .flags = CPUFREQ_STICKY,
- .verify = sun4i_cpufreq_verify,
- .target = sun4i_cpufreq_target,
- .get = sun4i_cpufreq_get,
- .init = sun4i_cpufreq_init,
- .suspend = sun4i_cpufreq_suspend,
- .resume = sun4i_cpufreq_resume,
- .name = "sun4i",
- .attr = sun4i_cpufreq_attr,
-};
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_initclks
-*
-*Description: init cpu frequency clock resource;
-*
-*Arguments : none
-*
-*Return : result;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static __init int sun4i_cpufreq_initclks(void)
-{
- clk_pll = clk_get(NULL, "core_pll");
- clk_cpu = clk_get(NULL, "cpu");
- clk_axi = clk_get(NULL, "axi");
- clk_ahb = clk_get(NULL, "ahb");
- clk_apb = clk_get(NULL, "apb");
-
- if (IS_ERR(clk_pll) || IS_ERR(clk_cpu) || IS_ERR(clk_axi) ||
- IS_ERR(clk_ahb) || IS_ERR(clk_apb)) {
- CPUFREQ_INF(KERN_ERR "%s: could not get clock(s)\n", __func__);
- return -ENOENT;
- }
-
- CPUFREQ_INF("%s: clocks pll=%lu,cpu=%lu,axi=%lu,ahp=%lu,apb=%lu\n", __func__,
- clk_get_rate(clk_pll), clk_get_rate(clk_cpu), clk_get_rate(clk_axi),
- clk_get_rate(clk_ahb), clk_get_rate(clk_apb));
-
- #ifdef CONFIG_CPU_FREQ_DVFS
- corevdd = regulator_get(NULL, "axp20_core");
- if (IS_ERR(corevdd)) {
- CPUFREQ_INF("try to get regulator failed, core vdd will not changed!\n");
- corevdd = NULL;
- } else {
- CPUFREQ_INF("try to get regulator(0x%x) successed.\n", (__u32)corevdd);
- last_vdd = regulator_get_voltage(corevdd) / 1000;
- }
- #endif
-
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_initcall
-*
-*Description: cpu frequency driver initcall
-*
-*Arguments : none
-*
-*Return : result
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int __init sun4i_cpufreq_initcall(void)
-{
- int ret = 0;
- struct cpufreq_frequency_table *sun4i_freq_tbl = sunxi_cpufreq_table();
-
- /* initialise some clock resource */
- ret = sun4i_cpufreq_initclks();
- if(ret) {
- return ret;
- }
-
- /* initialise current frequency configuration */
- sun4i_cpufreq_getcur(&cpu_cur);
- sun4i_cpufreq_show("cur", &cpu_cur);
-
- /* register cpu frequency driver */
- ret = cpufreq_register_driver(&sun4i_cpufreq_driver);
- /* register cpu frequency table to cpufreq core */
- cpufreq_frequency_table_get_attr(sun4i_freq_tbl, 0);
- /* update policy for boot cpu */
- cpufreq_update_policy(0);
-
- return ret;
-}
-late_initcall(sun4i_cpufreq_initcall);
diff --git a/arch/arm/mach-sun4i/cpu-freq/cpu-freq.h b/arch/arm/mach-sun4i/cpu-freq/cpu-freq.h
deleted file mode 100644
index 77098b1..0000000
--- a/arch/arm/mach-sun4i/cpu-freq/cpu-freq.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * arch/arm/mach-sun4i/cpu-freq/cpu-freq.h
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Kevin Zhang <ke...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#ifndef __SUN4I_CPU_FREQ_H__
-#define __SUN4I_CPU_FREQ_H__
-
-#include <linux/types.h>
-#include <linux/cpufreq.h>
-
-#undef CPUFREQ_DBG
-#undef CPUFREQ_ERR
-#if (0)
- #define CPUFREQ_DBG(format,args...) printk("[cpu_freq] DBG:"format,##args)
- #define CPUFREQ_ERR(format,args...) printk("[cpu_freq] ERR:"format,##args)
- #define CPUFREQ_INF(format,args...) printk("[cpu_freq] INF:"format,##args)
-#else
- #define CPUFREQ_DBG(format,args...) do{}while(0)
- #define CPUFREQ_ERR(format,args...) do{}while(0)
- #define CPUFREQ_INF(format,args...) do{}while(0)
-#endif
-
-
-#define SUN4I_CPUFREQ_MAX (1008000000) /* config the maximum frequency of sun4i core */
-#define SUN4I_CPUFREQ_MIN (60000000) /* config the minimum frequency of sun4i core */
-#define SUN4I_FREQTRANS_LATENCY (2000000) /* config the transition latency, based on ns */
-
-struct sun4i_clk_div_t {
- __u32 cpu_div:4; /* division of cpu clock, divide core_pll */
- __u32 axi_div:4; /* division of axi clock, divide cpu clock*/
- __u32 ahb_div:4; /* division of ahb clock, divide axi clock*/
- __u32 apb_div:4; /* division of apb clock, divide ahb clock*/
- __u32 reserved:16;
-};
-
-struct sun4i_cpu_freq_t {
- __u32 pll; /* core pll frequency value */
- union {
- struct sun4i_clk_div_t s; /* division configuration */
- __u32 i;
- } div;
-};
-
-#define SUN4I_CLK_DIV(cpu_div, axi_div, ahb_div, apb_div) \
- ((cpu_div<<0)|(axi_div<<4)|(ahb_div<<8)|(apb_div<<12))
-
-#ifdef CONFIG_CPU_FREQ_DVFS
-struct cpufreq_dvfs {
- unsigned int freq; /* cpu frequency */
- unsigned int volt; /* voltage for the frequency */
-};
-#endif
-
-struct cpufreq_div_order {
- __u32 div;
- __u32 pll;
-};
-
-/* Table fetchers */
-struct cpufreq_frequency_table *sunxi_cpufreq_table(void);
-struct cpufreq_div_order *sunxi_div_order_table(int *length);
-#ifdef CONFIG_CPU_FREQ_DVFS
-struct cpufreq_dvfs *sunxi_dvfs_table(void);
-#endif
-
-#endif /* #ifndef __SUN4I_CPU_FREQ_H__ */
-
-
diff --git a/arch/arm/mach-sun5i/Makefile b/arch/arm/mach-sun5i/Makefile
index b1d6960..edfae11 100644
--- a/arch/arm/mach-sun5i/Makefile
+++ b/arch/arm/mach-sun5i/Makefile
@@ -2,4 +2,3 @@ obj-y += clock/
obj-y += dma/
obj-y += devices.o
obj-$(CONFIG_PM) += pm/
-obj-$(CONFIG_CPU_FREQ) += cpu-freq/
diff --git a/arch/arm/mach-sun5i/cpu-freq/Makefile b/arch/arm/mach-sun5i/cpu-freq/Makefile
deleted file mode 100644
index ddd3bc9..0000000
--- a/arch/arm/mach-sun5i/cpu-freq/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-obj-$(CONFIG_CPU_FREQ) += cpu-freq.o
-obj-$(CONFIG_CPU_FREQ_TABLE) += cpu-freq-table.o
diff --git a/arch/arm/mach-sun5i/cpu-freq/cpu-freq-table.c b/arch/arm/mach-sun5i/cpu-freq/cpu-freq-table.c
deleted file mode 100644
index f63638b..0000000
--- a/arch/arm/mach-sun5i/cpu-freq/cpu-freq-table.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * arch/arm/mach-sun5i/cpu-freq/cpu-freq-table.c
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Kevin Zhang <ke...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <linux/types.h>
-#include <linux/clk.h>
-#include <linux/cpufreq.h>
-#include "cpu-freq.h"
-
-struct cpufreq_frequency_table sun4i_freq_tbl[] = {
-
- { .frequency = 30000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 48000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 60000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 72000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 84000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 96000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 108000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 120000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 132000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 144000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 156000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 168000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 180000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 192000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
- { .frequency = 204000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 216000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 240000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 264000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 288000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 300000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 336000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 360000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 384000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 408000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
- { .frequency = 432000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 480000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 528000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 576000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 600000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 648000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 672000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 696000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 720000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 744000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 768000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 816000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
- { .frequency = 864000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 912000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 960000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 1008000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- #if(1)
- { .frequency = 1056000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 1104000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 1152000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 1200000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
- { .frequency = 1248000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- { .frequency = 1296000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- { .frequency = 1344000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- { .frequency = 1392000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- { .frequency = 1440000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- { .frequency = 1488000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
- #endif
-
- /* table end */
- { .frequency = CPUFREQ_TABLE_END, .index = 0, },
-};
-
diff --git a/arch/arm/mach-sun5i/cpu-freq/cpu-freq.c b/arch/arm/mach-sun5i/cpu-freq/cpu-freq.c
deleted file mode 100644
index 7da2e90..0000000
--- a/arch/arm/mach-sun5i/cpu-freq/cpu-freq.c
+++ /dev/null
@@ -1,779 +0,0 @@
-/*
- * arch/arm/mach-sun5i/cpu-freq/cpu-freq.c
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Kevin Zhang <ke...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/cpufreq.h>
-#include <linux/cpu.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/regulator/consumer.h>
-#include "cpu-freq.h"
-
-
-static struct sun4i_cpu_freq_t cpu_cur; /* current cpu frequency configuration */
-static unsigned int last_target = ~0; /* backup last target frequency */
-
-static struct clk *clk_pll; /* pll clock handler */
-static struct clk *clk_cpu; /* cpu clock handler */
-static struct clk *clk_axi; /* axi clock handler */
-static struct clk *clk_ahb; /* ahb clock handler */
-static struct clk *clk_apb; /* apb clock handler */
-
-
-#ifdef CONFIG_CPU_FREQ_DVFS
-struct cpufreq_dvfs {
- unsigned int freq; /* cpu frequency */
- unsigned int volt; /* voltage for the frequency */
-};
-static struct cpufreq_dvfs dvfs_table[] = {
- {.freq = 1056000000, .volt = 1500}, /* core vdd is 1.50v if cpu frequency is (1008Mhz, xxxxMhz] */
- {.freq = 1008000000, .volt = 1400}, /* core vdd is 1.40v if cpu frequency is (960Mhz, 1008Mhz] */
- {.freq = 960000000, .volt = 1400}, /* core vdd is 1.40v if cpu frequency is (912Mhz, 960Mhz] */
- {.freq = 912000000, .volt = 1350}, /* core vdd is 1.35v if cpu frequency is (864Mhz, 912Mhz] */
- {.freq = 864000000, .volt = 1300}, /* core vdd is 1.30v if cpu frequency is (624Mhz, 864Mhz] */
- {.freq = 624000000, .volt = 1250}, /* core vdd is 1.25v if cpu frequency is (432Mhz, 624Mhz] */
- {.freq = 432000000, .volt = 1250}, /* core vdd is 1.25v if cpu frequency is (0, 432Mhz] */
- {.freq = 0, .volt = 1000}, /* end of cpu dvfs table */
-};
-static struct regulator *corevdd;
-static unsigned int last_vdd = 1400; /* backup last target voltage, default is 1.4v */
-#endif
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_verify
-*
-*Description: check if the cpu frequency policy is valid;
-*
-*Arguments : policy cpu frequency policy;
-*
-*Return : result, return if verify ok, else return -EINVAL;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_verify(struct cpufreq_policy *policy)
-{
- if (policy->cpu != 0)
- return -EINVAL;
-
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_show
-*
-*Description: show cpu frequency information;
-*
-*Arguments : pfx name;
-*
-*
-*Return :
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static void sun4i_cpufreq_show(const char *pfx, struct sun4i_cpu_freq_t *cfg)
-{
- CPUFREQ_DBG("%s: pll=%u, cpudiv=%u, axidiv=%u, ahbdiv=%u, apb=%u\n",
- pfx, cfg->pll, cfg->div.cpu_div, cfg->div.axi_div, cfg->div.ahb_div, cfg->div.apb_div);
-}
-
-
-#ifdef CONFIG_CPU_FREQ_DVFS
-/*
-*********************************************************************************************************
-* __get_vdd_value
-*
-*Description: get vdd with cpu frequency.
-*
-*Arguments : freq cpu frequency;
-*
-*Return : vdd value;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static inline unsigned int __get_vdd_value(unsigned int freq)
-{
- struct cpufreq_dvfs *dvfs_inf = &dvfs_table[0];
- while((dvfs_inf+1)->freq >= freq) dvfs_inf++;
-
- return dvfs_inf->volt;
-}
-#endif
-
-
-/*
-*********************************************************************************************************
-* __set_cpufreq_hw
-*
-*Description: set cpu frequency configuration to hardware.
-*
-*Arguments : freq frequency configuration;
-*
-*Return : result
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static inline int __set_cpufreq_hw(struct sun4i_cpu_freq_t *freq)
-{
- int ret;
- unsigned int frequency;
-
- /* try to adjust pll frequency */
- ret = clk_set_rate(clk_pll, freq->pll);
- /* try to adjust cpu frequency */
- frequency = freq->pll / freq->div.cpu_div;
- ret |= clk_set_rate(clk_cpu, frequency);
- /* try to adjuxt axi frequency */
- frequency /= freq->div.axi_div;
- ret |= clk_set_rate(clk_axi, frequency);
- /* try to adjust ahb frequency */
- frequency /= freq->div.ahb_div;
- ret |= clk_set_rate(clk_ahb, frequency);
- /* try to adjust apb frequency */
- frequency /= freq->div.apb_div;
- ret |= clk_set_rate(clk_apb, frequency);
-
- return ret;
-}
-
-
-/*
-*********************************************************************************************************
-* __set_cpufreq_target
-*
-*Description: set target frequency, the frequency limitation of axi is 450Mhz, the frequency
-* limitation of ahb is 250Mhz, and the limitation of apb is 150Mhz. for usb connecting,
-* the frequency of ahb must not lower than 60Mhz.
-*
-*Arguments : old cpu/axi/ahb/apb frequency old configuration.
-* new cpu/axi/ahb/apb frequency new configuration.
-*
-*Return : result, 0 - set frequency successed, !0 - set frequency failed;
-*
-*Notes : we check two frequency point: 204Mhz, 408Mhz, 816Mhz and 1200Mhz.
-* if increase cpu frequency, the flow should be:
-* low(1:1:1:2) -> 204Mhz(1:1:1:2) -> 204Mhz(1:1:2:2) -> 408Mhz(1:1:2:2)
-* -> 408Mhz(1:2:2:2) -> 816Mhz(1:2:2:2) -> 816Mhz(1:3:2:2) -> 1200Mhz(1:3:2:2)
-* -> 1200Mhz(1:4:2:2) -> target(1:4:2:2) -> target(x:x:x:x)
-* if decrease cpu frequency, the flow should be:
-* high(x:x:x:x) -> target(1:4:2:2) -> 1200Mhz(1:4:2:2) -> 1200Mhz(1:3:2:2)
-* -> 816Mhz(1:3:2:2) -> 816Mhz(1:2:2:2) -> 408Mhz(1:2:2:2) -> 408Mhz(1:1:2:2)
-* -> 204Mhz(1:1:2:2) -> 204Mhz(1:1:1:2) -> target(1:1:1:2)
-*********************************************************************************************************
-*/
-static int __set_cpufreq_target(struct sun4i_cpu_freq_t *old, struct sun4i_cpu_freq_t *new)
-{
- int ret = 0;
- struct sun4i_cpu_freq_t old_freq, new_freq;
-
- if(!old || !new) {
- return -EINVAL;
- }
-
- old_freq = *old;
- new_freq = *new;
-
- CPUFREQ_INF("cpu: %dMhz->%dMhz\n", old_freq.pll/1000000, new_freq.pll/1000000);
-
- if(new_freq.pll > old_freq.pll) {
- if((old_freq.pll <= 204000000) && (new_freq.pll >= 204000000)) {
- /* set to 204Mhz (1:1:1:2) */
- old_freq.pll = 204000000;
- old_freq.div.cpu_div = 1;
- old_freq.div.axi_div = 1;
- old_freq.div.ahb_div = 1;
- old_freq.div.apb_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- /* set to 204Mhz (1:1:2:2) */
- old_freq.div.ahb_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- }
- if((old_freq.pll <= 408000000) && (new_freq.pll >= 408000000)) {
- /* set to 408Mhz (1:1:2:2) */
- old_freq.pll = 408000000;
- old_freq.div.cpu_div = 1;
- old_freq.div.axi_div = 1;
- old_freq.div.ahb_div = 2;
- old_freq.div.apb_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- /* set to 408Mhz (1:2:2:2) */
- old_freq.div.axi_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- }
- if((old_freq.pll <= 816000000) && (new_freq.pll >= 816000000)) {
- /* set to 816Mhz (1:2:2:2) */
- old_freq.pll = 816000000;
- old_freq.div.cpu_div = 1;
- old_freq.div.axi_div = 2;
- old_freq.div.ahb_div = 2;
- old_freq.div.apb_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- /* set to 816Mhz (1:3:2:2) */
- old_freq.div.axi_div = 3;
- ret |= __set_cpufreq_hw(&old_freq);
- }
- if((old_freq.pll <= 1200000000) && (new_freq.pll >= 1200000000)) {
- /* set to 1200Mhz (1:3:2:2) */
- old_freq.pll = 1200000000;
- old_freq.div.cpu_div = 1;
- old_freq.div.axi_div = 3;
- old_freq.div.ahb_div = 2;
- old_freq.div.apb_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- /* set to 1200Mhz (1:4:2:2) */
- old_freq.div.axi_div = 4;
- ret |= __set_cpufreq_hw(&old_freq);
- }
-
- /* adjust to target frequency */
- ret |= __set_cpufreq_hw(&new_freq);
- }
- else if(new_freq.pll < old_freq.pll) {
- if((old_freq.pll > 1200000000) && (new_freq.pll <= 1200000000)) {
- /* set to 1200Mhz (1:3:2:2) */
- old_freq.pll = 1200000000;
- old_freq.div.cpu_div = 1;
- old_freq.div.axi_div = 3;
- old_freq.div.ahb_div = 2;
- old_freq.div.apb_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- }
- if((old_freq.pll > 816000000) && (new_freq.pll <= 816000000)) {
- /* set to 816Mhz (1:3:2:2) */
- old_freq.pll = 816000000;
- old_freq.div.cpu_div = 1;
- old_freq.div.axi_div = 3;
- old_freq.div.ahb_div = 2;
- old_freq.div.apb_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- /* set to 816Mhz (1:2:2:2) */
- old_freq.div.axi_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- }
- if((old_freq.pll > 408000000) && (new_freq.pll <= 408000000)) {
- /* set to 408Mhz (1:2:2:2) */
- old_freq.pll = 408000000;
- old_freq.div.cpu_div = 1;
- old_freq.div.axi_div = 2;
- old_freq.div.ahb_div = 2;
- old_freq.div.apb_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- /* set to 816Mhz (1:1:2:2) */
- old_freq.div.axi_div = 1;
- ret |= __set_cpufreq_hw(&old_freq);
- }
- if((old_freq.pll > 204000000) && (new_freq.pll <= 204000000)) {
- /* set to 204Mhz (1:1:2:2) */
- old_freq.pll = 204000000;
- old_freq.div.cpu_div = 1;
- old_freq.div.axi_div = 1;
- old_freq.div.ahb_div = 2;
- old_freq.div.apb_div = 2;
- ret |= __set_cpufreq_hw(&old_freq);
- /* set to 204Mhz (1:1:1:2) */
- old_freq.div.ahb_div = 1;
- ret |= __set_cpufreq_hw(&old_freq);
- }
-
- /* adjust to target frequency */
- ret |= __set_cpufreq_hw(&new_freq);
- }
-
- if(ret) {
- unsigned int frequency;
-
- CPUFREQ_ERR("try to set target frequency failed!\n");
-
- /* try to restore frequency configuration */
- frequency = clk_get_rate(clk_cpu);
- frequency /= 4;
- clk_set_rate(clk_axi, frequency);
- frequency /= 2;
- clk_set_rate(clk_ahb, frequency);
- frequency /= 2;
- clk_set_rate(clk_apb, frequency);
-
- clk_set_rate(clk_pll, old->pll);
- frequency = old->pll / old->div.cpu_div;
- clk_set_rate(clk_cpu, frequency);
- frequency /= old->div.axi_div;
- clk_set_rate(clk_axi, frequency);
- frequency /= old->div.ahb_div;
- clk_set_rate(clk_ahb, frequency);
- frequency /= old->div.apb_div;
- clk_set_rate(clk_apb, frequency);
-
- CPUFREQ_ERR(KERN_ERR "no compatible settings cpu freq for %d\n", new_freq.pll);
- return -1;
- }
-
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_settarget
-*
-*Description: adjust cpu frequency;
-*
-*Arguments : policy cpu frequency policy, to mark if need notify;
-* cpu_freq new cpu frequency configuration;
-*
-*Return : return 0 if set successed, otherwise, return -EINVAL
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_settarget(struct cpufreq_policy *policy, struct sun4i_cpu_freq_t *cpu_freq)
-{
- struct cpufreq_freqs freqs;
- struct sun4i_cpu_freq_t cpu_new;
-
- #ifdef CONFIG_CPU_FREQ_DVFS
- unsigned int new_vdd;
- #endif
-
- /* show current cpu frequency configuration, just for debug */
- sun4i_cpufreq_show("cur", &cpu_cur);
-
- /* get new cpu frequency configuration */
- cpu_new = *cpu_freq;
- sun4i_cpufreq_show("new", &cpu_new);
-
- /* notify that cpu clock will be adjust if needed */
- if (policy) {
- freqs.cpu = 0;
- freqs.old = cpu_cur.pll / 1000;
- freqs.new = cpu_new.pll / 1000;
- cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
- }
-
- #ifdef CONFIG_CPU_FREQ_DVFS
- /* get vdd value for new frequency */
- new_vdd = __get_vdd_value(cpu_new.pll);
-
- if(corevdd && (new_vdd > last_vdd)) {
- CPUFREQ_INF("set core vdd to %d\n", new_vdd);
- if(regulator_set_voltage(corevdd, new_vdd*1000, new_vdd*1000)) {
- CPUFREQ_INF("try to set voltage failed!\n");
-
- /* notify everyone that clock transition finish */
- if (policy) {
- freqs.cpu = 0;
- freqs.old = freqs.new;
- freqs.new = cpu_cur.pll / 1000;
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- }
- return -EINVAL;
- }
- }
- #endif
-
- if(__set_cpufreq_target(&cpu_cur, &cpu_new)){
-
- /* try to set cpu frequency failed */
-
- #ifdef CONFIG_CPU_FREQ_DVFS
- if(corevdd && (new_vdd > last_vdd)) {
- CPUFREQ_INF("set core vdd to %d\n", last_vdd);
- if(regulator_set_voltage(corevdd, last_vdd*1000, last_vdd*1000)){
- CPUFREQ_INF("try to set voltage failed!\n");
- last_vdd = new_vdd;
- }
- }
- #endif
-
- /* notify everyone that clock transition finish */
- if (policy) {
- freqs.cpu = 0;
- freqs.old = freqs.new;
- freqs.new = cpu_cur.pll / 1000;
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- }
-
- return -EINVAL;
- }
-
- #ifdef CONFIG_CPU_FREQ_DVFS
- if(corevdd && (new_vdd < last_vdd)) {
- CPUFREQ_INF("set core vdd to %d\n", new_vdd);
- if(regulator_set_voltage(corevdd, new_vdd*1000, new_vdd*1000)) {
- CPUFREQ_INF("try to set voltage failed!\n");
- new_vdd = last_vdd;
- }
- }
- last_vdd = new_vdd;
- #endif
-
- /* update our current settings */
- cpu_cur = cpu_new;
-
- /* notify everyone we've done this */
- if (policy) {
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- }
-
- CPUFREQ_DBG("%s: finished\n", __func__);
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_target
-*
-*Description: adjust the frequency that cpu is currently running;
-*
-*Arguments : policy cpu frequency policy;
-* freq target frequency to be set, based on khz;
-* relation method for selecting the target requency;
-*
-*Return : result, return 0 if set target frequency successed,
-* else, return -EINVAL;
-*
-*Notes : this function is called by the cpufreq core;
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_target(struct cpufreq_policy *policy, __u32 freq, __u32 relation)
-{
- int ret;
- unsigned int index;
- struct sun4i_cpu_freq_t freq_cfg;
-
- /* avoid repeated calls which cause a needless amout of duplicated
- * logging output (and CPU time as the calculation process is
- * done) */
- if (freq == last_target) {
- return 0;
- }
-
- /* try to look for a valid frequency value from cpu frequency table */
- if (cpufreq_frequency_table_target(policy, sun4i_freq_tbl, freq, relation, &index)) {
- CPUFREQ_ERR("%s: try to look for a valid frequency for %u failed!\n", __func__, freq);
- return -EINVAL;
- }
-
- if (sun4i_freq_tbl[index].frequency == last_target) {
- /* frequency is same as the value last set, need not adjust */
- return 0;
- }
- freq = sun4i_freq_tbl[index].frequency;
-
- /* update the target frequency */
- freq_cfg.pll = sun4i_freq_tbl[index].frequency * 1000;
- freq_cfg.div = *(struct sun4i_clk_div_t *)&sun4i_freq_tbl[index].index;
- CPUFREQ_DBG("%s: target frequency find is %u, entry %u\n", __func__, freq_cfg.pll, index);
-
- /* try to set target frequency */
- ret = sun4i_cpufreq_settarget(policy, &freq_cfg);
- if(!ret) {
- last_target = freq;
- }
-
- return ret;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_get
-*
-*Description: get the frequency that cpu currently is running;
-*
-*Arguments : cpu cpu number;
-*
-*Return : cpu frequency, based on khz;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static unsigned int sun4i_cpufreq_get(unsigned int cpu)
-{
- return clk_get_rate(clk_cpu) / 1000;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_init
-*
-*Description: cpu frequency initialise a policy;
-*
-*Arguments : policy cpu frequency policy;
-*
-*Return : result, return 0 if init ok, else, return -EINVAL;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_init(struct cpufreq_policy *policy)
-{
- CPUFREQ_DBG(KERN_INFO "%s: initialising policy %p\n", __func__, policy);
-
- if (policy->cpu != 0)
- return -EINVAL;
-
- policy->cur = sun4i_cpufreq_get(0);
- policy->min = policy->cpuinfo.min_freq = SUN4I_CPUFREQ_MIN / 1000;
- policy->max = policy->cpuinfo.max_freq = SUN4I_CPUFREQ_MAX / 1000;
- policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
-
- /* feed the latency information from the cpu driver */
- policy->cpuinfo.transition_latency = SUN4I_FREQTRANS_LATENCY;
-
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_getcur
-*
-*Description: get current cpu frequency configuration;
-*
-*Arguments : cfg cpu frequency cofniguration;
-*
-*Return : result;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_getcur(struct sun4i_cpu_freq_t *cfg)
-{
- unsigned int freq, freq0;
-
- if(!cfg) {
- return -EINVAL;
- }
-
- cfg->pll = clk_get_rate(clk_pll);
- freq = clk_get_rate(clk_cpu);
- cfg->div.cpu_div = cfg->pll / freq;
- freq0 = clk_get_rate(clk_axi);
- cfg->div.axi_div = freq / freq0;
- freq = clk_get_rate(clk_ahb);
- cfg->div.ahb_div = freq0 / freq;
- freq0 = clk_get_rate(clk_apb);
- cfg->div.apb_div = freq /freq0;
-
- return 0;
-}
-
-
-
-#ifdef CONFIG_PM
-
-/* variable for backup cpu frequency configuration */
-static struct sun4i_cpu_freq_t suspend_freq;
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_suspend
-*
-*Description: back up cpu frequency configuration for suspend;
-*
-*Arguments : policy cpu frequency policy;
-* pmsg power management message;
-*
-*Return : return 0,
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_suspend(struct cpufreq_policy *policy)
-{
- struct sun4i_cpu_freq_t suspend;
-
- CPUFREQ_DBG("%s, set cpu frequency to 60Mhz to prepare enter standby\n", __func__);
-
- sun4i_cpufreq_getcur(&suspend_freq);
-
- /* set cpu frequency to 60M hz for standby */
- suspend.pll = 60000000;
- suspend.div.cpu_div = 1;
- suspend.div.axi_div = 1;
- suspend.div.ahb_div = 1;
- suspend.div.apb_div = 2;
- __set_cpufreq_target(&suspend_freq, &suspend);
-
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_resume
-*
-*Description: cpu frequency configuration resume;
-*
-*Arguments : policy cpu frequency policy;
-*
-*Return : result;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int sun4i_cpufreq_resume(struct cpufreq_policy *policy)
-{
- struct sun4i_cpu_freq_t suspend;
-
- /* invalidate last_target setting */
- last_target = ~0;
-
- CPUFREQ_DBG("%s: resuming with policy %p\n", __func__, policy);
- sun4i_cpufreq_getcur(&suspend);
-
- /* restore cpu frequency configuration */
- __set_cpufreq_target(&suspend, &suspend_freq);
-
- CPUFREQ_DBG("%s: resuming done\n", __func__);
- return 0;
-}
-
-
-#else /* #ifdef CONFIG_PM */
-
-#define sun4i_cpufreq_suspend NULL
-#define sun4i_cpufreq_resume NULL
-
-#endif /* #ifdef CONFIG_PM */
-
-
-static struct cpufreq_driver sun4i_cpufreq_driver = {
- .flags = CPUFREQ_STICKY,
- .verify = sun4i_cpufreq_verify,
- .target = sun4i_cpufreq_target,
- .get = sun4i_cpufreq_get,
- .init = sun4i_cpufreq_init,
- .suspend = sun4i_cpufreq_suspend,
- .resume = sun4i_cpufreq_resume,
- .name = "sun4i",
-};
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_initclks
-*
-*Description: init cpu frequency clock resource;
-*
-*Arguments : none
-*
-*Return : result;
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static __init int sun4i_cpufreq_initclks(void)
-{
- clk_pll = clk_get(NULL, "core_pll");
- clk_cpu = clk_get(NULL, "cpu");
- clk_axi = clk_get(NULL, "axi");
- clk_ahb = clk_get(NULL, "ahb");
- clk_apb = clk_get(NULL, "apb");
-
- if (IS_ERR(clk_pll) || IS_ERR(clk_cpu) || IS_ERR(clk_axi) ||
- IS_ERR(clk_ahb) || IS_ERR(clk_apb)) {
- CPUFREQ_INF(KERN_ERR "%s: could not get clock(s)\n", __func__);
- return -ENOENT;
- }
-
- CPUFREQ_INF("%s: clocks pll=%lu,cpu=%lu,axi=%lu,ahp=%lu,apb=%lu\n", __func__,
- clk_get_rate(clk_pll), clk_get_rate(clk_cpu), clk_get_rate(clk_axi),
- clk_get_rate(clk_ahb), clk_get_rate(clk_apb));
-
- #ifdef CONFIG_CPU_FREQ_DVFS
- corevdd = regulator_get(NULL, "axp20_core");
- if (IS_ERR(corevdd)) {
- CPUFREQ_INF("try to get regulator failed, core vdd will not changed!\n");
- corevdd = NULL;
- } else {
- CPUFREQ_INF("try to get regulator(0x%x) successed.\n", (__u32)corevdd);
- last_vdd = regulator_get_voltage(corevdd) / 1000;
- }
- #endif
-
- return 0;
-}
-
-
-/*
-*********************************************************************************************************
-* sun4i_cpufreq_initcall
-*
-*Description: cpu frequency driver initcall
-*
-*Arguments : none
-*
-*Return : result
-*
-*Notes :
-*
-*********************************************************************************************************
-*/
-static int __init sun4i_cpufreq_initcall(void)
-{
- int ret = 0;
-
- /* initialise some clock resource */
- ret = sun4i_cpufreq_initclks();
- if(ret) {
- return ret;
- }
-
- /* initialise current frequency configuration */
- sun4i_cpufreq_getcur(&cpu_cur);
- sun4i_cpufreq_show("cur", &cpu_cur);
-
- /* register cpu frequency driver */
- ret = cpufreq_register_driver(&sun4i_cpufreq_driver);
- /* register cpu frequency table to cpufreq core */
- cpufreq_frequency_table_get_attr(sun4i_freq_tbl, 0);
- /* update policy for boot cpu */
- cpufreq_update_policy(0);
-
- return ret;
-}
-late_initcall(sun4i_cpufreq_initcall);
-
diff --git a/arch/arm/mach-sun5i/cpu-freq/cpu-freq.h b/arch/arm/mach-sun5i/cpu-freq/cpu-freq.h
deleted file mode 100644
index f2cc64c..0000000
--- a/arch/arm/mach-sun5i/cpu-freq/cpu-freq.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * arch/arm/mach-sun5i/cpu-freq/cpu-freq.h
- *
- * (C) Copyright 2007-2012
- * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
- * Kevin Zhang <ke...@allwinnertech.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#ifndef __SUN4I_CPU_FREQ_H__
-#define __SUN4I_CPU_FREQ_H__
-
-#include <linux/types.h>
-#include <linux/cpufreq.h>
-
-#undef CPUFREQ_DBG
-#undef CPUFREQ_ERR
-#if (0)
- #define CPUFREQ_DBG(format,args...) printk("[cpu_freq] DBG:"format,##args)
- #define CPUFREQ_ERR(format,args...) printk("[cpu_freq] ERR:"format,##args)
- #define CPUFREQ_INF(format,args...) printk("[cpu_freq] INF:"format,##args)
-#else
- #define CPUFREQ_DBG(format,args...) do{}while(0)
- #define CPUFREQ_ERR(format,args...) do{}while(0)
- #define CPUFREQ_INF(format,args...) do{}while(0)
-#endif
-
-
-#define SUN4I_CPUFREQ_MAX (1008000000) /* config the maximum frequency of sun4i core */
-#define SUN4I_CPUFREQ_MIN (60000000) /* config the minimum frequency of sun4i core */
-#define SUN4I_FREQTRANS_LATENCY (2000000) /* config the transition latency, based on ns */
-
-struct sun4i_clk_div_t {
- __u32 cpu_div:4; /* division of cpu clock, divide core_pll */
- __u32 axi_div:4; /* division of axi clock, divide cpu clock*/
- __u32 ahb_div:4; /* division of ahb clock, divide axi clock*/
- __u32 apb_div:4; /* division of apb clock, divide ahb clock*/
- __u32 reserved:16;
-};
-
-
-struct sun4i_cpu_freq_t {
- __u32 pll; /* core pll frequency value */
- struct sun4i_clk_div_t div; /* division configuration */
-};
-
-
-#define SUN4I_CLK_DIV(cpu_div, axi_div, ahb_div, apb_div) \
- ((cpu_div<<0)|(axi_div<<4)|(ahb_div<<8)|(apb_div<<12))
-
-
-
-extern struct cpufreq_frequency_table sun4i_freq_tbl[];
-
-#endif /* #ifndef __SUN4I_CPU_FREQ_H__ */
-
-
diff --git a/arch/arm/plat-sunxi/Makefile b/arch/arm/plat-sunxi/Makefile
index 936a3de..026705c 100644
--- a/arch/arm/plat-sunxi/Makefile
+++ b/arch/arm/plat-sunxi/Makefile
@@ -2,3 +2,4 @@ obj-y += sys_config.o
obj-y += core.o
obj-y += script.o
obj-y += clocksrc.o
+obj-$(CONFIG_CPU_FREQ) += cpu-freq/
diff --git a/arch/arm/plat-sunxi/cpu-freq/Makefile b/arch/arm/plat-sunxi/cpu-freq/Makefile
new file mode 100644
index 0000000..dc5d307
--- /dev/null
+++ b/arch/arm/plat-sunxi/cpu-freq/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CPU_FREQ) += cpu-freq.o cpu-freq-table.o
+
diff --git a/arch/arm/plat-sunxi/cpu-freq/cpu-freq-table.c b/arch/arm/plat-sunxi/cpu-freq/cpu-freq-table.c
new file mode 100644
index 0000000..04228bf
--- /dev/null
+++ b/arch/arm/plat-sunxi/cpu-freq/cpu-freq-table.c
@@ -0,0 +1,127 @@
+/*
+ * arch/arm/mach-sun4i/cpu-freq/cpu-freq-table.c
+ *
+ * (C) Copyright 2007-2012
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Kevin Zhang <ke...@allwinnertech.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include "cpu-freq.h"
+
+static struct cpufreq_frequency_table sun4i_freq_tbl[] = {
+
+ { .frequency = 30000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 48000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 60000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 72000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 84000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 96000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 108000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 120000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 132000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 144000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 156000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 168000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 180000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 192000, .index = SUN4I_CLK_DIV(1, 1, 1, 2), },
+ { .frequency = 204000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
+ { .frequency = 216000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
+ { .frequency = 240000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
+ { .frequency = 264000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
+ { .frequency = 288000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
+ { .frequency = 300000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
+ { .frequency = 336000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
+ { .frequency = 360000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
+ { .frequency = 384000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
+ { .frequency = 408000, .index = SUN4I_CLK_DIV(1, 1, 2, 2), },
+ { .frequency = 432000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 480000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 528000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 576000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 600000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 648000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 672000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 696000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 720000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 744000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 768000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 816000, .index = SUN4I_CLK_DIV(1, 2, 2, 2), },
+ { .frequency = 864000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
+ { .frequency = 912000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
+ { .frequency = 960000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
+ { .frequency = 1008000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
+ #if(1)
+ { .frequency = 1056000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
+ { .frequency = 1104000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
+ { .frequency = 1152000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
+ { .frequency = 1200000, .index = SUN4I_CLK_DIV(1, 3, 2, 2), },
+ { .frequency = 1248000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
+ { .frequency = 1296000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
+ { .frequency = 1344000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
+ { .frequency = 1392000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
+ { .frequency = 1440000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
+ { .frequency = 1488000, .index = SUN4I_CLK_DIV(1, 4, 2, 2), },
+ #endif
+
+ /* table end */
+ { .frequency = CPUFREQ_TABLE_END, .index = 0, },
+};
+
+/* div, pll (Hz) table */
+static struct cpufreq_div_order sun4i_div_order_tbl[] = {
+ { .div = SUN4I_CLK_DIV(1, 1, 1, 2), .pll = 204000000, },
+ { .div = SUN4I_CLK_DIV(1, 1, 2, 2), .pll = 408000000, },
+ { .div = SUN4I_CLK_DIV(1, 2, 2, 2), .pll = 816000000, },
+ { .div = SUN4I_CLK_DIV(1, 3, 2, 2), .pll = 1200000000, },
+ { .div = SUN4I_CLK_DIV(1, 4, 2, 2), .pll = 1248000000, },
+};
+
+#ifdef CONFIG_CPU_FREQ_DVFS
+static struct cpufreq_dvfs sun4i_dvfs_table[] = {
+ {.freq = 1056000000, .volt = 1500}, /* core vdd is 1.50v if cpu frequency is (1008Mhz, xxxxMhz] */
+ {.freq = 1008000000, .volt = 1400}, /* core vdd is 1.40v if cpu frequency is (960Mhz, 1008Mhz] */
+ {.freq = 960000000, .volt = 1400}, /* core vdd is 1.40v if cpu frequency is (912Mhz, 960Mhz] */
+ {.freq = 912000000, .volt = 1350}, /* core vdd is 1.35v if cpu frequency is (864Mhz, 912Mhz] */
+ {.freq = 864000000, .volt = 1300}, /* core vdd is 1.30v if cpu frequency is (624Mhz, 864Mhz] */
+ {.freq = 624000000, .volt = 1250}, /* core vdd is 1.25v if cpu frequency is (432Mhz, 624Mhz] */
+ {.freq = 432000000, .volt = 1250}, /* core vdd is 1.25v if cpu frequency is (0, 432Mhz] */
+ {.freq = 0, .volt = 1000}, /* end of cpu dvfs table */
+};
+#endif
+
+struct cpufreq_frequency_table * sunxi_cpufreq_table(void) {
+ /* TODO: improve to handle A13 and others */
+ return sun4i_freq_tbl;
+}
+
+struct cpufreq_div_order * sunxi_div_order_table(int *length) {
+ /* TODO: improve to handle A13 and others */
+ *length = ARRAY_SIZE(sun4i_div_order_tbl);
+ return sun4i_div_order_tbl;
+}
+
+#ifdef CONFIG_CPU_FREQ_DVFS
+struct cpufreq_dvfs * sunxi_dvfs_table(void) {
+ /* TODO: improve to handle A13 and others */
+ return sun4i_dvfs_table;
+}
+#endif
diff --git a/arch/arm/plat-sunxi/cpu-freq/cpu-freq.c b/arch/arm/plat-sunxi/cpu-freq/cpu-freq.c
new file mode 100644
index 0000000..099ea80
--- /dev/null
+++ b/arch/arm/plat-sunxi/cpu-freq/cpu-freq.c
@@ -0,0 +1,730 @@
+/*
+ * arch/arm/mach-sun4i/cpu-freq/cpu-freq.c
+ *
+ * (C) Copyright 2007-2012
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Kevin Zhang <ke...@allwinnertech.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/cpufreq.h>
+#include <linux/cpu.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include "cpu-freq.h"
+
+
+static struct sun4i_cpu_freq_t cpu_cur; /* current cpu frequency configuration */
+static unsigned int last_target = ~0; /* backup last target frequency */
+
+static struct clk *clk_pll; /* pll clock handler */
+static struct clk *clk_cpu; /* cpu clock handler */
+static struct clk *clk_axi; /* axi clock handler */
+static struct clk *clk_ahb; /* ahb clock handler */
+static struct clk *clk_apb; /* apb clock handler */
+
+
+#ifdef CONFIG_CPU_FREQ_DVFS
+static struct regulator *corevdd;
+static unsigned int last_vdd = 1400; /* backup last target voltage, default is 1.4v */
+#endif
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_verify
+*
+*Description: check if the cpu frequency policy is valid;
+*
+*Arguments : policy cpu frequency policy;
+*
+*Return : result, return if verify ok, else return -EINVAL;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static int sun4i_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_show
+*
+*Description: show cpu frequency information;
+*
+*Arguments : pfx name;
+*
+*
+*Return :
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static void sun4i_cpufreq_show(const char *pfx, struct sun4i_cpu_freq_t *cfg)
+{
+ CPUFREQ_DBG("%s: pll=%u, cpudiv=%u, axidiv=%u, ahbdiv=%u, apb=%u\n",
+ pfx, cfg->pll, cfg->div.s.cpu_div, cfg->div.s.axi_div, cfg->div.s.ahb_div, cfg->div.s.apb_div);
+}
+
+
+#ifdef CONFIG_CPU_FREQ_DVFS
+/*
+*********************************************************************************************************
+* __get_vdd_value
+*
+*Description: get vdd with cpu frequency.
+*
+*Arguments : freq cpu frequency;
+*
+*Return : vdd value;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static inline unsigned int __get_vdd_value(unsigned int freq)
+{
+ static struct cpufreq_dvfs *dvfs = NULL;
+ struct cpufreq_dvfs *dvfs_inf;
+
+ if (unlikely(dvfs == NULL))
+ dvfs = sunxi_dvfs_table();
+
+ dvfs_inf = dvfs;
+ while((dvfs_inf+1)->freq >= freq) dvfs_inf++;
+
+ return dvfs_inf->volt;
+}
+#endif
+
+
+/*
+*********************************************************************************************************
+* __set_cpufreq_hw
+*
+*Description: set cpu frequency configuration to hardware.
+*
+*Arguments : freq frequency configuration;
+*
+*Return : result
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static inline int __set_cpufreq_hw(struct sun4i_cpu_freq_t *freq)
+{
+ int ret;
+ unsigned int frequency;
+
+ /* try to adjust pll frequency */
+ ret = clk_set_rate(clk_pll, freq->pll);
+ /* try to adjust cpu frequency */
+ frequency = freq->pll / freq->div.s.cpu_div;
+ ret |= clk_set_rate(clk_cpu, frequency);
+ /* try to adjuxt axi frequency */
+ frequency /= freq->div.s.axi_div;
+ ret |= clk_set_rate(clk_axi, frequency);
+ /* try to adjust ahb frequency */
+ frequency /= freq->div.s.ahb_div;
+ ret |= clk_set_rate(clk_ahb, frequency);
+ /* try to adjust apb frequency */
+ frequency /= freq->div.s.apb_div;
+ ret |= clk_set_rate(clk_apb, frequency);
+
+ return ret;
+}
+
+
+/*
+*********************************************************************************************************
+* __set_cpufreq_target
+*
+*Description: set target frequency, the frequency limitation of axi is 450Mhz, the frequency
+* limitation of ahb is 250Mhz, and the limitation of apb is 150Mhz. for usb connecting,
+* the frequency of ahb must not lower than 60Mhz.
+*
+*Arguments : old cpu/axi/ahb/apb frequency old configuration.
+* new cpu/axi/ahb/apb frequency new configuration.
+*
+*Return : result, 0 - set frequency successed, !0 - set frequency failed;
+*
+*Notes : we check two frequency point: 204Mhz, 408Mhz, 816Mhz and 1200Mhz.
+* if increase cpu frequency, the flow should be:
+* low(1:1:1:2) -> 204Mhz(1:1:1:2) -> 204Mhz(1:1:2:2) -> 408Mhz(1:1:2:2)
+* -> 408Mhz(1:2:2:2) -> 816Mhz(1:2:2:2) -> 816Mhz(1:3:2:2) -> 1200Mhz(1:3:2:2)
+* -> 1200Mhz(1:4:2:2) -> target(1:4:2:2) -> target(x:x:x:x)
+* if decrease cpu frequency, the flow should be:
+* high(x:x:x:x) -> target(1:4:2:2) -> 1200Mhz(1:4:2:2) -> 1200Mhz(1:3:2:2)
+* -> 816Mhz(1:3:2:2) -> 816Mhz(1:2:2:2) -> 408Mhz(1:2:2:2) -> 408Mhz(1:1:2:2)
+* -> 204Mhz(1:1:2:2) -> 204Mhz(1:1:1:2) -> target(1:1:1:2)
+*********************************************************************************************************
+*/
+static int __set_cpufreq_target(struct sun4i_cpu_freq_t *old, struct sun4i_cpu_freq_t *new)
+{
+ int ret = 0;
+ int div_table_len;
+ unsigned int i = 0;
+ unsigned int j = 0;
+ struct sun4i_cpu_freq_t old_freq, new_freq;
+ static struct cpufreq_div_order *div_order_tbl = NULL;
+
+ if (unlikely(div_order_tbl == NULL))
+ div_order_tbl = sunxi_div_order_table(&div_table_len);
+
+ if (!old || !new)
+ return -EINVAL;
+
+ old_freq = *old;
+ new_freq = *new;
+
+ CPUFREQ_INF("cpu: %dMhz->%dMhz\n", old_freq.pll/1000000, new_freq.pll/1000000);
+
+ /* We're raising our clock */
+ if (new_freq.pll > old_freq.pll) {
+ /* We have a div table, the old and the new divs,
+ * let's change them in order */
+
+ /* Figure out old one */
+ while (i < div_table_len-1 &&
+ div_order_tbl[i].pll < old_freq.pll) i++;
+
+ /* Figure out new one */
+ j = i; /* it's either the same or bigger */
+ while (j < div_table_len-1 &&
+ div_order_tbl[j].pll < new_freq.pll) j++;
+
+ for (; i < div_table_len-1 && i < j; i++) {
+ old_freq.pll = div_order_tbl[i].pll;
+ old_freq.div.i = div_order_tbl[i].div;
+ ret |= __set_cpufreq_hw(&old_freq);
+
+ old_freq.div.i = div_order_tbl[i+1].div;
+ ret |= __set_cpufreq_hw(&old_freq);
+ }
+ /* We're lowering our clock */
+ } else if (new_freq.pll < old_freq.pll) {
+ /* We have a div table, the old and the new divs, let's change them in order */
+
+ /* Figure out new one*/
+ while (i < div_table_len-1 &&
+ div_order_tbl[i].pll < new_freq.pll) i++;
+
+ /* Figure out old one */
+ j = i; /* it's either the same or bigger */
+ while (j < div_table_len-1 &&
+ div_order_tbl[j].pll < old_freq.pll) j++;
+
+ for (; j > 0 && i < j; j--) {
+ old_freq.pll = div_order_tbl[j-1].pll;
+ old_freq.div.i = div_order_tbl[j].div;
+ ret |= __set_cpufreq_hw(&old_freq);
+
+ old_freq.div.i = div_order_tbl[j-1].div;
+ ret |= __set_cpufreq_hw(&old_freq);
+ }
+ }
+
+ /* adjust to target frequency */
+ ret |= __set_cpufreq_hw(&new_freq);
+
+ if (ret) {
+ unsigned int frequency;
+
+ CPUFREQ_ERR("try to set target frequency failed!\n");
+
+ /* try to restore frequency configuration */
+ frequency = clk_get_rate(clk_cpu);
+ frequency /= 4;
+ clk_set_rate(clk_axi, frequency);
+ frequency /= 2;
+ clk_set_rate(clk_ahb, frequency);
+ frequency /= 2;
+ clk_set_rate(clk_apb, frequency);
+
+ clk_set_rate(clk_pll, old->pll);
+ frequency = old->pll / old->div.s.cpu_div;
+ clk_set_rate(clk_cpu, frequency);
+ frequency /= old->div.s.axi_div;
+ clk_set_rate(clk_axi, frequency);
+ frequency /= old->div.s.ahb_div;
+ clk_set_rate(clk_ahb, frequency);
+ frequency /= old->div.s.apb_div;
+ clk_set_rate(clk_apb, frequency);
+
+ CPUFREQ_ERR(KERN_ERR "no compatible settings cpu freq for %d\n", new_freq.pll);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_settarget
+*
+*Description: adjust cpu frequency;
+*
+*Arguments : policy cpu frequency policy, to mark if need notify;
+* cpu_freq new cpu frequency configuration;
+*
+*Return : return 0 if set successed, otherwise, return -EINVAL
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static int sun4i_cpufreq_settarget(struct cpufreq_policy *policy, struct sun4i_cpu_freq_t *cpu_freq)
+{
+ struct cpufreq_freqs freqs;
+ struct sun4i_cpu_freq_t cpu_new;
+
+ #ifdef CONFIG_CPU_FREQ_DVFS
+ unsigned int new_vdd;
+ #endif
+
+ /* show current cpu frequency configuration, just for debug */
+ sun4i_cpufreq_show("cur", &cpu_cur);
+
+ /* get new cpu frequency configuration */
+ cpu_new = *cpu_freq;
+ sun4i_cpufreq_show("new", &cpu_new);
+
+ /* notify that cpu clock will be adjust if needed */
+ if (policy) {
+ freqs.cpu = 0;
+ freqs.old = cpu_cur.pll / 1000;
+ freqs.new = cpu_new.pll / 1000;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ }
+
+ #ifdef CONFIG_CPU_FREQ_DVFS
+ /* get vdd value for new frequency */
+ new_vdd = __get_vdd_value(cpu_new.pll);
+
+ if(corevdd && (new_vdd > last_vdd)) {
+ CPUFREQ_INF("set core vdd to %d\n", new_vdd);
+ if(regulator_set_voltage(corevdd, new_vdd*1000, new_vdd*1000)) {
+ CPUFREQ_INF("try to set voltage failed!\n");
+
+ /* notify everyone that clock transition finish */
+ if (policy) {
+ freqs.cpu = 0;
+ freqs.old = freqs.new;
+ freqs.new = cpu_cur.pll / 1000;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+ return -EINVAL;
+ }
+ }
+ #endif
+
+ if(__set_cpufreq_target(&cpu_cur, &cpu_new)){
+
+ /* try to set cpu frequency failed */
+
+ #ifdef CONFIG_CPU_FREQ_DVFS
+ if(corevdd && (new_vdd > last_vdd)) {
+ CPUFREQ_INF("set core vdd to %d\n", last_vdd);
+ if(regulator_set_voltage(corevdd, last_vdd*1000, last_vdd*1000)){
+ CPUFREQ_INF("try to set voltage failed!\n");
+ last_vdd = new_vdd;
+ }
+ }
+ #endif
+
+ /* notify everyone that clock transition finish */
+ if (policy) {
+ freqs.cpu = 0;
+ freqs.old = freqs.new;
+ freqs.new = cpu_cur.pll / 1000;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+
+ return -EINVAL;
+ }
+
+ #ifdef CONFIG_CPU_FREQ_DVFS
+ if(corevdd && (new_vdd < last_vdd)) {
+ CPUFREQ_INF("set core vdd to %d\n", new_vdd);
+ if(regulator_set_voltage(corevdd, new_vdd*1000, new_vdd*1000)) {
+ CPUFREQ_INF("try to set voltage failed!\n");
+ new_vdd = last_vdd;
+ }
+ }
+ last_vdd = new_vdd;
+ #endif
+
+ /* update our current settings */
+ cpu_cur = cpu_new;
+
+ /* notify everyone we've done this */
+ if (policy) {
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+
+ CPUFREQ_DBG("%s: finished\n", __func__);
+ return 0;
+}
+
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_target
+*
+*Description: adjust the frequency that cpu is currently running;
+*
+*Arguments : policy cpu frequency policy;
+* freq target frequency to be set, based on khz;
+* relation method for selecting the target requency;
+*
+*Return : result, return 0 if set target frequency successed,
+* else, return -EINVAL;
+*
+*Notes : this function is called by the cpufreq core;
+*
+*********************************************************************************************************
+*/
+static int sun4i_cpufreq_target(struct cpufreq_policy *policy, __u32 freq, __u32 relation)
+{
+ int ret;
+ unsigned int index;
+ struct sun4i_cpu_freq_t freq_cfg;
+ static struct cpufreq_frequency_table *sun4i_freq_tbl = NULL;
+
+ if (unlikely(sun4i_freq_tbl == NULL))
+ sun4i_freq_tbl = sunxi_cpufreq_table();
+
+ /* avoid repeated calls which cause a needless amout of duplicated
+ * logging output (and CPU time as the calculation process is
+ * done) */
+ if (freq == last_target) {
+ return 0;
+ }
+
+ /* try to look for a valid frequency value from cpu frequency table */
+ if (cpufreq_frequency_table_target(policy, sun4i_freq_tbl, freq, relation, &index)) {
+ CPUFREQ_ERR("%s: try to look for a valid frequency for %u failed!\n", __func__, freq);
+ return -EINVAL;
+ }
+
+ if (sun4i_freq_tbl[index].frequency == last_target) {
+ /* frequency is same as the value last set, need not adjust */
+ return 0;
+ }
+ freq = sun4i_freq_tbl[index].frequency;
+
+ /* update the target frequency */
+ freq_cfg.pll = sun4i_freq_tbl[index].frequency * 1000;
+ freq_cfg.div.i = sun4i_freq_tbl[index].index;
+ CPUFREQ_DBG("%s: target frequency find is %u, entry %u\n", __func__, freq_cfg.pll, index);
+
+ /* try to set target frequency */
+ ret = sun4i_cpufreq_settarget(policy, &freq_cfg);
+ if(!ret) {
+ last_target = freq;
+ }
+
+ return ret;
+}
+
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_get
+*
+*Description: get the frequency that cpu currently is running;
+*
+*Arguments : cpu cpu number;
+*
+*Return : cpu frequency, based on khz;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static unsigned int sun4i_cpufreq_get(unsigned int cpu)
+{
+ return clk_get_rate(clk_cpu) / 1000;
+}
+
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_init
+*
+*Description: cpu frequency initialise a policy;
+*
+*Arguments : policy cpu frequency policy;
+*
+*Return : result, return 0 if init ok, else, return -EINVAL;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static int sun4i_cpufreq_init(struct cpufreq_policy *policy)
+{
+ CPUFREQ_DBG(KERN_INFO "%s: initialising policy %p\n", __func__, policy);
+
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ policy->cur = sun4i_cpufreq_get(0);
+ policy->min = policy->cpuinfo.min_freq = SUN4I_CPUFREQ_MIN / 1000;
+ policy->max = policy->cpuinfo.max_freq = SUN4I_CPUFREQ_MAX / 1000;
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+ /* feed the latency information from the cpu driver */
+ policy->cpuinfo.transition_latency = SUN4I_FREQTRANS_LATENCY;
+
+ return 0;
+}
+
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_getcur
+*
+*Description: get current cpu frequency configuration;
+*
+*Arguments : cfg cpu frequency cofniguration;
+*
+*Return : result;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static int sun4i_cpufreq_getcur(struct sun4i_cpu_freq_t *cfg)
+{
+ unsigned int freq, freq0;
+
+ if(!cfg) {
+ return -EINVAL;
+ }
+
+ cfg->pll = clk_get_rate(clk_pll);
+ freq = clk_get_rate(clk_cpu);
+ cfg->div.s.cpu_div = cfg->pll / freq;
+ freq0 = clk_get_rate(clk_axi);
+ cfg->div.s.axi_div = freq / freq0;
+ freq = clk_get_rate(clk_ahb);
+ cfg->div.s.ahb_div = freq0 / freq;
+ freq0 = clk_get_rate(clk_apb);
+ cfg->div.s.apb_div = freq /freq0;
+
+ return 0;
+}
+
+
+
+#ifdef CONFIG_PM
+
+/* variable for backup cpu frequency configuration */
+static struct sun4i_cpu_freq_t suspend_freq;
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_suspend
+*
+*Description: back up cpu frequency configuration for suspend;
+*
+*Arguments : policy cpu frequency policy;
+* pmsg power management message;
+*
+*Return : return 0,
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static int sun4i_cpufreq_suspend(struct cpufreq_policy *policy)
+{
+ struct sun4i_cpu_freq_t suspend;
+
+ CPUFREQ_DBG("%s, set cpu frequency to 60Mhz to prepare enter standby\n", __func__);
+
+ sun4i_cpufreq_getcur(&suspend_freq);
+
+ /* set cpu frequency to 60M hz for standby */
+ suspend.pll = 60000000;
+ suspend.div.s.cpu_div = 1;
+ suspend.div.s.axi_div = 1;
+ suspend.div.s.ahb_div = 1;
+ suspend.div.s.apb_div = 2;
+ __set_cpufreq_target(&suspend_freq, &suspend);
+
+ return 0;
+}
+
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_resume
+*
+*Description: cpu frequency configuration resume;
+*
+*Arguments : policy cpu frequency policy;
+*
+*Return : result;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static int sun4i_cpufreq_resume(struct cpufreq_policy *policy)
+{
+ struct sun4i_cpu_freq_t suspend;
+
+ /* invalidate last_target setting */
+ last_target = ~0;
+
+ CPUFREQ_DBG("%s: resuming with policy %p\n", __func__, policy);
+ sun4i_cpufreq_getcur(&suspend);
+
+ /* restore cpu frequency configuration */
+ __set_cpufreq_target(&suspend, &suspend_freq);
+
+ CPUFREQ_DBG("%s: resuming done\n", __func__);
+ return 0;
+}
+
+
+#else /* #ifdef CONFIG_PM */
+
+#define sun4i_cpufreq_suspend NULL
+#define sun4i_cpufreq_resume NULL
+
+#endif /* #ifdef CONFIG_PM */
+
+static struct freq_attr *sun4i_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver sun4i_cpufreq_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = sun4i_cpufreq_verify,
+ .target = sun4i_cpufreq_target,
+ .get = sun4i_cpufreq_get,
+ .init = sun4i_cpufreq_init,
+ .suspend = sun4i_cpufreq_suspend,
+ .resume = sun4i_cpufreq_resume,
+ .name = "sun4i",
+ .attr = sun4i_cpufreq_attr,
+};
+
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_initclks
+*
+*Description: init cpu frequency clock resource;
+*
+*Arguments : none
+*
+*Return : result;
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static __init int sun4i_cpufreq_initclks(void)
+{
+ clk_pll = clk_get(NULL, "core_pll");
+ clk_cpu = clk_get(NULL, "cpu");
+ clk_axi = clk_get(NULL, "axi");
+ clk_ahb = clk_get(NULL, "ahb");
+ clk_apb = clk_get(NULL, "apb");
+
+ if (IS_ERR(clk_pll) || IS_ERR(clk_cpu) || IS_ERR(clk_axi) ||
+ IS_ERR(clk_ahb) || IS_ERR(clk_apb)) {
+ CPUFREQ_INF(KERN_ERR "%s: could not get clock(s)\n", __func__);
+ return -ENOENT;
+ }
+
+ CPUFREQ_INF("%s: clocks pll=%lu,cpu=%lu,axi=%lu,ahp=%lu,apb=%lu\n", __func__,
+ clk_get_rate(clk_pll), clk_get_rate(clk_cpu), clk_get_rate(clk_axi),
+ clk_get_rate(clk_ahb), clk_get_rate(clk_apb));
+
+ #ifdef CONFIG_CPU_FREQ_DVFS
+ corevdd = regulator_get(NULL, "axp20_core");
+ if (IS_ERR(corevdd)) {
+ CPUFREQ_INF("try to get regulator failed, core vdd will not changed!\n");
+ corevdd = NULL;
+ } else {
+ CPUFREQ_INF("try to get regulator(0x%x) successed.\n", (__u32)corevdd);
+ last_vdd = regulator_get_voltage(corevdd) / 1000;
+ }
+ #endif
+
+ return 0;
+}
+
+
+/*
+*********************************************************************************************************
+* sun4i_cpufreq_initcall
+*
+*Description: cpu frequency driver initcall
+*
+*Arguments : none
+*
+*Return : result
+*
+*Notes :
+*
+*********************************************************************************************************
+*/
+static int __init sun4i_cpufreq_initcall(void)
+{
+ int ret = 0;
+ struct cpufreq_frequency_table *sun4i_freq_tbl = sunxi_cpufreq_table();
+
+ /* initialise some clock resource */
+ ret = sun4i_cpufreq_initclks();
+ if(ret) {
+ return ret;
+ }
+
+ /* initialise current frequency configuration */
+ sun4i_cpufreq_getcur(&cpu_cur);
+ sun4i_cpufreq_show("cur", &cpu_cur);
+
+ /* register cpu frequency driver */
+ ret = cpufreq_register_driver(&sun4i_cpufreq_driver);
+ /* register cpu frequency table to cpufreq core */
+ cpufreq_frequency_table_get_attr(sun4i_freq_tbl, 0);
+ /* update policy for boot cpu */
+ cpufreq_update_policy(0);
+
+ return ret;
+}
+late_initcall(sun4i_cpufreq_initcall);
diff --git a/arch/arm/plat-sunxi/cpu-freq/cpu-freq.h b/arch/arm/plat-sunxi/cpu-freq/cpu-freq.h
new file mode 100644
index 0000000..77098b1
--- /dev/null
+++ b/arch/arm/plat-sunxi/cpu-freq/cpu-freq.h
@@ -0,0 +1,87 @@
+/*
+ * arch/arm/mach-sun4i/cpu-freq/cpu-freq.h
+ *
+ * (C) Copyright 2007-2012
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Kevin Zhang <ke...@allwinnertech.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __SUN4I_CPU_FREQ_H__
+#define __SUN4I_CPU_FREQ_H__
+
+#include <linux/types.h>
+#include <linux/cpufreq.h>
+
+#undef CPUFREQ_DBG
+#undef CPUFREQ_ERR
+#if (0)
+ #define CPUFREQ_DBG(format,args...) printk("[cpu_freq] DBG:"format,##args)
+ #define CPUFREQ_ERR(format,args...) printk("[cpu_freq] ERR:"format,##args)
+ #define CPUFREQ_INF(format,args...) printk("[cpu_freq] INF:"format,##args)
+#else
+ #define CPUFREQ_DBG(format,args...) do{}while(0)
+ #define CPUFREQ_ERR(format,args...) do{}while(0)
+ #define CPUFREQ_INF(format,args...) do{}while(0)
+#endif
+
+
+#define SUN4I_CPUFREQ_MAX (1008000000) /* config the maximum frequency of sun4i core */
+#define SUN4I_CPUFREQ_MIN (60000000) /* config the minimum frequency of sun4i core */
+#define SUN4I_FREQTRANS_LATENCY (2000000) /* config the transition latency, based on ns */
+
+struct sun4i_clk_div_t {
+ __u32 cpu_div:4; /* division of cpu clock, divide core_pll */
+ __u32 axi_div:4; /* division of axi clock, divide cpu clock*/
+ __u32 ahb_div:4; /* division of ahb clock, divide axi clock*/
+ __u32 apb_div:4; /* division of apb clock, divide ahb clock*/
+ __u32 reserved:16;
+};
+
+struct sun4i_cpu_freq_t {
+ __u32 pll; /* core pll frequency value */
+ union {
+ struct sun4i_clk_div_t s; /* division configuration */
+ __u32 i;
+ } div;
+};
+
+#define SUN4I_CLK_DIV(cpu_div, axi_div, ahb_div, apb_div) \
+ ((cpu_div<<0)|(axi_div<<4)|(ahb_div<<8)|(apb_div<<12))
+
+#ifdef CONFIG_CPU_FREQ_DVFS
+struct cpufreq_dvfs {
+ unsigned int freq; /* cpu frequency */
+ unsigned int volt; /* voltage for the frequency */
+};
+#endif
+
+struct cpufreq_div_order {
+ __u32 div;
+ __u32 pll;
+};
+
+/* Table fetchers */
+struct cpufreq_frequency_table *sunxi_cpufreq_table(void);
+struct cpufreq_div_order *sunxi_div_order_table(int *length);
+#ifdef CONFIG_CPU_FREQ_DVFS
+struct cpufreq_dvfs *sunxi_dvfs_table(void);
+#endif
+
+#endif /* #ifndef __SUN4I_CPU_FREQ_H__ */
+
+
--
1.8.1.2

Hans de Goede

unread,
Feb 9, 2013, 10:47:59 AM2/9/13
to linux...@googlegroups.com, Hans de Goede
cpu-freq has 2 sets of limits, 1 called cpuinfo which indicates what the
minimum / maximum freq of the cpu is, and the 2nd set indicates between
which limits the governor should automatically scale.

This patch splits the setting of the 2 sets of limits, and adds a
SUNXI_SCALING_MIN Kconfig option to allow raising the minimum speed
the (ondemand) governor will use from the current 60 MHz, which is
unworkably slow IMHO, to something better, ie 408 MHz, which is the last
speed at which the cpu runs at its lowest voltage of 1.0 volt.

This patch keeps the default at 60 MHz, to avoid regressions in battery
use, but I would like to advocate people to try out 408 Mhz instead,
which not only makes the interactive feel of the device a lot better, it
likely also *saves* power since it will cause the cpu to reach an idle
state quicker, which will allow it to power down entire blocks, thus stopping
the constant (non speed dependent) leakage current of those blocks sooner,
while using aprox. the same amount of power to do the work (it uses more
power due to the higher clock, but does the work proportionally faster,
resulting in aprox. the same power use). This is the so called race to idle
trick.

While at it also fix the formatting of sun4i_cpufreq_init().

Signed-off-by: Hans de Goede <hdeg...@redhat.com>
---
arch/arm/plat-sunxi/Kconfig | 10 ++++++++++
arch/arm/plat-sunxi/cpu-freq/cpu-freq.c | 22 ++++++++++++----------
arch/arm/plat-sunxi/cpu-freq/cpu-freq.h | 4 ++++
3 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/arch/arm/plat-sunxi/Kconfig b/arch/arm/plat-sunxi/Kconfig
index cd89acf..7c46724 100644
--- a/arch/arm/plat-sunxi/Kconfig
+++ b/arch/arm/plat-sunxi/Kconfig
@@ -23,4 +23,14 @@ config MACH_SUN5I
depends on ARCH_SUN5I
default y

+config SUNXI_SCALING_MIN
+ int "Minimum clockspeed for the cpu governor"
+ default "60"
+ help
+ The (default) minimum clockspeed the cpu scaling governor may use,
+ the lowest the sunxi cpu core can go is 60 MHz, which is quite slow,
+ so you may wish to set a somewhat higher minimum speed. 408 Mhz is
+ the last speed at the lowest voltage setting and as such is a good
+ value to use.
+
endmenu
diff --git a/arch/arm/plat-sunxi/cpu-freq/cpu-freq.c b/arch/arm/plat-sunxi/cpu-freq/cpu-freq.c
index 099ea80..90221ef 100644
--- a/arch/arm/plat-sunxi/cpu-freq/cpu-freq.c
+++ b/arch/arm/plat-sunxi/cpu-freq/cpu-freq.c
@@ -491,20 +491,22 @@ static unsigned int sun4i_cpufreq_get(unsigned int cpu)
*/
static int sun4i_cpufreq_init(struct cpufreq_policy *policy)
{
- CPUFREQ_DBG(KERN_INFO "%s: initialising policy %p\n", __func__, policy);
+ CPUFREQ_DBG(KERN_INFO "%s: initialize policy %p\n", __func__, policy);

- if (policy->cpu != 0)
- return -EINVAL;
+ if (policy->cpu != 0)
+ return -EINVAL;

- policy->cur = sun4i_cpufreq_get(0);
- policy->min = policy->cpuinfo.min_freq = SUN4I_CPUFREQ_MIN / 1000;
- policy->max = policy->cpuinfo.max_freq = SUN4I_CPUFREQ_MAX / 1000;
- policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+ policy->cur = sun4i_cpufreq_get(0);
+ policy->min = SUN4I_SCALING_MIN / 1000;
+ policy->max = SUN4I_SCALING_MAX / 1000;
+ policy->cpuinfo.min_freq = SUN4I_CPUFREQ_MIN / 1000;
+ policy->cpuinfo.max_freq = SUN4I_CPUFREQ_MAX / 1000;
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;

- /* feed the latency information from the cpu driver */
- policy->cpuinfo.transition_latency = SUN4I_FREQTRANS_LATENCY;
+ /* feed the latency information from the cpu driver */
+ policy->cpuinfo.transition_latency = SUN4I_FREQTRANS_LATENCY;

- return 0;
+ return 0;
}


diff --git a/arch/arm/plat-sunxi/cpu-freq/cpu-freq.h b/arch/arm/plat-sunxi/cpu-freq/cpu-freq.h
index 77098b1..41d3184 100644
--- a/arch/arm/plat-sunxi/cpu-freq/cpu-freq.h
+++ b/arch/arm/plat-sunxi/cpu-freq/cpu-freq.h
@@ -40,8 +40,12 @@
#endif


+/* Absolute minimum and maximum */
#define SUN4I_CPUFREQ_MAX (1008000000) /* config the maximum frequency of sun4i core */
#define SUN4I_CPUFREQ_MIN (60000000) /* config the minimum frequency of sun4i core */
+/* Defaults limits for the scaling governor */
+#define SUN4I_SCALING_MIN (CONFIG_SUNXI_SCALING_MIN * 1000000)
+#define SUN4I_SCALING_MAX (1008000000)
#define SUN4I_FREQTRANS_LATENCY (2000000) /* config the transition latency, based on ns */

struct sun4i_clk_div_t {
--
1.8.1.2

Jari Helaakoski

unread,
Feb 9, 2013, 3:26:37 PM2/9/13
to linux...@googlegroups.com


2013/2/9 Hans de Goede <hdeg...@redhat.com>
Anyone tried CPU_FREQ_USR_EVNT_NOTIFY?
This will allow input sub-system notify cpu-freq that some user event has happend, need up cpu frequency to response it.
 -Jari

Siarhei Siamashka

unread,
Feb 11, 2013, 8:15:31 PM2/11/13
to linux...@googlegroups.com
On Sat, 9 Feb 2013 16:47:56 +0100
Hans de Goede <hdeg...@redhat.com> wrote:

> Since it is now possible to disable fb-mem reservation from the cmdline,
> enable the kmalloc fallback path even if CONFIG_FB_SUNXI_RESERVED_MEM is set.

I'm not sure if using kmalloc even as a fallback is a really good idea.
It is not suitable for allocating large buffers at any time other
than immediately after the system boot. Trying to allocate buffers
later becomes a lottery, especially on the long running systems with
some memory fragmentation accumulated.

I tried to experiment a bit with CONFIG_FB_SUNXI_RESERVED_MEM not set
some time ago and it was indeed unreliable for me.

The proper solution seems to be CMA [1][2], which can be backported
to 3.4 using, for example, the patches from LTSI kernel.

1. http://elinux.org/images/7/7c/Elce11_szyprowski_park.pdf
2. http://lwn.net/Articles/486301/
Best regards,
Siarhei Siamashka

Alejandro Mery

unread,
Feb 15, 2013, 12:16:23 PM2/15/13
to linux...@googlegroups.com, Hans de Goede
sorry for the delay. applied on stage/sunxi-3.4. backporting to 3.0's
tomorrow.

cheers,
Alejandro

Siarhei Siamashka

unread,
Feb 15, 2013, 5:01:02 PM2/15/13
to linux...@googlegroups.com, Hans de Goede
On Tue, 12 Feb 2013 03:15:31 +0200
Siarhei Siamashka <siarhei....@gmail.com> wrote:

> On Sat, 9 Feb 2013 16:47:56 +0100
> Hans de Goede <hdeg...@redhat.com> wrote:
>
> > Since it is now possible to disable fb-mem reservation from the cmdline,
> > enable the kmalloc fallback path even if CONFIG_FB_SUNXI_RESERVED_MEM is set.
>
> I'm not sure if using kmalloc even as a fallback is a really good idea.
> It is not suitable for allocating large buffers at any time other
> than immediately after the system boot. Trying to allocate buffers
> later becomes a lottery, especially on the long running systems with
> some memory fragmentation accumulated.
>
> I tried to experiment a bit with CONFIG_FB_SUNXI_RESERVED_MEM not set
> some time ago and it was indeed unreliable for me.
>
> The proper solution seems to be CMA [1][2], which can be backported
> to 3.4 using, for example, the patches from LTSI kernel.
>
> 1. http://elinux.org/images/7/7c/Elce11_szyprowski_park.pdf
> 2. http://lwn.net/Articles/486301/

Now that this patch has actually landed to the stage branch, I would
like to remind that this approach is not just "theoretically" broken.
It really fails in practice.

For example, using the stage/sunxi-3.4 branch on my Mele A2000
(512MB RAM) with sunxi_fb_mem_reserve=0 added to cmdline I get the
following results:

1280x720 32bpp, fb0_framebuffer_num=2 - FAIL
1280x720 32bpp, fb0_framebuffer_num=1 - SUCCESS
1920x1080 32bpp, fb0_framebuffer_num=1 - FAIL

"FAIL" here means that the device shows colored garbage on screen for a
few seconds and goes into the reboot loop. Apparently even allocating
just 8MB of memory without prior reservation is a problem even for the
framebuffer drivers statically compiled into the kernel. The devices
with 1GB of RAM are supposed to be a bit more lucky with large buffers
allocations though.

I would say that we probably should either take CMA into use, or don't
even try allocating memory without reservation.

Hans de Goede

unread,
Feb 15, 2013, 6:37:35 PM2/15/13