summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx31
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx31')
-rw-r--r--firmware/target/arm/imx31/avic-imx31.c7
-rw-r--r--firmware/target/arm/imx31/avic-imx31.h37
-rw-r--r--firmware/target/arm/imx31/ccm-imx31.c62
-rw-r--r--firmware/target/arm/imx31/ccm-imx31.h17
-rw-r--r--firmware/target/arm/imx31/debug-imx31.c33
-rw-r--r--firmware/target/arm/imx31/dvfs_dptc-imx31.c712
-rw-r--r--firmware/target/arm/imx31/dvfs_dptc-imx31.h99
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h279
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c7
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c8
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c43
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/system-target.h15
-rw-r--r--firmware/target/arm/imx31/mc13783-imx31.c21
-rw-r--r--firmware/target/arm/imx31/rolo_restart_firmware.S (renamed from firmware/target/arm/imx31/rolo_restart.S)10
-rw-r--r--firmware/target/arm/imx31/sdma-imx31.c2
15 files changed, 1274 insertions, 78 deletions
diff --git a/firmware/target/arm/imx31/avic-imx31.c b/firmware/target/arm/imx31/avic-imx31.c
index 4ba7da4be0..c8bf419bcd 100644
--- a/firmware/target/arm/imx31/avic-imx31.c
+++ b/firmware/target/arm/imx31/avic-imx31.c
@@ -128,7 +128,7 @@ void avic_init(void)
128 : : : "r0"); 128 : : : "r0");
129 129
130 /* Enable normal interrupts at all priorities */ 130 /* Enable normal interrupts at all priorities */
131 avic->nimask = 0x1f; 131 avic->nimask = AVIC_NIL_ENABLE;
132} 132}
133 133
134void avic_set_int_priority(enum IMX31_INT_LIST ints, 134void avic_set_int_priority(enum IMX31_INT_LIST ints,
@@ -210,3 +210,8 @@ void avic_set_int_type(enum IMX31_INT_LIST ints, enum INT_TYPE intstype)
210 210
211 restore_interrupt(oldstatus); 211 restore_interrupt(oldstatus);
212} 212}
213
214void avic_set_ni_level(unsigned int level)
215{
216 AVIC_NIMASK = level > 0x1f ? 0x1f : level;
217}
diff --git a/firmware/target/arm/imx31/avic-imx31.h b/firmware/target/arm/imx31/avic-imx31.h
index a049713600..ba8f91cc1a 100644
--- a/firmware/target/arm/imx31/avic-imx31.h
+++ b/firmware/target/arm/imx31/avic-imx31.h
@@ -172,7 +172,11 @@ struct avic_map
172 }; 172 };
173}; 173};
174 174
175/* #define IRQ priorities for different modules (0-15) */
175#define INT_PRIO_DEFAULT 7 176#define INT_PRIO_DEFAULT 7
177#define INT_PRIO_DVFS (INT_PRIO_DEFAULT+1)
178#define INT_PRIO_DPTC (INT_PRIO_DEFAULT+1)
179#define INT_PRIO_SDMA (INT_PRIO_DEFAULT+2)
176 180
177enum INT_TYPE 181enum INT_TYPE
178{ 182{
@@ -210,4 +214,37 @@ void avic_set_int_priority(enum IMX31_INT_LIST ints,
210void avic_disable_int(enum IMX31_INT_LIST ints); 214void avic_disable_int(enum IMX31_INT_LIST ints);
211void avic_set_int_type(enum IMX31_INT_LIST ints, enum INT_TYPE intstype); 215void avic_set_int_type(enum IMX31_INT_LIST ints, enum INT_TYPE intstype);
212 216
217#define AVIC_NIL_DISABLE 0xf
218#define AVIC_NIL_ENABLE 0x1f
219void avic_set_ni_level(unsigned int level);
220
221/* Call a service routine while allowing preemption by interrupts of higher
222 * priority. r4-r7 must be preserved for epilogue code to restore context. */
223#define AVIC_NESTED_NI_CALL_PROLOGUE() \
224({ asm volatile ( \
225 "sub lr, lr, #4 \n" /* prepare return address */ \
226 "stmfd sp!, { r0-r7, r12, lr } \n" /* preserve return context */ \
227 "mov r0, #0x68000000 \n" /* AVIC_BASE_ADDR */ \
228 "mrs r4, spsr \n" /* save SPSR_irq */ \
229 "ldr r5, [r0, #0x04] \n" /* save NIMASK */ \
230 "ldr r1, [r0, #0x40] \n" /* load NIVECSR */ \
231 "mov r2, sp \n" /* remember IRQ stack to use in call */ \
232 "str r1, [r0, #0x04] \n" /* copy NIVECSR to NIMASK */ \
233 "cps #0x13 \n" /* switch to SVC mode (+ unmask IRQ) */ \
234 "mov r6, sp \n" /* save SP_svc */ \
235 "mov r7, lr \n" /* save LR_svc */ \
236 "mov sp, r2 \n" /* switch to SP_irq */ \
237 ); })
238
239#define AVIC_NESTED_NI_CALL_EPILOGUE() \
240({ asm volatile ( \
241 "mov sp, r6 \n" /* restore SP_svc */ \
242 "mov lr, r7 \n" /* restore LR_svc */ \
243 "cps #0x12 \n" /* return to IRQ mode (+ mask IRQ) */ \
244 "mov r0, #0x68000000 \n" /* AVIC BASE ADDR */ \
245 "msr spsr_cxsf, r4 \n" /* restore SPSR_irq */ \
246 "str r5, [r0, #0x04] \n" /* restore NIMASK */ \
247 "ldmfd sp!, { r0-r7, r12, pc }^ \n" /* reload context and return */ \
248 ); })
249
213#endif /* AVIC_IMX31_H */ 250#endif /* AVIC_IMX31_H */
diff --git a/firmware/target/arm/imx31/ccm-imx31.c b/firmware/target/arm/imx31/ccm-imx31.c
index 0d166e5dbf..2cf2080cf1 100644
--- a/firmware/target/arm/imx31/ccm-imx31.c
+++ b/firmware/target/arm/imx31/ccm-imx31.c
@@ -24,7 +24,8 @@
24#include "cpu.h" 24#include "cpu.h"
25#include "ccm-imx31.h" 25#include "ccm-imx31.h"
26 26
27unsigned int ccm_get_src_pll(void) 27/* Return the current source pll for MCU */
28enum IMX31_PLLS ccm_get_src_pll(void)
28{ 29{
29 return (CCM_PMCR0 & 0xC0000000) == 0 ? PLL_SERIAL : PLL_MCU; 30 return (CCM_PMCR0 & 0xC0000000) == 0 ? PLL_SERIAL : PLL_MCU;
30} 31}
@@ -45,8 +46,21 @@ void ccm_module_clock_gating(enum IMX31_CG_LIST cg, enum IMX31_CG_MODES mode)
45 imx31_regmod32(reg, mode << shift, mask); 46 imx31_regmod32(reg, mode << shift, mask);
46} 47}
47 48
49/* Decode PLL output frequency from register value */
50unsigned int ccm_calc_pll_rate(unsigned int infreq, unsigned long regval)
51{
52 uint32_t mfn = regval & 0x3ff;
53 uint32_t pd = ((regval >> 26) & 0xf) + 1;
54 uint32_t mfd = ((regval >> 16) & 0x3ff) + 1;
55 uint32_t mfi = (regval >> 10) & 0xf;
56
57 mfi = mfi <= 5 ? 5 : mfi;
58
59 return 2ull*infreq*(mfi * mfd + mfn) / (mfd * pd);
60}
61
48/* Get the PLL reference clock frequency in HZ */ 62/* Get the PLL reference clock frequency in HZ */
49unsigned int ccm_get_pll_ref_clk(void) 63unsigned int ccm_get_pll_ref_clk_rate(void)
50{ 64{
51 if ((CCM_CCMR & (3 << 1)) == (1 << 1)) 65 if ((CCM_CCMR & (3 << 1)) == (1 << 1))
52 return CONFIG_CKIL_FREQ * 1024; 66 return CONFIG_CKIL_FREQ * 1024;
@@ -55,41 +69,59 @@ unsigned int ccm_get_pll_ref_clk(void)
55} 69}
56 70
57/* Return PLL frequency in HZ */ 71/* Return PLL frequency in HZ */
58unsigned int ccm_get_pll(enum IMX31_PLLS pll) 72unsigned int ccm_get_pll_rate(enum IMX31_PLLS pll)
59{ 73{
60 uint32_t infreq = ccm_get_pll_ref_clk(); 74 return ccm_calc_pll_rate(ccm_get_pll_ref_clk_rate(), (&CCM_MPCTL)[pll]);
61 uint32_t reg = (&CCM_MPCTL)[pll]; 75}
62 uint32_t mfn = reg & 0x3ff;
63 uint32_t pd = ((reg >> 26) & 0xf) + 1;
64 uint64_t mfd = ((reg >> 16) & 0x3ff) + 1;
65 uint32_t mfi = (reg >> 10) & 0xf;
66 76
67 mfi = mfi <= 5 ? 5 : mfi; 77unsigned int ccm_get_mcu_clk(void)
78{
79 unsigned int pllnum = ccm_get_src_pll();
80 unsigned int fpll = ccm_get_pll_rate(pllnum);
81 unsigned int mcu_podf = (CCM_PDR0 & 0x7) + 1;
68 82
69 return 2*infreq*(mfi * mfd + mfn) / (mfd * pd); 83 return fpll / mcu_podf;
70} 84}
71 85
72unsigned int ccm_get_ipg_clk(void) 86unsigned int ccm_get_ipg_clk(void)
73{ 87{
74 unsigned int pllnum = ccm_get_src_pll(); 88 unsigned int pllnum = ccm_get_src_pll();
75 unsigned int pll = ccm_get_pll(pllnum); 89 unsigned int fpll = ccm_get_pll_rate(pllnum);
76 uint32_t reg = CCM_PDR0; 90 uint32_t reg = CCM_PDR0;
77 unsigned int max_pdf = ((reg >> 3) & 0x7) + 1; 91 unsigned int max_pdf = ((reg >> 3) & 0x7) + 1;
78 unsigned int ipg_pdf = ((reg >> 6) & 0x3) + 1; 92 unsigned int ipg_pdf = ((reg >> 6) & 0x3) + 1;
79 93
80 return pll / (max_pdf * ipg_pdf); 94 return fpll / (max_pdf * ipg_pdf);
81} 95}
82 96
83unsigned int ccm_get_ahb_clk(void) 97unsigned int ccm_get_ahb_clk(void)
84{ 98{
85 unsigned int pllnum = ccm_get_src_pll(); 99 unsigned int pllnum = ccm_get_src_pll();
86 unsigned int pll = ccm_get_pll(pllnum); 100 unsigned int fpll = ccm_get_pll_rate(pllnum);
87 unsigned int max_pdf = ((CCM_PDR0 >> 3) & 0x7) + 1; 101 unsigned int max_pdf = ((CCM_PDR0 >> 3) & 0x7) + 1;
88 102
89 return pll / max_pdf; 103 return fpll / max_pdf;
90} 104}
91 105
92unsigned int ccm_get_ata_clk(void) 106unsigned int ccm_get_ata_clk(void)
93{ 107{
94 return ccm_get_ipg_clk(); 108 return ccm_get_ipg_clk();
95} 109}
110
111/* Write new values to the current PLL and post-dividers */
112void ccm_set_mcupll_and_pdr(unsigned long pllctl, unsigned long pdr)
113{
114 unsigned int pll = ccm_get_src_pll();
115 volatile unsigned long *pllreg = &(&CCM_MPCTL)[pll];
116 unsigned long fref = ccm_get_pll_ref_clk_rate();
117 unsigned long curfreq = ccm_calc_pll_rate(fref, *pllreg);
118 unsigned long newfreq = ccm_calc_pll_rate(fref, pllctl);
119
120 if (newfreq > curfreq)
121 CCM_PDR0 = pdr;
122
123 *pllreg = pllctl;
124
125 if (newfreq <= curfreq)
126 CCM_PDR0 = pdr;
127}
diff --git a/firmware/target/arm/imx31/ccm-imx31.h b/firmware/target/arm/imx31/ccm-imx31.h
index e95891255d..400f6cad5d 100644
--- a/firmware/target/arm/imx31/ccm-imx31.h
+++ b/firmware/target/arm/imx31/ccm-imx31.h
@@ -93,11 +93,21 @@ enum IMX31_PLLS
93 NUM_PLLS, 93 NUM_PLLS,
94}; 94};
95 95
96
97/* Return the current source pll for MCU */
98enum IMX31_PLLS ccm_get_src_pll(void);
99
100/* Decode PLL output frequency from register value */
101unsigned int ccm_calc_pll_rate(unsigned int infreq, unsigned long regval);
102
96/* Get the PLL reference clock frequency in HZ */ 103/* Get the PLL reference clock frequency in HZ */
97unsigned int ccm_get_pll_ref_clk(void); 104unsigned int ccm_get_pll_ref_clk_rate(void);
98 105
99/* Return PLL frequency in HZ */ 106/* Return PLL frequency in HZ */
100unsigned int ccm_get_pll(enum IMX31_PLLS pll); 107unsigned int ccm_get_pll_rate(enum IMX31_PLLS pll);
108
109/* Return MCU frequency in HZ */
110unsigned int ccm_get_mcu_clk(void);
101 111
102/* Return ipg_clk in HZ */ 112/* Return ipg_clk in HZ */
103unsigned int ccm_get_ipg_clk(void); 113unsigned int ccm_get_ipg_clk(void);
@@ -108,4 +118,7 @@ unsigned int ccm_get_ahb_clk(void);
108/* Return the ATA frequency in HZ */ 118/* Return the ATA frequency in HZ */
109unsigned int ccm_get_ata_clk(void); 119unsigned int ccm_get_ata_clk(void);
110 120
121/* Write new values to the current PLL and post-dividers */
122void ccm_set_mcupll_and_pdr(unsigned long pllctl, unsigned long pdr);
123
111#endif /* _CCM_IMX31_H_ */ 124#endif /* _CCM_IMX31_H_ */
diff --git a/firmware/target/arm/imx31/debug-imx31.c b/firmware/target/arm/imx31/debug-imx31.c
index dc3c293495..d854c9bc36 100644
--- a/firmware/target/arm/imx31/debug-imx31.c
+++ b/firmware/target/arm/imx31/debug-imx31.c
@@ -29,6 +29,7 @@
29#include "mc13783.h" 29#include "mc13783.h"
30#include "adc.h" 30#include "adc.h"
31#include "ccm-imx31.h" 31#include "ccm-imx31.h"
32#include "dvfs_dptc-imx31.h"
32 33
33bool __dbg_hw_info(void) 34bool __dbg_hw_info(void)
34{ 35{
@@ -39,6 +40,9 @@ bool __dbg_hw_info(void)
39 unsigned int freq; 40 unsigned int freq;
40 uint32_t regval; 41 uint32_t regval;
41 42
43 extern volatile unsigned int dvfs_nr_dn, dvfs_nr_up, dvfs_nr_pnc;
44 extern volatile unsigned int dptc_nr_dn, dptc_nr_up, dptc_nr_pnc;
45
42 lcd_clear_display(); 46 lcd_clear_display();
43 lcd_setfont(FONT_SYSFIXED); 47 lcd_setfont(FONT_SYSFIXED);
44 48
@@ -52,11 +56,11 @@ bool __dbg_hw_info(void)
52 spctl = CCM_SPCTL; 56 spctl = CCM_SPCTL;
53 upctl = CCM_UPCTL; 57 upctl = CCM_UPCTL;
54 58
55 pllref = ccm_get_pll_ref_clk(); 59 pllref = ccm_get_pll_ref_clk_rate();
56 60
57 mcu_pllfreq = ccm_get_pll(PLL_MCU); 61 mcu_pllfreq = ccm_calc_pll_rate(pllref, mpctl);
58 ser_pllfreq = ccm_get_pll(PLL_SERIAL); 62 ser_pllfreq = ccm_calc_pll_rate(pllref, spctl);
59 usb_pllfreq = ccm_get_pll(PLL_USB); 63 usb_pllfreq = ccm_calc_pll_rate(pllref, upctl);
60 64
61 lcd_putsf(0, line++, "pll_ref_clk: %u", pllref); 65 lcd_putsf(0, line++, "pll_ref_clk: %u", pllref);
62 line++; 66 line++;
@@ -107,10 +111,29 @@ bool __dbg_hw_info(void)
107 111
108 freq = usb_pllfreq / (((CCM_PDR0 >> 16) & 0x1f) + 1); 112 freq = usb_pllfreq / (((CCM_PDR0 >> 16) & 0x1f) + 1);
109 lcd_putsf(0, line++, " ipg_per_baud: %u", freq); 113 lcd_putsf(0, line++, " ipg_per_baud: %u", freq);
114
115 line++;
116 lcd_putsf(0, line++, "PMCR0: %08lX", CCM_PMCR0);
117
118 line++;
119 lcd_putsf(0, line++, "cpu_frequency: %ld Hz", cpu_frequency);
120
121 lcd_putsf(0, line++, "dvfs_level: %u", dvfs_get_level());
122 lcd_putsf(0, line++, "dvfs d|u|p: %u %u %u", dvfs_nr_dn, dvfs_nr_up, dvfs_nr_pnc);
123 regval = dvfs_dptc_get_voltage();
124 lcd_putsf(0, line++, "cpu_voltage: %d.%03d V", regval / 1000,
125 regval % 1000);
126
127 lcd_putsf(0, line++, "dptc_wp: %u", dptc_get_wp());
128 lcd_putsf(0, line++, "dptc d|u|p: %u %u %u", dptc_nr_dn, dptc_nr_up, dptc_nr_pnc);
129 lcd_putsf(0, line++, "DVCR0,1: %08lX %08lX", CCM_DCVR0, CCM_DCVR1);
130 lcd_putsf(0, line++, "DVCR2,3: %08lX %08lX", CCM_DCVR2, CCM_DCVR3);
131 lcd_putsf(0, line++, "SWITCHERS0: %08lX", mc13783_read(MC13783_SWITCHERS0));
132 lcd_putsf(0, line++, "SWITCHERS1: %08lX", mc13783_read(MC13783_SWITCHERS1));
110 133
111 lcd_update(); 134 lcd_update();
112 135
113 if (button_get(true) == (DEBUG_CANCEL|BUTTON_REL)) 136 if (button_get_w_tmo(HZ/10) == (DEBUG_CANCEL|BUTTON_REL))
114 return false; 137 return false;
115 } 138 }
116} 139}
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.c b/firmware/target/arm/imx31/dvfs_dptc-imx31.c
index 8f32fd0fba..6e177ba6bd 100644
--- a/firmware/target/arm/imx31/dvfs_dptc-imx31.c
+++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.c
@@ -22,27 +22,721 @@
22 ****************************************************************************/ 22 ****************************************************************************/
23#include "config.h" 23#include "config.h"
24#include "system.h" 24#include "system.h"
25#include "ccm-imx31.h" 25#include "logf.h"
26#include "mc13783.h" 26#include "mc13783.h"
27#include "iomuxc-imx31.h"
28#include "ccm-imx31.h"
29#include "avic-imx31.h"
30#include "dvfs_dptc-imx31.h"
31#include "dvfs_dptc_tables-target.h"
27 32
28/* Most of the code in here is based upon the Linux BSP provided by Freescale 33/* Most of the code in here is based upon the Linux BSP provided by Freescale
29 * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. */ 34 * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. */
30 35
31void dvfs_dptc_start(void) 36/* The current DVFS index level */
37static volatile unsigned int dvfs_level = DVFS_LEVEL_DEFAULT;
38/* The current DPTC working point */
39static volatile unsigned int dptc_wp = DPTC_WP_DEFAULT;
40
41
42static void update_dptc_counts(unsigned int level, unsigned int wp)
43{
44 int oldlevel = disable_irq_save();
45 const struct dptc_dcvr_table_entry *entry = &dptc_dcvr_table[level][wp];
46
47 CCM_DCVR0 = entry->dcvr0;
48 CCM_DCVR1 = entry->dcvr1;
49 CCM_DCVR2 = entry->dcvr2;
50 CCM_DCVR3 = entry->dcvr3;
51
52 restore_irq(oldlevel);
53}
54
55
56static inline uint32_t check_regulator_setting(uint32_t setting)
57{
58 /* Simply a safety check *in case* table gets scrambled */
59 if (setting < VOLTAGE_SETTING_MIN)
60 setting = VOLTAGE_SETTING_MIN;
61 else if (setting > VOLTAGE_SETTING_MAX)
62 setting = VOLTAGE_SETTING_MAX;
63
64 return setting;
65}
66
67
68/** DVFS **/
69static bool dvfs_running = false; /* Has driver enabled DVFS? */
70
71/* Request tracking since boot */
72unsigned int dvfs_nr_dn = 0;
73unsigned int dvfs_nr_up = 0;
74unsigned int dvfs_nr_pnc = 0;
75
76static void dvfs_stop(void);
77
78
79/* Wait for the UPDTEN flag to be set so that all bits may be written */
80static inline void wait_for_dvfs_update_en(void)
81{
82 while (!(CCM_PMCR0 & CCM_PMCR0_UPDTEN));
83}
84
85
86static void do_dvfs_update(unsigned int level)
87{
88 const struct dvfs_clock_table_entry *setting = &dvfs_clock_table[level];
89 unsigned long pmcr0 = CCM_PMCR0;
90
91 if (pmcr0 & CCM_PMCR0_DPTEN)
92 {
93 /* Ignore voltage change request from DPTC. Voltage is *not* valid. */
94 pmcr0 &= ~CCM_PMCR0_DPVCR;
95 }
96
97 pmcr0 &= ~CCM_PMCR0_VSCNT;
98
99 if (level > ((pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS))
100 {
101 pmcr0 |= CCM_PMCR0_UDSC; /* Up scaling, increase */
102 pmcr0 |= setting->vscnt << CCM_PMCR0_VSCNT_POS;
103 }
104 else
105 {
106 pmcr0 &= ~CCM_PMCR0_UDSC; /* Down scaling, decrease */
107 pmcr0 |= 0x1 << CCM_PMCR0_VSCNT_POS;
108 }
109
110 /* DVSUP (new frequency index) setup */
111 pmcr0 = (pmcr0 & ~CCM_PMCR0_DVSUP) | (level << CCM_PMCR0_DVSUP_POS);
112
113 dvfs_level = level;
114
115 if ((setting->pll_num << CCM_PMCR0_DFSUP_MCUPLL_POS) ^
116 (pmcr0 & CCM_PMCR0_DFSUP_MCUPLL))
117 {
118 /* Update pll and post-dividers. */
119 pmcr0 ^= CCM_PMCR0_DFSUP_MCUPLL;
120 pmcr0 &= ~CCM_PMCR0_DFSUP_POST_DIVIDERS;
121 }
122 else
123 {
124 /* Post-dividers update only */
125 pmcr0 |= CCM_PMCR0_DFSUP_POST_DIVIDERS;
126 }
127
128 CCM_PMCR0 = pmcr0;
129 udelay(100); /* Software wait for voltage ramp-up */
130 CCM_PDR0 = setting->pdr_val;
131
132 if (!(pmcr0 & CCM_PMCR0_DFSUP_POST_DIVIDERS))
133 {
134 /* Update the PLL settings */
135 if (pmcr0 & CCM_PMCR0_DFSUP_MCUPLL)
136 CCM_MPCTL = setting->pll_val;
137 else
138 CCM_SPCTL = setting->pll_val;
139 }
140
141 cpu_frequency = ccm_get_mcu_clk();
142
143 if (pmcr0 & CCM_PMCR0_DPTEN)
144 {
145 update_dptc_counts(level, dptc_wp);
146 /* Enable DPTC to request voltage changes. Voltage is valid. */
147 CCM_PMCR0 |= CCM_PMCR0_DPVCR;
148 udelay(2);
149 CCM_PMCR0 |= CCM_PMCR0_DPVV;
150 }
151}
152
153
154/* Start DVFS, change the set point and stop it */
155static void set_current_dvfs_level(unsigned int level)
156{
157 int oldlevel = disable_irq_save();
158
159 CCM_PMCR0 |= CCM_PMCR0_DVFEN;
160
161 wait_for_dvfs_update_en();
162
163 do_dvfs_update(level);
164
165 wait_for_dvfs_update_en();
166
167 CCM_PMCR0 &= ~CCM_PMCR0_DVFEN;
168
169 restore_irq(oldlevel);
170}
171
172
173/* DVFS Interrupt handler */
174static void __attribute__((used)) dvfs_int(void)
175{
176 unsigned long pmcr0 = CCM_PMCR0;
177 unsigned long fsvai = pmcr0 & CCM_PMCR0_FSVAI;
178 unsigned int level = (pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS;
179
180 if (pmcr0 & CCM_PMCR0_FSVAIM)
181 return; /* Do nothing. DVFS interrupt is masked. */
182
183 if (!(pmcr0 & CCM_PMCR0_UPDTEN))
184 return; /* Do nothing. DVFS didn't finish previous flow update. */
185
186 switch (fsvai)
187 {
188 case CCM_PMCR0_FSVAI_DECREASE:
189 if (level >= DVFS_NUM_LEVELS - 1)
190 return; /* DVFS already at lowest level */
191
192 /* Upon the DECREASE event, the frequency will be changed to the next
193 * higher state index. */
194 level++;
195 dvfs_nr_dn++;
196 break;
197
198 /* Single-step frequency increase */
199 case CCM_PMCR0_FSVAI_INCREASE:
200 if (level == 0)
201 return; /* DVFS already at highest level */
202
203 /* Upon the INCREASE event, the frequency will be changed to the next
204 * lower state index. */
205 level--;
206 dvfs_nr_up++;
207 break;
208
209 /* Right to highest if panic */
210 case CCM_PMCR0_FSVAI_INCREASE_NOW:
211 if (level == 0)
212 return; /* DVFS already at highest level */
213
214 /* Upon the INCREASE_NOW event, the frequency will be increased to
215 * the maximum (index 0). */
216 level = 0;
217 dvfs_nr_pnc++;
218 break;
219
220 case CCM_PMCR0_FSVAI_NO_INT:
221 default:
222 return; /* Do nothing. Freq change is not required */
223 } /* end switch */
224
225 do_dvfs_update(level);
226}
227
228
229/* Interrupt vector for DVFS */
230static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void)
231{
232 /* Audio can glitch with the long udelay if nested IRQ isn't allowed. */
233 AVIC_NESTED_NI_CALL_PROLOGUE();
234 asm volatile ("bl dvfs_int");
235 AVIC_NESTED_NI_CALL_EPILOGUE();
236}
237
238
239/* Initialize the DVFS hardware */
240static void dvfs_init(void)
241{
242 if (CCM_PMCR0 & CCM_PMCR0_DVFEN)
243 {
244 /* Turn it off first. Really, shouldn't happen though. */
245 dvfs_running = true;
246 dvfs_stop();
247 }
248
249 /* Combine SW1A and SW1B DVS pins for a possible five DVS levels
250 * per working point. Four, MAXIMUM, are actually used, one for each
251 * frequency. */
252 mc13783_set(MC13783_ARBITRATION_SWITCHERS, MC13783_SW1ABDVS);
253
254 /* Set DVS speed to 25mV every 4us. */
255 mc13783_write_masked(MC13783_SWITCHERS4, MC13783_SW1ADVSSPEED_4US,
256 MC13783_SW1ADVSSPEED);
257
258 /* Set DVFS pins to functional outputs. Input mode and pad setting is
259 * fixed in hardware. */
260 iomuxc_set_pin_mux(IOMUXC_DVFS0,
261 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_NONE);
262 iomuxc_set_pin_mux(IOMUXC_DVFS1,
263 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_NONE);
264
265#ifndef DVFS_NO_PWRRDY
266 /* Configure PWRRDY signal pin. */
267 imx31_regclr32(&GPIO1_GDIR, (1 << 5));
268 iomuxc_set_pin_mux(IOMUXC_GPIO1_5,
269 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_FUNCTIONAL);
270#endif
271
272 /* Initialize DVFS signal weights and detection modes. */
273 int i;
274 for (i = 0; i < 16; i++)
275 {
276 dvfs_set_lt_weight(i, lt_signals[i].weight);
277 dvfs_set_lt_detect(i, lt_signals[i].detect);
278 }
279
280 /* Set up LTR0. */
281 imx31_regmod32(&CCM_LTR0,
282 DVFS_UPTHR << CCM_LTR0_UPTHR_POS |
283 DVFS_DNTHR << CCM_LTR0_DNTHR_POS |
284 DVFS_DIV3CK,
285 CCM_LTR0_UPTHR | CCM_LTR0_DNTHR | CCM_LTR0_DIV3CK);
286
287 /* Set up LTR1. */
288 imx31_regmod32(&CCM_LTR1,
289 DVFS_DNCNT << CCM_LTR1_DNCNT_POS |
290 DVFS_UPCNT << CCM_LTR1_UPCNT_POS |
291 DVFS_PNCTHR << CCM_LTR1_PNCTHR_POS |
292 CCM_LTR1_LTBRSR,
293 CCM_LTR1_DNCNT | CCM_LTR1_UPCNT |
294 CCM_LTR1_PNCTHR | CCM_LTR1_LTBRSR);
295
296 /* Set up LTR2-- EMA configuration. */
297 imx31_regmod32(&CCM_LTR2, DVFS_EMAC << CCM_LTR2_EMAC_POS,
298 CCM_LTR2_EMAC);
299
300 /* DVFS interrupt goes to MCU. Mask load buffer full interrupt. */
301 imx31_regset32(&CCM_PMCR0, CCM_PMCR0_DVFIS | CCM_PMCR0_LBMI);
302
303 /* Initialize current core PLL and dividers for default level. Assumes
304 * clocking scheme has been set up appropriately in other init code. */
305 ccm_set_mcupll_and_pdr(dvfs_clock_table[DVFS_LEVEL_DEFAULT].pll_val,
306 dvfs_clock_table[DVFS_LEVEL_DEFAULT].pdr_val);
307
308 /* Set initial level and working point. */
309 set_current_dvfs_level(DVFS_LEVEL_DEFAULT);
310
311 logf("DVFS: Initialized");
312}
313
314
315/* Start the DVFS hardware */
316static void dvfs_start(void)
317{
318 int oldlevel;
319
320 /* Have to wait at least 3 div3 clocks before enabling after being
321 * stopped. */
322 udelay(1500);
323
324 oldlevel = disable_irq_save();
325
326 if (!dvfs_running)
327 {
328 dvfs_running = true;
329
330 /* Unmask DVFS interrupt source and enable DVFS. */
331 avic_enable_int(INT_CCM_DVFS, INT_TYPE_IRQ, INT_PRIO_DVFS,
332 CCM_DVFS_HANDLER);
333
334 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_FSVAIM) | CCM_PMCR0_DVFEN;
335 }
336
337 restore_irq(oldlevel);
338
339 logf("DVFS: started");
340}
341
342
343/* Stop the DVFS hardware and return to default frequency */
344static void dvfs_stop(void)
32{ 345{
33 /* For now, just set the regulator voltage off of overdrive mode */ 346 int oldlevel = disable_irq_save();
34 /* For 264 MHz, DPTC is not needed and lower V can be used */ 347
348 if (dvfs_running)
349 {
350 /* Mask DVFS interrupts. */
351 CCM_PMCR0 |= CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI;
352 avic_disable_int(INT_CCM_DVFS);
353
354 if (((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS) !=
355 DVFS_LEVEL_DEFAULT)
356 {
357 /* Set default frequency level */
358 wait_for_dvfs_update_en();
359 do_dvfs_update(DVFS_LEVEL_DEFAULT);
360 wait_for_dvfs_update_en();
361 }
362
363 /* Disable DVFS. */
364 CCM_PMCR0 &= ~CCM_PMCR0_DVFEN;
365 dvfs_running = false;
366 }
367
368 restore_irq(oldlevel);
369
370 logf("DVFS: stopped");
371}
372
373
374/** DPTC **/
375
376/* Request tracking since boot */
377unsigned int dptc_nr_dn = 0;
378unsigned int dptc_nr_up = 0;
379unsigned int dptc_nr_pnc = 0;
380
381
382/* Enable DPTC and unmask interrupt. */
383static void enable_dptc(void)
384{
385 int oldlevel = disable_irq_save();
386
387 /* Enable DPTC, assert voltage change request. */
388 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_PTVAIM) | CCM_PMCR0_DPTEN |
389 CCM_PMCR0_DPVCR;
390
391 udelay(2);
392
393 /* Set voltage valid *after* setting change request */
394 CCM_PMCR0 |= CCM_PMCR0_DPVV;
395
396 restore_irq(oldlevel);
397}
398
399
400static void dptc_new_wp(unsigned int wp)
401{
402 unsigned int level = dvfs_level;
403 const union dvfs_dptc_voltage_table_entry *entry = &dvfs_dptc_voltage_table[wp];
404
405 uint32_t sw1a = check_regulator_setting(entry->sw1a);
406 uint32_t sw1advs = check_regulator_setting(entry->sw1advs);
407 uint32_t sw1bdvs = check_regulator_setting(entry->sw1bdvs);
408 uint32_t sw1bstby = check_regulator_setting(entry->sw1bstby);
409
410 dptc_wp = wp;
35 411
36 mc13783_write_masked(MC13783_SWITCHERS0, 412 mc13783_write_masked(MC13783_SWITCHERS0,
37 MC13783_SW_1_350 << MC13783_SW1A_POS, 413 sw1a << MC13783_SW1A_POS | /* SW1A */
38 MC13783_SW1A); 414 sw1advs << MC13783_SW1ADVS_POS, /* SW1ADVS */
39 imx31_regmod32(&CCM_PMCR0, CCM_PMCR0_DVS1_0_DVS0_0, 415 MC13783_SW1A | MC13783_SW1ADVS);
40 CCM_PMCR0_DVSUP_DVS); 416
417 mc13783_write_masked(MC13783_SWITCHERS1,
418 sw1bdvs << MC13783_SW1BDVS_POS | /* SW1BDVS */
419 sw1bstby << MC13783_SW1BSTBY_POS, /* SW1BSTBY */
420 MC13783_SW1BDVS | MC13783_SW1BSTBY);
421
422
423 udelay(100); /* Wait to settle */
424
425 update_dptc_counts(level, wp);
426}
427
428
429/* DPTC service thread */
430#ifdef ROCKBOX_HAS_LOGF
431#define DPTC_STACK_SIZE DEFAULT_STACK_SIZE
432#else
433#define DPTC_STACK_SIZE 160
434#endif
435static int dptc_thread_stack[DPTC_STACK_SIZE/sizeof(int)];
436static const char * const dptc_thread_name = "dptc";
437static struct wakeup dptc_wakeup; /* Object to signal upon DPTC event */
438static struct mutex dptc_mutex; /* Avoid mutually disrupting voltage updates */
439static unsigned long dptc_int_data; /* Data passed to thread for each event */
440static bool dptc_running = false; /* Has driver enabled DPTC? */
441
442
443static void dptc_interrupt_thread(void)
444{
445 int wp;
446
447 mutex_lock(&dptc_mutex);
448
449 while (1)
450 {
451 mutex_unlock(&dptc_mutex);
452
453 wakeup_wait(&dptc_wakeup, TIMEOUT_BLOCK);
454
455 mutex_lock(&dptc_mutex);
456
457 if (!dptc_running)
458 continue;
459
460 wp = dptc_wp;
461
462 switch (dptc_int_data & CCM_PMCR0_PTVAI)
463 {
464 case CCM_PMCR0_PTVAI_DECREASE:
465 wp++;
466 dptc_nr_dn++;
467 break;
468
469 case CCM_PMCR0_PTVAI_INCREASE:
470 wp--;
471 dptc_nr_up++;
472 break;
473
474 case CCM_PMCR0_PTVAI_INCREASE_NOW:
475 wp = DPTC_WP_PANIC;
476 dptc_nr_pnc++;
477 break;
478
479 case CCM_PMCR0_PTVAI_NO_INT:
480 logf("DPTC: unexpected INT");
481 continue;
482 }
483
484 if (wp < 0)
485 {
486 wp = 0;
487 logf("DPTC: already @ highest (%d)", wp);
488 }
489 else if (wp >= DPTC_NUM_WP)
490 {
491 wp = DPTC_NUM_WP - 1;
492 logf("DPTC: already @ lowest (%d)", wp);
493 }
494 else
495 {
496 logf("DPTC: new wp (%d)", wp);
497 }
498
499 dptc_new_wp(wp);
500 enable_dptc();
501 }
502}
503
504
505/* Interrupt vector for DPTC */
506static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void)
507{
508 /* Snapshot the interrupt cause */
509 unsigned long pmcr0 = CCM_PMCR0;
510 dptc_int_data = pmcr0;
511
512 /* Mask DPTC interrupt and disable DPTC until the change request is
513 * serviced. */
514 CCM_PMCR0 = (pmcr0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
515
516 wakeup_signal(&dptc_wakeup);
517}
518
519
520/* Initialize the DPTC hardware */
521static void dptc_init(void)
522{
523 /* Force DPTC off if running for some reason. */
524 imx31_regmod32(&CCM_PMCR0, CCM_PMCR0_PTVAIM,
525 CCM_PMCR0_PTVAIM | CCM_PMCR0_DPTEN);
526
527 /* Set default, safe working point. */
528 dptc_new_wp(DPTC_WP_DEFAULT);
529
530 /* Interrupt goes to MCU, specified reference circuits enabled when
531 * DPTC is active. */
532 imx31_regset32(&CCM_PMCR0, CCM_PMCR0_PTVIS | DPTC_DRCE_MASK);
533
534 /* DPTC counting range = 256 system clocks */
535 imx31_regclr32(&CCM_PMCR0, CCM_PMCR0_DCR);
536
537 /* Create PMIC regulator service. */
538 wakeup_init(&dptc_wakeup);
539 mutex_init(&dptc_mutex);
540 create_thread(dptc_interrupt_thread,
541 dptc_thread_stack, sizeof(dptc_thread_stack), 0,
542 dptc_thread_name IF_PRIO(, PRIORITY_REALTIME_1) IF_COP(, CPU));
543
544 logf("DPTC: Initialized");
41} 545}
42 546
43 547
548/* Start DPTC module */
549static void dptc_start(void)
550{
551 int oldstate;
552
553 mutex_lock(&dptc_mutex);
554
555 oldstate = disable_irq_save();
556
557 if (!dptc_running)
558 {
559 dptc_running = true;
560
561 /* Enable DPTC and unmask interrupt. */
562 avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC,
563 CCM_CLK_HANDLER);
564
565 update_dptc_counts(dvfs_level, dptc_wp);
566 enable_dptc();
567 }
568
569 restore_irq(oldstate);
570
571 mutex_unlock(&dptc_mutex);
572
573 logf("DPTC: started");
574}
575
576
577/* Stop the DPTC hardware if running and go back to default working point */
578static void dptc_stop(void)
579{
580 int oldlevel;
581
582 mutex_lock(&dptc_mutex);
583
584 oldlevel = disable_irq_save();
585
586 if (dptc_running)
587 {
588 /* Disable DPTC and mask interrupt. */
589 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
590 avic_disable_int(INT_CCM_CLK);
591 dptc_int_data = 0;
592
593 dptc_running = false;
594 }
595
596 restore_irq(oldlevel);
597
598 /* Go back to default working point. */
599 dptc_new_wp(DPTC_WP_DEFAULT);
600
601 mutex_unlock(&dptc_mutex);
602
603 logf("DPTC: stopped");
604}
605
606
607/** Main module interface **/
608
609/* Initialize DVFS and DPTC */
610void dvfs_dptc_init(void)
611{
612 dptc_init();
613 dvfs_init();
614}
615
616
617/* Start DVFS and DPTC */
618void dvfs_dptc_start(void)
619{
620 dvfs_start();
621 if (0) /* Hold off for now */
622 {
623 dptc_start();
624 }
625}
626
627
628/* Stop DVFS and DPTC */
44void dvfs_dptc_stop(void) 629void dvfs_dptc_stop(void)
45{ 630{
46 /* Nothing for now */ 631 dptc_stop();
632 dvfs_stop();
47} 633}
48 634
635
636/* Set a signal load tracking weight */
637void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value)
638{
639 volatile unsigned long *reg_p = &CCM_LTR2;
640 unsigned int shift = 3 * index;
641
642 if (index < 9)
643 {
644 reg_p = &CCM_LTR3;
645 shift += 5; /* Bits 7:5, 10:8 ... 31:29 */
646 }
647 else if (index < 16)
648 {
649 shift -= 16; /* Bits 13:11, 16:14 ... 31:29 */
650 }
651
652 imx31_regmod32(reg_p, value << shift, 0x7 << shift);
653}
654
655
656/* Set a signal load detection mode */
657void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge)
658{
659 unsigned long bit = 0;
660
661 if ((unsigned)index < 13)
662 bit = 1ul << (index + 3);
663 else if ((unsigned)index < 16)
664 bit = 1ul << (index + 29);
665
666 imx31_regmod32(&CCM_LTR0, edge ? bit : 0, bit);
667}
668
669
670void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert)
671{
672 if ((unsigned)dvgp <= 3)
673 {
674 unsigned long bit = 1ul << dvgp;
675 imx31_regmod32(&CCM_PMCR1, assert ? bit : 0, bit);
676 }
677}
678
679
680/* Turn the wait-for-interrupt monitoring on or off */
681void dvfs_wfi_monitor(bool on)
682{
683 imx31_regmod32(&CCM_PMCR0, on ? 0 : CCM_PMCR0_WFIM,
684 CCM_PMCR0_WFIM);
685}
686
687
688/* Obtain the current core voltage setting, in millivolts 8-) */
689unsigned int dvfs_dptc_get_voltage(void)
690{
691 unsigned int v;
692
693 int oldlevel = disable_irq_save();
694 v = dvfs_dptc_voltage_table[dptc_wp].sw[dvfs_level];
695 restore_irq(oldlevel);
696
697 /* 25mV steps from 0.900V to 1.675V */
698 return v * 25 + 900;
699}
700
701
702/* Get the current DVFS level */
703unsigned int dvfs_get_level(void)
704{
705 return dvfs_level;
706}
707
708
709/* If DVFS is disabled, set the level explicitly */
710void dvfs_set_level(unsigned int level)
711{
712 int oldlevel = disable_irq_save();
713
714 unsigned int currlevel =
715 (CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS;
716
717 if (!dvfs_running && level < DVFS_NUM_LEVELS && level != currlevel)
718 set_current_dvfs_level(level);
719
720 restore_irq(oldlevel);
721}
722
723
724/* Get the current DPTC working point */
725unsigned int dptc_get_wp(void)
726{
727 return dptc_wp;
728}
729
730
731/* If DPTC is not running, set the working point explicitly */
732void dptc_set_wp(unsigned int wp)
733{
734 mutex_lock(&dptc_mutex);
735
736 if (!dptc_running && wp < DPTC_NUM_WP)
737 {
738 dptc_new_wp(wp);
739 }
740
741 mutex_unlock(&dptc_mutex);
742}
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.h b/firmware/target/arm/imx31/dvfs_dptc-imx31.h
index 8f6f5da98d..2bf6114a11 100644
--- a/firmware/target/arm/imx31/dvfs_dptc-imx31.h
+++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.h
@@ -24,7 +24,106 @@
24#ifndef _DVFS_DPTC_IMX31_H_ 24#ifndef _DVFS_DPTC_IMX31_H_
25#define _DVFS_DPTC_IMX31_H_ 25#define _DVFS_DPTC_IMX31_H_
26 26
27/* DVFS load tracking signals */
28enum DVFS_LT_SIGS
29{
30 DVFS_LT_SIG_M3IF_M0_BUF = 0, /* Hready signal of M3IF's master #0
31 (L2 Cache) */
32 DVFS_LT_SIG_M3IF_M1 = 1, /* Hready signal of M3IF's master #1
33 (L2 Cache) */
34 DVFS_LT_SIG_MBX_MBXCLKGATE = 2, /* Hready signal of M3IF's master #2
35 (MBX) */
36 DVFS_LT_SIG_M3IF_M3 = 3, /* Hready signal of M3IF's master #3
37 (MAX) */
38 DVFS_LT_SIG_M3IF_M4 = 4, /* Hready signal of M3IF's master #4
39 (SDMA) */
40 DVFS_LT_SIG_M3IF_M5 = 5, /* Hready signal of M3IF's master #5
41 (mpeg4_vga_encoder) */
42 DVFS_LT_SIG_M3IF_M6 = 6, /* Hready signal of M3IF's master #6
43 (IPU) */
44 DVFS_LT_SIG_M3IF_M7 = 7, /* Hready signal of M3IF's master #7
45 (IPU) */
46 DVFS_LT_SIG_ARM11_P_IRQ_B_RBT_GATE = 8, /* ARM normal interrupt */
47 DVFS_LT_SIG_ARM11_P_FIQ_B_RBT_GATE = 9, /* ARM fast interrupt */
48 DVFS_LT_SIG_IPI_GPIO1_INT0 = 10, /* Interrupt line from GPIO */
49 DVFS_LT_SIG_IPI_INT_IPU_FUNC = 11, /* Interrupt line from IPU */
50 DVFS_LT_SIG_DVGP0 = 12, /* Software-controllable general-purpose
51 bits from the CCM */
52 DVFS_LT_SIG_DVGP1 = 13, /* Software-controllable general-purpose
53 bits from the CCM */
54 DVFS_LT_SIG_DVGP2 = 14, /* Software-controllable general-purpose
55 bits from the CCM */
56 DVFS_LT_SIG_DVGP3 = 15, /* Software-controllable general-purpose
57 bits from the CCM */
58};
59
60
61enum DVFS_DVGPS
62{
63 DVFS_DVGP_0 = 0,
64 DVFS_DVGP_1,
65 DVFS_DVGP_2,
66 DVFS_DVGP_3,
67};
68
69union dvfs_dptc_voltage_table_entry
70{
71 uint8_t sw[4]; /* Access as array */
72
73 struct
74 {
75 /* Chosen by PMIC pin states */
76 /* when SWxABDVS bit is 1: */
77 /* DVSSWxA DVSSWxB */
78 uint8_t sw1a; /* 0 0 */
79 uint8_t sw1advs; /* 1 0 */
80 uint8_t sw1bdvs; /* 0 1 */
81 uint8_t sw1bstby; /* 1 1 */
82 };
83};
84
85
86struct dptc_dcvr_table_entry
87{
88 uint32_t dcvr0; /* DCVR register values for working point */
89 uint32_t dcvr1;
90 uint32_t dcvr2;
91 uint32_t dcvr3;
92};
93
94
95struct dvfs_clock_table_entry
96{
97 uint32_t pll_val; /* Setting for target PLL */
98 uint32_t pdr_val; /* Post-divider for target setting */
99 uint32_t pll_num : 1; /* 1 = MCU PLL, 0 = Serial PLL */
100 uint32_t vscnt : 3; /* Voltage scaling counter, CKIL delay */
101};
102
103
104struct dvfs_lt_signal_descriptor
105{
106 uint8_t weight : 3; /* Signal weight = 0-7 */
107 uint8_t detect : 1; /* 1 = edge-detected */
108};
109
110
111extern long cpu_voltage_setting;
112
113void dvfs_dptc_init(void);
27void dvfs_dptc_start(void); 114void dvfs_dptc_start(void);
28void dvfs_dptc_stop(void); 115void dvfs_dptc_stop(void);
29 116
117void dvfs_wfi_monitor(bool on);
118void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value);
119void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge);
120void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert);
121
122unsigned int dvfs_dptc_get_voltage(void);
123unsigned int dvfs_get_level(void);
124void dvfs_set_level(unsigned int level);
125
126unsigned int dptc_get_wp(void);
127void dptc_set_wp(unsigned int wp);
128
30#endif /* _DVFS_DPTC_IMX31_H_ */ 129#endif /* _DVFS_DPTC_IMX31_H_ */
diff --git a/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h b/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h
new file mode 100644
index 0000000000..2356e23252
--- /dev/null
+++ b/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h
@@ -0,0 +1,279 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 by Michael Sevakis
11 *
12 * Target-specific i.MX31 DVFS and DPTC driver declarations
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#ifndef _DVFS_DPTC_TARGET_H_
24#define _DVFS_DPTC_TARGET_H_
25
26#define DVFS_LEVEL_DEFAULT 1 /* 264 MHz - safe frequency for 1.35V */
27#define DVFS_NUM_LEVELS 3 /* 528 MHz, 264 MHz, 132 MHz */
28#define DVFS_NO_PWRRDY /* PWRRDY is connected to different SoC port */
29
30#define DPTC_WP_DEFAULT 1 /* 1.600, 1.350, 1.350 */
31#define DPTC_WP_PANIC 3 /* Up to minimum for > 400 MHz */
32#define DPTC_NUM_WP 17
33
34#define VOLTAGE_SETTING_MIN MC13783_SW_1_350
35#define VOLTAGE_SETTING_MAX MC13783_SW_1_625
36
37/* Frequency increase threshold. Increase frequency change request
38 * will be sent if DVFS counter value will be more than this value. */
39#define DVFS_UPTHR 30
40
41/* Frequency decrease threshold. Decrease frequency change request
42 * will be sent if DVFS counter value will be less than this value. */
43#define DVFS_DNTHR 18
44
45/* Panic threshold. Panic frequency change request
46 * will be sent if DVFS counter value will be more than this value. */
47#define DVFS_PNCTHR 63
48
49/* With the ARM clocked at 532, this setting yields a DIV_3_CLK of 2.03 kHz.
50 *
51 * Note: To get said clock, the divider would have to be 262144. The values
52 * and their meanings are not published in the reference manual for i.MX31
53 * but show up in the i.MX35 reference manual. Either that chip is different
54 * and the values have an additional division or the comments in the BSP are
55 * incorrect.
56 */
57#define DVFS_DIV3CK CCM_LTR0_DIV3CK_131072
58
59/* UPCNT defines the amount of times the up threshold should be exceeded
60 * before DVFS will trigger frequency increase request. */
61
62#if 0
63/* Freescale BSP value: a bit too agressive IMHO */
64#define DVFS_UPCNT 0x33
65#endif
66#define DVFS_UPCNT 0x48
67
68/* DNCNT defines the amount of times the down threshold should be undershot
69 * before DVFS will trigger frequency decrease request. */
70#define DVFS_DNCNT 0x33
71
72/* EMAC defines how many samples are included in EMA calculation */
73#define DVFS_EMAC 0x20
74
75/* Define mask of which reference circuits are employed for DPTC */
76#define DPTC_DRCE_MASK (CCM_PMCR0_DRCE1 | CCM_PMCR0_DRCE3)
77
78/* When panicing, this working point is used */
79#define DPTC_PANIC_WP
80
81/* Due to a hardware bug in chip revisions < 2.0, when switching between
82 * Serial and MCU PLLs, DVFS forces the target PLL to go into reset and
83 * relock, only post divider frequency scaling is possible.
84 */
85
86static const union dvfs_dptc_voltage_table_entry
87dvfs_dptc_voltage_table[DPTC_NUM_WP] =
88{
89 /* For each working point, there are four DVFS settings, chosen by the
90 * DVS pin states on the PMIC set by the DVFS routines. Pins are reversed
91 * and actual order as used by PMIC for DVSUP values of 00, 01, 10 and 11
92 * is below.
93 *
94 * SW1A SW1ADVS SW1BDVS SW1BSTBY
95 * 0 2 1 3 */
96 { { MC13783_SW_1_625, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
97 { { MC13783_SW_1_600, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
98 { { MC13783_SW_1_575, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
99 { { MC13783_SW_1_550, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
100 { { MC13783_SW_1_525, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
101 { { MC13783_SW_1_500, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
102 { { MC13783_SW_1_475, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
103 { { MC13783_SW_1_450, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
104 { { MC13783_SW_1_425, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
105 { { MC13783_SW_1_400, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
106 { { MC13783_SW_1_375, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
107 { { MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
108 { { MC13783_SW_1_325, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
109 { { MC13783_SW_1_300, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
110 { { MC13783_SW_1_275, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
111 { { MC13783_SW_1_250, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
112 { { MC13783_SW_1_225, MC13783_SW_1_350, MC13783_SW_1_350, MC13783_SW_1_350 } },
113};
114
115#if 1
116/* For 27 MHz PLL reference clock */
117static const struct dptc_dcvr_table_entry
118dptc_dcvr_table[DVFS_NUM_LEVELS][DPTC_NUM_WP] =
119{
120 /* DCVR0 DCVR1 DCVR2 DCVR3 */
121 { /* 528 MHz */
122 { 0xffc00000, 0x90400000, 0xffc00000, 0xdd000000 },
123 { 0xffc00000, 0x90629890, 0xffc00000, 0xdd34ed20 },
124 { 0xffc00000, 0x90629890, 0xffc00000, 0xdd34ed20 },
125 { 0xffc00000, 0x90629894, 0xffc00000, 0xdd74fd24 },
126 { 0xffc00000, 0x90a2a894, 0xffc00000, 0xddb50d28 },
127 { 0xffc00000, 0x90e2b89c, 0xffc00000, 0xde352d30 },
128 { 0xffc00000, 0x9162d8a0, 0xffc00000, 0xdef55d38 },
129 { 0xffc00000, 0x91e2f8a8, 0xffc00000, 0xdfb58d44 },
130 { 0xffc00000, 0x926308b0, 0xffc00000, 0xe0b5cd54 },
131 { 0xffc00000, 0x92e328bc, 0xffc00000, 0xe1f60d64 },
132 { 0xffc00000, 0x93a358c0, 0xffc00000, 0xe3365d74 },
133 { 0xffc00000, 0xf66388cc, 0xffc00000, 0xf6768d84 },
134 { 0xffc00000, 0xf663b8d4, 0xffc00000, 0xf676dd98 },
135 { 0xffc00000, 0xf663e8e0, 0xffc00000, 0xf6773da4 },
136 { 0xffc00000, 0xf66418ec, 0xffc00000, 0xf6778dbc },
137 { 0xffc00000, 0xf66458fc, 0xffc00000, 0xf677edd0 },
138 { 0xffc00000, 0xf6648908, 0xffc00000, 0xf6783de8 },
139
140 },
141 { /* 264 MHz */
142 { 0xffc00000, 0x90400000, 0xffc00000, 0xdd000000 },
143 { 0xffc00000, 0x9048a224, 0xffc00000, 0xdd0d4348 },
144 { 0xffc00000, 0x9048a224, 0xffc00000, 0xdd0d4348 },
145 { 0xffc00000, 0x9048a224, 0xffc00000, 0xdd4d4348 },
146 { 0xffc00000, 0x9088b228, 0xffc00000, 0xdd8d434c },
147 { 0xffc00000, 0x90c8b228, 0xffc00000, 0xde0d534c },
148 { 0xffc00000, 0x9148b228, 0xffc00000, 0xdecd5350 },
149 { 0xffc00000, 0x91c8c22c, 0xffc00000, 0xdf8d6354 },
150 { 0xffc00000, 0x9248d22c, 0xffc00000, 0xe08d7354 },
151 { 0xffc00000, 0x92c8d230, 0xffc00000, 0xe1cd8358 },
152 { 0xffc00000, 0x9388e234, 0xffc00000, 0xe30d935c },
153 { 0xffc00000, 0xf648e234, 0xffc00000, 0xf64db364 },
154 { 0xffc00000, 0xf648f238, 0xffc00000, 0xf64dc368 },
155 { 0xffc00000, 0xf648f23c, 0xffc00000, 0xf64dd36c },
156 { 0xffc00000, 0xf649023c, 0xffc00000, 0xf64de370 },
157 { 0xffc00000, 0xf649123c, 0xffc00000, 0xf64df374 },
158 { 0xffc00000, 0xf6492240, 0xffc00000, 0xf64e1378 },
159
160 },
161 { /* 132 MHz */
162 { 0xffc00000, 0x90400000, 0xffc00000, 0xdd000000 },
163 { 0xffc00000, 0x9048a224, 0xffc00000, 0xdd0d4348 },
164 { 0xffc00000, 0x9048a224, 0xffc00000, 0xdd0d4348 },
165 { 0xffc00000, 0x9048a224, 0xffc00000, 0xdd4d4348 },
166 { 0xffc00000, 0x9088b228, 0xffc00000, 0xdd8d434c },
167 { 0xffc00000, 0x90c8b228, 0xffc00000, 0xde0d534c },
168 { 0xffc00000, 0x9148b228, 0xffc00000, 0xdecd5350 },
169 { 0xffc00000, 0x91c8c22c, 0xffc00000, 0xdf8d6354 },
170 { 0xffc00000, 0x9248d22c, 0xffc00000, 0xe08d7354 },
171 { 0xffc00000, 0x92c8d230, 0xffc00000, 0xe1cd8358 },
172 { 0xffc00000, 0x9388e234, 0xffc00000, 0xe30d935c },
173 { 0xffc00000, 0xf648e234, 0xffc00000, 0xf64db364 },
174 { 0xffc00000, 0xf648f238, 0xffc00000, 0xf64dc368 },
175 { 0xffc00000, 0xf648f23c, 0xffc00000, 0xf64dd36c },
176 { 0xffc00000, 0xf649023c, 0xffc00000, 0xf64de370 },
177 { 0xffc00000, 0xf649123c, 0xffc00000, 0xf64df374 },
178 { 0xffc00000, 0xf6492240, 0xffc00000, 0xf64e1378 },
179 },
180};
181#else/* For 26 MHz PLL reference clock */
182static const struct dptc_dcvr_table_entry
183dptc_dcvr_table[DVFS_NUM_LEVELS][DPTC_NUM_WP] =
184{
185 /* DCVR0 DCVR1 DCVR2 DCVR3 */
186 { /* 528 MHz */
187 { 0xffc00000, 0x95c00000, 0xffc00000, 0xe5800000 },
188 { 0xffc00000, 0x95e3e8e4, 0xffc00000, 0xe5b6fda0 },
189 { 0xffc00000, 0x95e3e8e4, 0xffc00000, 0xe5b6fda0 },
190 { 0xffc00000, 0x95e3e8e8, 0xffc00000, 0xe5f70da4 },
191 { 0xffc00000, 0x9623f8e8, 0xffc00000, 0xe6371da8 },
192 { 0xffc00000, 0x966408f0, 0xffc00000, 0xe6b73db0 },
193 { 0xffc00000, 0x96e428f4, 0xffc00000, 0xe7776dbc },
194 { 0xffc00000, 0x976448fc, 0xffc00000, 0xe8379dc8 },
195 { 0xffc00000, 0x97e46904, 0xffc00000, 0xe977ddd8 },
196 { 0xffc00000, 0x98a48910, 0xffc00000, 0xeab81de8 },
197 { 0xffc00000, 0x9964b918, 0xffc00000, 0xebf86df8 },
198 { 0xffc00000, 0xffe4e924, 0xffc00000, 0xfff8ae08 },
199 { 0xffc00000, 0xffe5192c, 0xffc00000, 0xfff8fe1c },
200 { 0xffc00000, 0xffe54938, 0xffc00000, 0xfff95e2c },
201 { 0xffc00000, 0xffe57944, 0xffc00000, 0xfff9ae44 },
202 { 0xffc00000, 0xffe5b954, 0xffc00000, 0xfffa0e58 },
203 { 0xffc00000, 0xffe5e960, 0xffc00000, 0xfffa6e70 },
204 },
205 { /* 264 MHz */
206 { 0xffc00000, 0x95c00000, 0xffc00000, 0xe5800000 },
207 { 0xffc00000, 0x95c8f238, 0xffc00000, 0xe58dc368 },
208 { 0xffc00000, 0x95c8f238, 0xffc00000, 0xe58dc368 },
209 { 0xffc00000, 0x95c8f238, 0xffc00000, 0xe5cdc368 },
210 { 0xffc00000, 0x9609023c, 0xffc00000, 0xe60dc36c },
211 { 0xffc00000, 0x9649023c, 0xffc00000, 0xe68dd36c },
212 { 0xffc00000, 0x96c9023c, 0xffc00000, 0xe74dd370 },
213 { 0xffc00000, 0x97491240, 0xffc00000, 0xe80de374 },
214 { 0xffc00000, 0x97c92240, 0xffc00000, 0xe94df374 },
215 { 0xffc00000, 0x98892244, 0xffc00000, 0xea8e0378 },
216 { 0xffc00000, 0x99493248, 0xffc00000, 0xebce137c },
217 { 0xffc00000, 0xffc93248, 0xffc00000, 0xffce3384 },
218 { 0xffc00000, 0xffc9424c, 0xffc00000, 0xffce4388 },
219 { 0xffc00000, 0xffc95250, 0xffc00000, 0xffce538c },
220 { 0xffc00000, 0xffc96250, 0xffc00000, 0xffce7390 },
221 { 0xffc00000, 0xffc97254, 0xffc00000, 0xffce8394 },
222 { 0xffc00000, 0xffc98258, 0xffc00000, 0xffcea39c },
223 },
224 { /* 132 MHz */
225 { 0xffc00000, 0x95c00000, 0xffc00000, 0xe5800000 },
226 { 0xffc00000, 0x95c8f238, 0xffc00000, 0xe58dc368 },
227 { 0xffc00000, 0x95c8f238, 0xffc00000, 0xe58dc368 },
228 { 0xffc00000, 0x95c8f238, 0xffc00000, 0xe5cdc368 },
229 { 0xffc00000, 0x9609023c, 0xffc00000, 0xe60dc36c },
230 { 0xffc00000, 0x9649023c, 0xffc00000, 0xe68dd36c },
231 { 0xffc00000, 0x96c9023c, 0xffc00000, 0xe74dd370 },
232 { 0xffc00000, 0x97491240, 0xffc00000, 0xe80de374 },
233 { 0xffc00000, 0x97c92240, 0xffc00000, 0xe94df374 },
234 { 0xffc00000, 0x98892244, 0xffc00000, 0xea8e0378 },
235 { 0xffc00000, 0x99493248, 0xffc00000, 0xebce137c },
236 { 0xffc00000, 0xffc93248, 0xffc00000, 0xffce3384 },
237 { 0xffc00000, 0xffc9424c, 0xffc00000, 0xffce4388 },
238 { 0xffc00000, 0xffc95250, 0xffc00000, 0xffce538c },
239 { 0xffc00000, 0xffc96250, 0xffc00000, 0xffce7390 },
240 { 0xffc00000, 0xffc97254, 0xffc00000, 0xffce8394 },
241 { 0xffc00000, 0xffc98258, 0xffc00000, 0xffcea39c },
242 },
243};
244#endif
245
246
247/* For 27 MHz PLL reference clock */
248static const struct dvfs_clock_table_entry
249dvfs_clock_table[DVFS_NUM_LEVELS] =
250{
251 /* PLL val PDR0 val PLL VSCNT */
252 { 0x00082407, 0xff841e58, 1, 7 }, /* MCUPLL, 528 MHz, /1 = 528 MHz */
253 { 0x00082407, 0xff841e59, 1, 7 }, /* MCUPLL, 528 MHz, /2 = 264 MHz */
254 { 0x00082407, 0xff841e5b, 1, 7 }, /* MCUPLL, 528 MHz, /4 = 132 MHz */
255};
256
257
258/* DVFS load-tracking signal weights and detect modes */
259static const struct dvfs_lt_signal_descriptor lt_signals[16] =
260{
261 { 0, 0 }, /* DVFS_LT_SIG_M3IF_M0_BUF */
262 { 0, 0 }, /* DVFS_LT_SIG_M3IF_M1 */
263 { 0, 0 }, /* DVFS_LT_SIG_MBX_MBXCLKGATE */
264 { 0, 0 }, /* DVFS_LT_SIG_M3IF_M3 */
265 { 0, 0 }, /* DVFS_LT_SIG_M3IF_M4 */
266 { 0, 0 }, /* DVFS_LT_SIG_M3IF_M5 */
267 { 0, 0 }, /* DVFS_LT_SIG_M3IF_M6 */
268 { 0, 0 }, /* DVFS_LT_SIG_M3IF_M7 */
269 { 0, 0 }, /* DVFS_LT_SIG_ARM11_P_IRQ_B_RBT_GATE */
270 { 0, 0 }, /* DVFS_LT_SIG_ARM11_P_FIQ_B_RBT_GATE */
271 { 0, 0 }, /* DVFS_LT_SIG_IPI_GPIO1_INT0 */
272 { 0, 0 }, /* DVFS_LT_SIG_IPI_INT_IPU_FUNC */
273 { 7, 0 }, /* DVFS_LT_SIG_DVGP0 */
274 { 7, 0 }, /* DVFS_LT_SIG_DVGP1 */
275 { 7, 0 }, /* DVFS_LT_SIG_DVGP2 */
276 { 7, 0 }, /* DVFS_LT_SIG_DVGP3 */
277};
278
279#endif /* _DVFS_DPTC_TARGET_H_ */
diff --git a/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c
index 8e81447bd3..894aea4fd3 100644
--- a/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c
@@ -70,10 +70,11 @@ void kernel_device_init(void)
70 sdma_init(); 70 sdma_init();
71 spi_init(); 71 spi_init();
72 mc13783_init(); 72 mc13783_init();
73 dvfs_dptc_start(); 73 dvfs_dptc_init();
74 dvfs_wfi_monitor(true); /* Monitor the WFI signal */
75 dvfs_dptc_start(); /* Should be ok to start even so early */
74} 76}
75 77
76#ifdef BOOTLOADER
77void tick_stop(void) 78void tick_stop(void)
78{ 79{
79 avic_disable_int(INT_EPIT1); /* Disable insterrupt */ 80 avic_disable_int(INT_EPIT1); /* Disable insterrupt */
@@ -81,4 +82,4 @@ void tick_stop(void)
81 EPITSR1 = EPITSR_OCIF; /* Clear pending */ 82 EPITSR1 = EPITSR_OCIF; /* Clear pending */
82 ccm_module_clock_gating(CG_EPIT1, CGM_OFF); /* Turn off module clock */ 83 ccm_module_clock_gating(CG_EPIT1, CGM_OFF); /* Turn off module clock */
83} 84}
84#endif 85
diff --git a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c
index 7e3b39dba8..d7fe87f168 100644
--- a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c
@@ -28,6 +28,7 @@
28#include "backlight-target.h" 28#include "backlight-target.h"
29#include "avic-imx31.h" 29#include "avic-imx31.h"
30#include "mc13783.h" 30#include "mc13783.h"
31#include "dvfs_dptc-imx31.h"
31#if CONFIG_TUNER 32#if CONFIG_TUNER
32#include "fmradio_i2c.h" 33#include "fmradio_i2c.h"
33#endif 34#endif
@@ -121,6 +122,9 @@ bool tuner_powered(void)
121 122
122void power_off(void) 123void power_off(void)
123{ 124{
125 /* Turn off voltage and frequency scaling */
126 dvfs_dptc_stop();
127
124 /* Cut backlight */ 128 /* Cut backlight */
125 _backlight_off(); 129 _backlight_off();
126 130
@@ -131,9 +135,7 @@ void power_off(void)
131 mc13783_set(MC13783_POWER_CONTROL0, MC13783_USEROFFSPI); 135 mc13783_set(MC13783_POWER_CONTROL0, MC13783_USEROFFSPI);
132 136
133 /* Wait for power cut */ 137 /* Wait for power cut */
134 disable_interrupt(IRQ_FIQ_STATUS); 138 system_halt();
135
136 while (1);
137} 139}
138 140
139void power_init(void) 141void power_init(void)
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
index cd684e77ac..7c0d30c783 100644
--- a/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
@@ -26,6 +26,7 @@
26#include "gpio-imx31.h" 26#include "gpio-imx31.h"
27#include "mmu-imx31.h" 27#include "mmu-imx31.h"
28#include "system-target.h" 28#include "system-target.h"
29#include "powermgmt-target.h"
29#include "lcd.h" 30#include "lcd.h"
30#include "serial-imx31.h" 31#include "serial-imx31.h"
31#include "debug.h" 32#include "debug.h"
@@ -115,18 +116,24 @@ int system_memory_guard(int newmode)
115 return 0; 116 return 0;
116} 117}
117 118
119void system_halt(void)
120{
121 disable_interrupt(IRQ_FIQ_STATUS);
122 avic_set_ni_level(AVIC_NIL_DISABLE);
123 while (1)
124 core_idle();
125}
126
118void system_reboot(void) 127void system_reboot(void)
119{ 128{
120 /* Multi-context so no SPI available (WDT?) */ 129 /* Multi-context so no SPI available (WDT?) */
121 while (1); 130 system_halt();
122} 131}
123 132
124void system_exception_wait(void) 133void system_exception_wait(void)
125{ 134{
126 /* Called in many contexts so button reading may be a chore */ 135 /* Called in many contexts so button reading may be a chore */
127 avic_disable_int(INT_ALL); 136 system_halt();
128 core_idle();
129 while (1);
130} 137}
131 138
132void system_init(void) 139void system_init(void)
@@ -175,6 +182,9 @@ void system_init(void)
175 182
176 unsigned int i; 183 unsigned int i;
177 184
185 /* Initialize frequency with current */
186 cpu_frequency = ccm_get_mcu_clk();
187
178 /* MCR WFI enables wait mode (CCM_CCMR_LPM_WAIT_MODE = 0) */ 188 /* MCR WFI enables wait mode (CCM_CCMR_LPM_WAIT_MODE = 0) */
179 imx31_regclr32(&CCM_CCMR, CCM_CCMR_LPM); 189 imx31_regclr32(&CCM_CCMR, CCM_CCMR_LPM);
180 190
@@ -239,16 +249,33 @@ void __attribute__((naked)) imx31_regclr32(volatile uint32_t *reg_p,
239 (void)reg_p; (void)mask; 249 (void)reg_p; (void)mask;
240} 250}
241 251
242#ifdef BOOTLOADER 252
243void system_prepare_fw_start(void) 253void system_prepare_fw_start(void)
244{ 254{
245 dvfs_dptc_stop(); 255 dvfs_dptc_stop();
246 disable_interrupt(IRQ_FIQ_STATUS);
247 avic_disable_int(INT_ALL);
248 mc13783_close(); 256 mc13783_close();
249 tick_stop(); 257 tick_stop();
258 disable_interrupt(IRQ_FIQ_STATUS);
259 avic_set_ni_level(AVIC_NIL_DISABLE);
250} 260}
251#endif 261
262
263#ifndef BOOTLOADER
264void rolo_restart_firmware(const unsigned char *source, unsigned char *dest,
265 int length) __attribute__((noreturn));
266
267void __attribute__((noreturn))
268rolo_restart(const unsigned char *source, unsigned char *dest, int length)
269{
270 /* Some housekeeping tasks must be performed for a safe changeover */
271 charging_algorithm_close();
272 system_prepare_fw_start();
273
274 /* Copying routine where new image is run */
275 rolo_restart_firmware(source, dest, length);
276}
277#endif /* BOOTLOADER */
278
252 279
253inline void dumpregs(void) 280inline void dumpregs(void)
254{ 281{
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-target.h b/firmware/target/arm/imx31/gigabeat-s/system-target.h
index b859093c58..af95471db6 100644
--- a/firmware/target/arm/imx31/gigabeat-s/system-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/system-target.h
@@ -24,12 +24,12 @@
24#include "system-arm.h" 24#include "system-arm.h"
25#include "mmu-arm.h" 25#include "mmu-arm.h"
26 26
27#ifndef HAVE_ADJUSTABLE_CPU_FREQ 27/* High enough for most tasks but low enough for reduced voltage */
28/* TODO: implement CPU frequency scaling */ 28#define CPUFREQ_DEFAULT 264000000
29#define CPUFREQ_DEFAULT CPU_FREQ 29/* Still quite powerful, minimum possible frequency */
30#define CPUFREQ_NORMAL CPU_FREQ 30#define CPUFREQ_NORMAL 132000000
31#define CPUFREQ_MAX CPU_FREQ 31/* Overdrive mode */
32#endif 32#define CPUFREQ_MAX 528000000
33 33
34static inline void udelay(unsigned int usecs) 34static inline void udelay(unsigned int usecs)
35{ 35{
@@ -45,10 +45,11 @@ void gpt_stop(void);
45 45
46unsigned int iim_system_rev(void); 46unsigned int iim_system_rev(void);
47 47
48/* Prepare for transition to firmware */ 48/* Prepare for transition to (new) firmware */
49void system_prepare_fw_start(void); 49void system_prepare_fw_start(void);
50void tick_stop(void); 50void tick_stop(void);
51void kernel_device_init(void); 51void kernel_device_init(void);
52void system_halt(void);
52 53
53void imx31_regmod32(volatile uint32_t *reg_p, uint32_t value, 54void imx31_regmod32(volatile uint32_t *reg_p, uint32_t value,
54 uint32_t mask); 55 uint32_t mask);
diff --git a/firmware/target/arm/imx31/mc13783-imx31.c b/firmware/target/arm/imx31/mc13783-imx31.c
index 1c43b3b6fa..a083614488 100644
--- a/firmware/target/arm/imx31/mc13783-imx31.c
+++ b/firmware/target/arm/imx31/mc13783-imx31.c
@@ -26,10 +26,6 @@
26#include "debug.h" 26#include "debug.h"
27#include "kernel.h" 27#include "kernel.h"
28 28
29#ifdef BOOTLOADER
30#define PMIC_DRIVER_CLOSE
31#endif
32
33extern const struct mc13783_event_list mc13783_event_list; 29extern const struct mc13783_event_list mc13783_event_list;
34extern struct spi_node mc13783_spi; 30extern struct spi_node mc13783_spi;
35 31
@@ -47,10 +43,7 @@ static const unsigned char pmic_intm_regs[2] =
47static const unsigned char pmic_ints_regs[2] = 43static const unsigned char pmic_ints_regs[2] =
48 { MC13783_INTERRUPT_STATUS0, MC13783_INTERRUPT_STATUS1 }; 44 { MC13783_INTERRUPT_STATUS0, MC13783_INTERRUPT_STATUS1 };
49 45
50#ifdef PMIC_DRIVER_CLOSE 46static volatile unsigned int mc13783_thread_id = 0;
51static bool pmic_close = false;
52static unsigned int mc13783_thread_id = 0;
53#endif
54 47
55static void mc13783_interrupt_thread(void) 48static void mc13783_interrupt_thread(void)
56{ 49{
@@ -65,10 +58,8 @@ static void mc13783_interrupt_thread(void)
65 58
66 wakeup_wait(&mc13783_wake, TIMEOUT_BLOCK); 59 wakeup_wait(&mc13783_wake, TIMEOUT_BLOCK);
67 60
68#ifdef PMIC_DRIVER_CLOSE 61 if (mc13783_thread_id == 0)
69 if (pmic_close)
70 break; 62 break;
71#endif
72 63
73 mc13783_read_regset(pmic_ints_regs, pending, 2); 64 mc13783_read_regset(pmic_ints_regs, pending, 2);
74 65
@@ -107,9 +98,7 @@ static void mc13783_interrupt_thread(void)
107 while (++event < event_last); 98 while (++event < event_last);
108 } 99 }
109 100
110#ifdef PMIC_DRIVER_CLOSE
111 gpio_disable_event(MC13783_EVENT_ID); 101 gpio_disable_event(MC13783_EVENT_ID);
112#endif
113} 102}
114 103
115/* GPIO interrupt handler for mc13783 */ 104/* GPIO interrupt handler for mc13783 */
@@ -136,15 +125,12 @@ void mc13783_init(void)
136 125
137 MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); 126 MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE);
138 127
139#ifdef PMIC_DRIVER_CLOSE
140 mc13783_thread_id = 128 mc13783_thread_id =
141#endif
142 create_thread(mc13783_interrupt_thread, 129 create_thread(mc13783_interrupt_thread,
143 mc13783_thread_stack, sizeof(mc13783_thread_stack), 0, 130 mc13783_thread_stack, sizeof(mc13783_thread_stack), 0,
144 mc13783_thread_name IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); 131 mc13783_thread_name IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
145} 132}
146 133
147#ifdef PMIC_DRIVER_CLOSE
148void mc13783_close(void) 134void mc13783_close(void)
149{ 135{
150 unsigned int thread_id = mc13783_thread_id; 136 unsigned int thread_id = mc13783_thread_id;
@@ -153,12 +139,9 @@ void mc13783_close(void)
153 return; 139 return;
154 140
155 mc13783_thread_id = 0; 141 mc13783_thread_id = 0;
156
157 pmic_close = true;
158 wakeup_signal(&mc13783_wake); 142 wakeup_signal(&mc13783_wake);
159 thread_wait(thread_id); 143 thread_wait(thread_id);
160} 144}
161#endif /* PMIC_DRIVER_CLOSE */
162 145
163bool mc13783_enable_event(enum mc13783_event_ids id) 146bool mc13783_enable_event(enum mc13783_event_ids id)
164{ 147{
diff --git a/firmware/target/arm/imx31/rolo_restart.S b/firmware/target/arm/imx31/rolo_restart_firmware.S
index 902f513f62..5f24f653e0 100644
--- a/firmware/target/arm/imx31/rolo_restart.S
+++ b/firmware/target/arm/imx31/rolo_restart_firmware.S
@@ -9,7 +9,7 @@
9 * 9 *
10 * Copyright (C) 2008 by Michael Sevakis 10 * Copyright (C) 2008 by Michael Sevakis
11 * 11 *
12 * RoLo restart code for IMX31 12 * RoLo firmware restart code for IMX31
13 * 13 *
14 * This program is free software; you can redistribute it and/or 14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License 15 * modify it under the terms of the GNU General Public License
@@ -24,13 +24,13 @@
24#include "cpu.h" 24#include "cpu.h"
25 25
26/**************************************************************************** 26/****************************************************************************
27 * void rolo_restart(const unsigned char* source, unsigned char* dest, 27 * void rolo_restart_firmware(const unsigned char* source, unsigned char* dest,
28 * int length) __attribute__((noreturn)); 28 * int length) __attribute__((noreturn));
29 */ 29 */
30 .section .text, "ax", %progbits 30 .section .text, "ax", %progbits
31 .align 2 31 .align 2
32 .global rolo_restart 32 .global rolo_restart_firmware
33rolo_restart: 33rolo_restart_firmware:
34 adr r4, restart_copy_start 34 adr r4, restart_copy_start
35 adr r5, restart_copy_end 35 adr r5, restart_copy_end
36 ldr r6, =IRAM_BASE_ADDR 36 ldr r6, =IRAM_BASE_ADDR
diff --git a/firmware/target/arm/imx31/sdma-imx31.c b/firmware/target/arm/imx31/sdma-imx31.c
index 6f715ebb3f..40a43f8121 100644
--- a/firmware/target/arm/imx31/sdma-imx31.c
+++ b/firmware/target/arm/imx31/sdma-imx31.c
@@ -533,7 +533,7 @@ void sdma_init(void)
533 /* 32-word channel contexts, use default bootscript address */ 533 /* 32-word channel contexts, use default bootscript address */
534 SDMA_CHN0ADDR = SDMA_CHN0ADDR_SMSZ | 0x0050; 534 SDMA_CHN0ADDR = SDMA_CHN0ADDR_SMSZ | 0x0050;
535 535
536 avic_enable_int(INT_SDMA, INT_TYPE_IRQ, INT_PRIO_DEFAULT+1, SDMA_HANDLER); 536 avic_enable_int(INT_SDMA, INT_TYPE_IRQ, INT_PRIO_SDMA, SDMA_HANDLER);
537 537
538 /* SDMA core must run at the proper frequency based upon the AHB/IPG 538 /* SDMA core must run at the proper frequency based upon the AHB/IPG
539 * ratio */ 539 * ratio */