diff options
Diffstat (limited to 'firmware/target/arm/pp')
22 files changed, 6639 insertions, 0 deletions
diff --git a/firmware/target/arm/pp/adc-pp5020.c b/firmware/target/arm/pp/adc-pp5020.c new file mode 100644 index 0000000000..2d75a7e25f --- /dev/null +++ b/firmware/target/arm/pp/adc-pp5020.c | |||
@@ -0,0 +1,179 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Barry Wardell | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include "cpu.h" | ||
23 | #include "system.h" | ||
24 | #include "kernel.h" | ||
25 | #include "thread.h" | ||
26 | #include "adc.h" | ||
27 | |||
28 | #define ADC_ADDR (*(volatile unsigned long*)(0x7000ad00)) | ||
29 | #define ADC_STATUS (*(volatile unsigned long*)(0x7000ad04)) | ||
30 | #define ADC_DATA_1 (*(volatile unsigned long*)(0x7000ad20)) | ||
31 | #define ADC_DATA_2 (*(volatile unsigned long*)(0x7000ad24)) | ||
32 | #define ADC_INIT (*(volatile unsigned long*)(0x7000ad2c)) | ||
33 | |||
34 | #if defined(PBELL_VIBE500) | ||
35 | #define ADC_UNK (*(volatile unsigned long*)(0x7000002c)) | ||
36 | #endif | ||
37 | |||
38 | static unsigned short adcdata[NUM_ADC_CHANNELS]; | ||
39 | |||
40 | /* Scan ADC so that adcdata[channel] gets updated. */ | ||
41 | unsigned short adc_scan(int channel) | ||
42 | { | ||
43 | unsigned int adc_data_1; | ||
44 | unsigned int adc_data_2; | ||
45 | |||
46 | if (channel >= NUM_ADC_CHANNELS) | ||
47 | return 0; | ||
48 | |||
49 | /* Start conversion */ | ||
50 | ADC_ADDR |= 0x80000000; | ||
51 | |||
52 | /* Wait for conversion to complete */ | ||
53 | while((ADC_STATUS & (0x40<<8*channel))==0); | ||
54 | |||
55 | /* Stop conversion */ | ||
56 | ADC_ADDR &=~ 0x80000000; | ||
57 | |||
58 | /* ADC_DATA_1 and ADC_DATA_2 are both four bytes, one byte per channel. | ||
59 | For each channel, ADC_DATA_1 stores the 8-bit msb, ADC_DATA_2 stores the | ||
60 | 2-bit lsb (in bits 0 and 1). Each channel is 10 bits total. */ | ||
61 | adc_data_1 = ((ADC_DATA_1 >> (8*channel)) & 0xff); | ||
62 | adc_data_2 = ((ADC_DATA_2 >> (8*channel+6)) & 0x3); | ||
63 | |||
64 | adcdata[channel] = (adc_data_1<<2 | adc_data_2); | ||
65 | |||
66 | #if !(defined(PHILIPS_HDD1630) || defined(PHILIPS_HDD6330)) | ||
67 | /* ADC values read low if PLL is enabled */ | ||
68 | if(PLL_CONTROL & 0x80000000){ | ||
69 | adcdata[channel] += 0x14; | ||
70 | if(adcdata[channel] > 0x400) | ||
71 | adcdata[channel] = 0x400; | ||
72 | } | ||
73 | #endif | ||
74 | |||
75 | return adcdata[channel]; | ||
76 | } | ||
77 | |||
78 | /* Read 10-bit channel data */ | ||
79 | unsigned short adc_read(int channel) | ||
80 | { | ||
81 | return adcdata[channel]; | ||
82 | } | ||
83 | |||
84 | static int adc_counter; | ||
85 | |||
86 | static void adc_tick(void) | ||
87 | { | ||
88 | if(++adc_counter == HZ) | ||
89 | { | ||
90 | adc_counter = 0; | ||
91 | adc_scan(0); | ||
92 | adc_scan(1); | ||
93 | adc_scan(2); | ||
94 | adc_scan(3); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | /* Figured out from how the OF does things */ | ||
99 | void adc_init(void) | ||
100 | { | ||
101 | #if defined(PBELL_VIBE500) | ||
102 | ADC_UNK |= 0x1000; | ||
103 | #endif | ||
104 | |||
105 | #if defined(PHILIPS_HDD1630) || defined(PHILIPS_HDD6330) | ||
106 | ADC_INIT = 0; | ||
107 | #else | ||
108 | ADC_INIT |= 1; | ||
109 | ADC_INIT |= 0x40000000; | ||
110 | #endif | ||
111 | udelay(100); | ||
112 | |||
113 | /* Reset ADC */ | ||
114 | DEV_RS2 |= 0x20; | ||
115 | udelay(100); | ||
116 | |||
117 | DEV_RS2 &=~ 0x20; | ||
118 | udelay(100); | ||
119 | |||
120 | /* Enable ADC */ | ||
121 | DEV_EN2 |= 0x20; | ||
122 | udelay(100); | ||
123 | |||
124 | ADC_CLOCK_SRC |= 0x3; | ||
125 | udelay(100); | ||
126 | |||
127 | ADC_ADDR = 0; | ||
128 | ADC_ADDR |= 0x40; | ||
129 | |||
130 | #if !(defined(PHILIPS_HDD1630) || defined(PHILIPS_HDD6330)) | ||
131 | ADC_ADDR |= 0x20000000; | ||
132 | udelay(100); | ||
133 | |||
134 | ADC_INIT; | ||
135 | ADC_INIT = 0; | ||
136 | udelay(100); | ||
137 | #endif | ||
138 | |||
139 | ADC_STATUS = 0; | ||
140 | |||
141 | /* Enable channel 0 (battery) */ | ||
142 | DEV_INIT1 &=~0x3; | ||
143 | ADC_ADDR |= 0x1000000; | ||
144 | ADC_STATUS |= 0x20; | ||
145 | |||
146 | /* Enable channel 1 (unknown) */ | ||
147 | DEV_INIT1 &=~30; | ||
148 | ADC_ADDR |= 0x2000000; | ||
149 | ADC_STATUS |= 0x2000; | ||
150 | |||
151 | #if defined (IRIVER_H10) || defined(IRIVER_H10_5GB) || \ | ||
152 | defined(MROBE_100) | ||
153 | /* Enable channel 2 (H10:remote) */ | ||
154 | DEV_INIT1 &=~0x300; | ||
155 | DEV_INIT1 |= 0x100; | ||
156 | ADC_ADDR |= 0x4000000; | ||
157 | ADC_STATUS |= 0x200000; | ||
158 | |||
159 | /* Enable channel 3 (H10:scroll pad) */ | ||
160 | DEV_INIT1 &=~0x3000; | ||
161 | DEV_INIT1 |= 0x1000; | ||
162 | ADC_ADDR |= 0x8000000; | ||
163 | ADC_STATUS |= 0x20000000; | ||
164 | #endif | ||
165 | |||
166 | #if defined(PHILIPS_HDD1630) || defined(PHILIPS_HDD6330) | ||
167 | ADC_INIT |= 0x80000000; | ||
168 | udelay(100); | ||
169 | ADC_INIT = 0; | ||
170 | #endif | ||
171 | |||
172 | /* Force a scan of all channels to get initial values */ | ||
173 | adc_scan(0); | ||
174 | adc_scan(1); | ||
175 | adc_scan(2); | ||
176 | adc_scan(3); | ||
177 | |||
178 | tick_add_task(adc_tick); | ||
179 | } | ||
diff --git a/firmware/target/arm/pp/ascodec-pp.c b/firmware/target/arm/pp/ascodec-pp.c new file mode 100644 index 0000000000..49a69d1a11 --- /dev/null +++ b/firmware/target/arm/pp/ascodec-pp.c | |||
@@ -0,0 +1,82 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Driver for AS3514 audio codec | ||
11 | * | ||
12 | * Copyright (c) 2007 Daniel Ankers | ||
13 | * Copyright (c) 2007 Christian Gmeiner | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License | ||
17 | * as published by the Free Software Foundation; either version 2 | ||
18 | * of the License, or (at your option) any later version. | ||
19 | * | ||
20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
21 | * KIND, either express or implied. | ||
22 | * | ||
23 | ****************************************************************************/ | ||
24 | #include "cpu.h" | ||
25 | #include "system.h" | ||
26 | |||
27 | #include "audiohw.h" | ||
28 | #include "i2s.h" | ||
29 | #include "ascodec-target.h" | ||
30 | |||
31 | /* | ||
32 | * Initialise the PP I2C and I2S. | ||
33 | */ | ||
34 | void audiohw_init(void) | ||
35 | { | ||
36 | /* normal outputs for CDI and I2S pin groups */ | ||
37 | DEV_INIT2 &= ~0x300; | ||
38 | |||
39 | /*mini2?*/ | ||
40 | DEV_INIT1 &=~0x3000000; | ||
41 | /*mini2?*/ | ||
42 | |||
43 | /* I2S device reset */ | ||
44 | DEV_RS |= DEV_I2S; | ||
45 | DEV_RS &=~DEV_I2S; | ||
46 | |||
47 | /* I2S device enable */ | ||
48 | DEV_EN |= DEV_I2S; | ||
49 | |||
50 | /* enable external dev clock clocks */ | ||
51 | DEV_EN |= DEV_EXTCLOCKS; | ||
52 | |||
53 | /* external dev clock to 24MHz */ | ||
54 | outl(inl(0x70000018) & ~0xc, 0x70000018); | ||
55 | |||
56 | #ifdef SANSA_E200 | ||
57 | /* Prevent pops on startup */ | ||
58 | GPIOG_ENABLE |= 0x08; | ||
59 | GPIO_SET_BITWISE(GPIOG_OUTPUT_VAL, 0x08); | ||
60 | GPIOG_OUTPUT_EN |= 0x08; | ||
61 | #endif | ||
62 | |||
63 | i2s_reset(); | ||
64 | |||
65 | audiohw_preinit(); | ||
66 | } | ||
67 | |||
68 | void ascodec_suppressor_on(bool on) | ||
69 | { | ||
70 | /* CHECK: Good for c200 too? */ | ||
71 | #ifdef SANSA_E200 | ||
72 | if (on) { | ||
73 | /* Set pop prevention */ | ||
74 | GPIO_SET_BITWISE(GPIOG_OUTPUT_VAL, 0x08); | ||
75 | } else { | ||
76 | /* Release pop prevention */ | ||
77 | GPIO_CLEAR_BITWISE(GPIOG_OUTPUT_VAL, 0x08); | ||
78 | } | ||
79 | #else | ||
80 | (void)on; | ||
81 | #endif | ||
82 | } | ||
diff --git a/firmware/target/arm/pp/ata-pp5002.c b/firmware/target/arm/pp/ata-pp5002.c new file mode 100644 index 0000000000..5ab0e9ddc7 --- /dev/null +++ b/firmware/target/arm/pp/ata-pp5002.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Barry Wardell | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | /* ATA stuff was taken from the iPod code */ | ||
23 | |||
24 | #include <stdbool.h> | ||
25 | #include "system.h" | ||
26 | #include "ata-driver.h" | ||
27 | |||
28 | void ata_reset() | ||
29 | { | ||
30 | |||
31 | } | ||
32 | |||
33 | void ata_enable(bool on) | ||
34 | { | ||
35 | /* TODO: Implement ata_enable() */ | ||
36 | (void)on; | ||
37 | } | ||
38 | |||
39 | bool ata_is_coldstart() | ||
40 | { | ||
41 | return false; | ||
42 | /* TODO: Implement coldstart variable */ | ||
43 | } | ||
44 | |||
45 | void ata_device_init() | ||
46 | { | ||
47 | /* From ipod-ide.c:ipod_ide_register() */ | ||
48 | outl(inl(0xc0003024) | (1 << 7), 0xc0003024); | ||
49 | outl(inl(0xc0003024) & ~(1<<2), 0xc0003024); | ||
50 | |||
51 | outl(0x10, 0xc0003000); | ||
52 | outl(0x80002150, 0xc0003004); | ||
53 | } | ||
diff --git a/firmware/target/arm/pp/ata-pp5020.c b/firmware/target/arm/pp/ata-pp5020.c new file mode 100644 index 0000000000..50a38cb23d --- /dev/null +++ b/firmware/target/arm/pp/ata-pp5020.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Barry Wardell | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | /* ATA stuff was taken from the iPod code */ | ||
23 | |||
24 | #include <stdbool.h> | ||
25 | #include "system.h" | ||
26 | #include "ata-driver.h" | ||
27 | |||
28 | void ata_reset() | ||
29 | { | ||
30 | |||
31 | } | ||
32 | |||
33 | void ata_enable(bool on) | ||
34 | { | ||
35 | /* TODO: Implement ata_enable() */ | ||
36 | (void)on; | ||
37 | } | ||
38 | |||
39 | bool ata_is_coldstart() | ||
40 | { | ||
41 | return false; | ||
42 | /* TODO: Implement coldstart variable */ | ||
43 | } | ||
44 | |||
45 | void ata_device_init() | ||
46 | { | ||
47 | #ifdef SAMSUNG_YH920 | ||
48 | CPU_INT_DIS = (1<<IDE_IRQ); | ||
49 | #endif | ||
50 | #ifdef HAVE_ATA_DMA | ||
51 | IDE_DMA_CONTROL |= 2; | ||
52 | IDE_DMA_CONTROL &= ~1; | ||
53 | IDE0_CFG &= ~0x8010; | ||
54 | IDE0_CFG |= 0x20; | ||
55 | #else | ||
56 | |||
57 | /* From ipod-ide.c:ipod_ide_register() */ | ||
58 | IDE0_CFG |= (1<<5); | ||
59 | #ifdef IPOD_NANO | ||
60 | IDE0_CFG |= (0x10000000); /* cpu > 65MHz */ | ||
61 | #else | ||
62 | IDE0_CFG &=~(0x10000000); /* cpu < 65MHz */ | ||
63 | #endif | ||
64 | #endif | ||
65 | |||
66 | IDE0_PRI_TIMING0 = 0x10; | ||
67 | IDE0_PRI_TIMING1 = 0x80002150; | ||
68 | } | ||
69 | |||
70 | /* These are PIO timings for 80 Mhz. At 24 Mhz, | ||
71 | the first value is 0 but the rest are the same. | ||
72 | They go in IDE0_PRI_TIMING0. | ||
73 | |||
74 | Rockbox used 0x10, and test_disk shows that leads to faster PIO. | ||
75 | If 0x10 is incorrect, these timings may be needed with some devices. | ||
76 | static const unsigned long pio80mhz[] = { | ||
77 | 0xC293, 0x43A2, 0x11A1, 0x7232, 0x3131 | ||
78 | }; | ||
79 | */ | ||
80 | |||
81 | #ifdef HAVE_ATA_DMA | ||
82 | /* Timings for multi-word and ultra DMA modes. | ||
83 | These go in IDE0_PRI_TIMING1 | ||
84 | */ | ||
85 | static const unsigned long tm_mwdma[] = { | ||
86 | 0xF9F92, 0x56562, 0x45451 | ||
87 | }; | ||
88 | |||
89 | static const unsigned long tm_udma[] = { | ||
90 | 0x800037C1, 0x80003491, 0x80003371, | ||
91 | #if ATA_MAX_UDMA > 2 | ||
92 | 0x80003271, 0x80003071 | ||
93 | #endif | ||
94 | }; | ||
95 | |||
96 | #if ATA_MAX_UDMA > 2 | ||
97 | static bool dma_boosted = false; | ||
98 | static bool dma_needs_boost; | ||
99 | #endif | ||
100 | |||
101 | /* This function sets up registers for 80 Mhz. | ||
102 | Ultra DMA mode 2 works at 30 Mhz. | ||
103 | */ | ||
104 | void ata_dma_set_mode(unsigned char mode) { | ||
105 | int modeidx; | ||
106 | |||
107 | (*(volatile unsigned long *)(0x600060C4)) = 0xC0000000; /* 80 Mhz */ | ||
108 | #if !defined(IPOD_NANO) | ||
109 | IDE0_CFG &= ~0x10000000; | ||
110 | #endif | ||
111 | |||
112 | modeidx = mode & 7; | ||
113 | mode &= 0xF8; | ||
114 | if (mode == 0x40 && modeidx <= ATA_MAX_UDMA) { | ||
115 | IDE0_PRI_TIMING1 = tm_udma[modeidx]; | ||
116 | #if ATA_MAX_UDMA > 2 | ||
117 | if (modeidx > 2) | ||
118 | dma_needs_boost = true; | ||
119 | else | ||
120 | dma_needs_boost = false; | ||
121 | #endif | ||
122 | } else if (mode == 0x20 && modeidx <= ATA_MAX_MWDMA) | ||
123 | IDE0_PRI_TIMING1 = tm_mwdma[modeidx]; | ||
124 | |||
125 | #if !defined(IPOD_NANO) | ||
126 | IDE0_CFG |= 0x20000000; /* >= 50 Mhz */ | ||
127 | #endif | ||
128 | } | ||
129 | |||
130 | #define IDE_CFG_INTRQ 8 | ||
131 | #define IDE_DMA_CONTROL_READ 8 | ||
132 | |||
133 | /* This waits for an ATA interrupt using polling. | ||
134 | In ATA_CONTROL, CONTROL_nIEN must be cleared. | ||
135 | */ | ||
136 | STATICIRAM ICODE_ATTR int ata_wait_intrq(void) | ||
137 | { | ||
138 | long timeout = current_tick + HZ*10; | ||
139 | |||
140 | do | ||
141 | { | ||
142 | if (IDE0_CFG & IDE_CFG_INTRQ) | ||
143 | return 1; | ||
144 | ata_keep_active(); | ||
145 | yield(); | ||
146 | } while (TIME_BEFORE(current_tick, timeout)); | ||
147 | |||
148 | return 0; /* timeout */ | ||
149 | } | ||
150 | |||
151 | /* This function checks if parameters are appropriate for DMA, | ||
152 | and if they are, it sets up for DMA. | ||
153 | |||
154 | If return value is false, caller may use PIO for this transfer. | ||
155 | |||
156 | If return value is true, caller must issue a DMA ATA command | ||
157 | and then call ata_dma_finish(). | ||
158 | */ | ||
159 | bool ata_dma_setup(void *addr, unsigned long bytes, bool write) { | ||
160 | /* Require cacheline alignment for reads to prevent interference. */ | ||
161 | if (!write && ((unsigned long)addr & 15)) | ||
162 | return false; | ||
163 | |||
164 | /* Writes only need to be word-aligned, but by default DMA | ||
165 | * is not used for writing as it appears to be slower. | ||
166 | */ | ||
167 | #ifdef ATA_DMA_WRITES | ||
168 | if (write && ((unsigned long)addr & 3)) | ||
169 | return false; | ||
170 | #else | ||
171 | if (write) | ||
172 | return false; | ||
173 | #endif | ||
174 | |||
175 | #if ATA_MAX_UDMA > 2 | ||
176 | if (dma_needs_boost && !dma_boosted) { | ||
177 | cpu_boost(true); | ||
178 | dma_boosted = true; | ||
179 | } | ||
180 | #endif | ||
181 | |||
182 | if (write) { | ||
183 | /* If unflushed, old data may be written to disk */ | ||
184 | commit_dcache(); | ||
185 | } | ||
186 | else { | ||
187 | /* Invalidate cache because new data may be present in RAM */ | ||
188 | commit_discard_dcache(); | ||
189 | } | ||
190 | |||
191 | /* Clear pending interrupts so ata_dma_finish() can wait for an | ||
192 | interrupt from this transfer | ||
193 | */ | ||
194 | IDE0_CFG |= IDE_CFG_INTRQ; | ||
195 | |||
196 | IDE_DMA_CONTROL |= 2; | ||
197 | IDE_DMA_LENGTH = bytes - 4; | ||
198 | |||
199 | #if !defined(BOOTLOADER) || defined (HAVE_BOOTLOADER_USB_MODE) | ||
200 | if ((unsigned long)addr < DRAM_START) | ||
201 | /* Rockbox remaps DRAM to start at 0 */ | ||
202 | IDE_DMA_ADDR = (unsigned long)addr + DRAM_START; | ||
203 | else | ||
204 | #endif | ||
205 | IDE_DMA_ADDR = (unsigned long)addr; | ||
206 | |||
207 | if (write) | ||
208 | IDE_DMA_CONTROL &= ~IDE_DMA_CONTROL_READ; | ||
209 | else | ||
210 | IDE_DMA_CONTROL |= IDE_DMA_CONTROL_READ; | ||
211 | |||
212 | IDE0_CFG |= 0x8000; | ||
213 | |||
214 | return true; | ||
215 | } | ||
216 | |||
217 | /* This function waits for a DMA transfer to end. | ||
218 | It must be called to finish what ata_dma_setup started. | ||
219 | |||
220 | Return value is true if DMA completed before the timeout, and false | ||
221 | if a timeout happened. | ||
222 | */ | ||
223 | bool ata_dma_finish(void) { | ||
224 | bool res; | ||
225 | |||
226 | /* It may be okay to put this at the end of setup */ | ||
227 | IDE_DMA_CONTROL |= 1; | ||
228 | |||
229 | /* Wait for end of transfer. | ||
230 | Reading standard ATA status while DMA is in progress causes | ||
231 | failures and hangs. Because of that, another wait is used. | ||
232 | */ | ||
233 | res = ata_wait_intrq(); | ||
234 | |||
235 | IDE0_CFG &= ~0x8000; | ||
236 | IDE_DMA_CONTROL &= ~0x80000001; | ||
237 | |||
238 | #if ATA_MAX_UDMA > 2 | ||
239 | if (dma_boosted) { | ||
240 | cpu_boost(false); | ||
241 | dma_boosted = false; | ||
242 | } | ||
243 | #endif | ||
244 | |||
245 | return res; | ||
246 | } | ||
247 | |||
248 | #endif /* HAVE_ATA_DMA */ | ||
diff --git a/firmware/target/arm/pp/ata-sd-pp.c b/firmware/target/arm/pp/ata-sd-pp.c new file mode 100644 index 0000000000..f83bb60566 --- /dev/null +++ b/firmware/target/arm/pp/ata-sd-pp.c | |||
@@ -0,0 +1,1387 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Daniel Ankers | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" /* for HAVE_MULTIDRIVE */ | ||
22 | #include "fat.h" | ||
23 | #include "sdmmc.h" | ||
24 | #include "gcc_extensions.h" | ||
25 | #ifdef HAVE_HOTSWAP | ||
26 | #include "sd-pp-target.h" | ||
27 | #endif | ||
28 | #include "ata_idle_notify.h" | ||
29 | #include "system.h" | ||
30 | #include <string.h> | ||
31 | #include "thread.h" | ||
32 | #include "led.h" | ||
33 | #include "disk.h" | ||
34 | #include "cpu.h" | ||
35 | #include "panic.h" | ||
36 | #include "usb.h" | ||
37 | #include "sd.h" | ||
38 | #include "storage.h" | ||
39 | |||
40 | #define SECTOR_SIZE 512 | ||
41 | #define BLOCKS_PER_BANK 0x7a7800 | ||
42 | |||
43 | /* Comparing documentations of various MMC/SD controllers revealed, */ | ||
44 | /* that this controller seems to be a mix of PXA27x, PXA255 and */ | ||
45 | /* some PP specific stuff. The register and bit definitions are */ | ||
46 | /* taken from the 'PXA27x Developers Manual', as it appears to be */ | ||
47 | /* the closest match. Known differences and obscurities are commented.*/ | ||
48 | |||
49 | #define MMC_STRPCL (*(volatile unsigned int *)(0x70008200)) | ||
50 | #define MMC_STAT (*(volatile unsigned int *)(0x70008204)) | ||
51 | #define MMC_CLKRT (*(volatile unsigned int *)(0x70008208)) | ||
52 | #define MMC_SPI (*(volatile unsigned int *)(0x7000820c)) | ||
53 | #define MMC_CMDAT (*(volatile unsigned int *)(0x70008210)) | ||
54 | #define MMC_RESTO (*(volatile unsigned int *)(0x70008214)) | ||
55 | #define MMC_RDTO (*(volatile unsigned int *)(0x70008218)) | ||
56 | #define MMC_BLKLEN (*(volatile unsigned int *)(0x7000821c)) | ||
57 | #define MMC_NUMBLK (*(volatile unsigned int *)(0x70008220)) | ||
58 | #define MMC_I_MASK (*(volatile unsigned int *)(0x70008224)) | ||
59 | #define MMC_CMD (*(volatile unsigned int *)(0x70008228)) | ||
60 | #define MMC_ARGH (*(volatile unsigned int *)(0x7000822c)) | ||
61 | #define MMC_ARGL (*(volatile unsigned int *)(0x70008230)) | ||
62 | #define MMC_RES (*(volatile unsigned int *)(0x70008234)) | ||
63 | |||
64 | /* PXA255/27x have separate RX/TX FIFOs with 32x8 bit */ | ||
65 | /* PP502x has a combined Data FIFO with 16x16 bit */ | ||
66 | #define MMC_DATA_FIFO (*(volatile unsigned int *)(0x70008280)) | ||
67 | |||
68 | /* PP specific registers, no other controller seem to have such. */ | ||
69 | #define MMC_SD_STATE (*(volatile unsigned int *)(0x70008238)) | ||
70 | #define MMC_INIT_1 (*(volatile unsigned int *)(0x70008240)) | ||
71 | #define MMC_INIT_2 (*(volatile unsigned int *)(0x70008244)) | ||
72 | |||
73 | /* MMC_STAT bits */ | ||
74 | #define STAT_SDIO_SUSPEND_ACK (1 << 16) | ||
75 | #define STAT_SDIO_INT (1 << 15) | ||
76 | #define STAT_RD_STALLED (1 << 14) | ||
77 | #define STAT_END_CMD_RES (1 << 13) | ||
78 | #define STAT_PRG_DONE (1 << 12) | ||
79 | #define STAT_DATA_TRAN_DONE (1 << 11) | ||
80 | #define STAT_SPI_WR_ERR (1 << 10) | ||
81 | #define STAT_FLASH_ERR (1 << 9) | ||
82 | #define STAT_CLK_EN (1 << 8) | ||
83 | #define STAT_RECV_FIFO_FULL (1 << 7) /* taken from PXA255 */ | ||
84 | #define STAT_XMIT_FIFO_EMPTY (1 << 6) /* taken from PXA255 */ | ||
85 | #define STAT_RES_CRC_ERR (1 << 5) | ||
86 | #define STAT_DAT_ERR_TOKEN (1 << 4) | ||
87 | #define STAT_CRC_RD_ERR (1 << 3) | ||
88 | #define STAT_CRC_WR_ERR (1 << 2) | ||
89 | #define STAT_TIME_OUT_RES (1 << 1) | ||
90 | #define STAT_TIME_OUT_READ (1) | ||
91 | #define STAT_ERROR_BITS (0x3f) | ||
92 | |||
93 | /* MMC_CMDAT bits */ | ||
94 | /* Some of the bits used by the OF don't make much sense with these */ | ||
95 | /* definitions. So they're probably different between PXA and PP502x */ | ||
96 | /* Bits 0-5 appear to match though. */ | ||
97 | #define CMDAT_SDIO_RESUME (1 << 13) | ||
98 | #define CMDAT_SDIO_SUSPEND (1 << 12) | ||
99 | #define CMDAT_SDIO_INT_EN (1 << 11) | ||
100 | #define CMDAT_STOP_TRAN (1 << 10) | ||
101 | #define CMDAT_SD_4DAT (1 << 8) | ||
102 | #define CMDAT_DMA_EN (1 << 7) | ||
103 | #define CMDAT_INIT (1 << 6) | ||
104 | #define CMDAT_BUSY (1 << 5) | ||
105 | #define CMDAT_STRM_BLK (1 << 4) | ||
106 | #define CMDAT_WR_RD (1 << 3) | ||
107 | #define CMDAT_DATA_EN (1 << 2) | ||
108 | #define CMDAT_RES_TYPE3 (3) | ||
109 | #define CMDAT_RES_TYPE2 (2) | ||
110 | #define CMDAT_RES_TYPE1 (1) | ||
111 | |||
112 | /* MMC_I_MASK bits */ | ||
113 | /* PP502x apparently only has bits 0-3 */ | ||
114 | #define I_MASK_SDIO_SUSPEND_ACK (1 << 12) | ||
115 | #define I_MASK_SDIO_INT (1 << 11) | ||
116 | #define I_MASK_RD_STALLED (1 << 10) | ||
117 | #define I_MASK_RES_ERR (1 << 9) | ||
118 | #define I_MASK_DAT_ERR (1 << 8) | ||
119 | #define I_MASK_TINT (1 << 7) | ||
120 | #define I_MASK_TXFIFO_WR_REQ (1 << 6) | ||
121 | #define I_MASK_RXFIFO_RD_REQ (1 << 5) | ||
122 | #define I_MASK_CLK_IS_OFF (1 << 4) | ||
123 | #define I_MASK_STOP_CMD (1 << 3) | ||
124 | #define I_MASK_END_CMD_RES (1 << 2) | ||
125 | #define I_MASK_PRG_DONE (1 << 1) | ||
126 | #define I_MASK_DATA_TRAN_DONE (1 << 0) | ||
127 | |||
128 | #define FIFO_LEN 16 /* FIFO is 16 words deep */ | ||
129 | |||
130 | #define EC_OK 0 | ||
131 | #define EC_FAILED 1 | ||
132 | #define EC_NOCARD 2 | ||
133 | #define EC_WAIT_STATE_FAILED 3 | ||
134 | #define EC_CHECK_TIMEOUT_FAILED 4 | ||
135 | #define EC_POWER_UP 5 | ||
136 | #define EC_READ_TIMEOUT 6 | ||
137 | #define EC_WRITE_TIMEOUT 7 | ||
138 | #define EC_TRAN_SEL_BANK 8 | ||
139 | #define EC_TRAN_READ_ENTRY 9 | ||
140 | #define EC_TRAN_READ_EXIT 10 | ||
141 | #define EC_TRAN_WRITE_ENTRY 11 | ||
142 | #define EC_TRAN_WRITE_EXIT 12 | ||
143 | #define EC_FIFO_SEL_BANK_EMPTY 13 | ||
144 | #define EC_FIFO_SEL_BANK_DONE 14 | ||
145 | #define EC_FIFO_ENA_BANK_EMPTY 15 | ||
146 | #define EC_FIFO_READ_FULL 16 | ||
147 | #define EC_FIFO_WR_EMPTY 17 | ||
148 | #define EC_FIFO_WR_DONE 18 | ||
149 | #define EC_COMMAND 19 | ||
150 | #define NUM_EC 20 | ||
151 | |||
152 | /* for compatibility */ | ||
153 | static long last_disk_activity = -1; | ||
154 | |||
155 | /** static, private data **/ | ||
156 | static bool initialized = false; | ||
157 | static unsigned int sd_thread_id = 0; | ||
158 | |||
159 | #define Q_CLOSE 1 | ||
160 | |||
161 | static long next_yield = 0; | ||
162 | #define MIN_YIELD_PERIOD 1000 | ||
163 | |||
164 | static tCardInfo card_info[2]; | ||
165 | static tCardInfo *currcard = NULL; /* current active card */ | ||
166 | |||
167 | struct sd_card_status | ||
168 | { | ||
169 | int retry; | ||
170 | int retry_max; | ||
171 | }; | ||
172 | |||
173 | static struct sd_card_status sd_status[NUM_DRIVES] = | ||
174 | { | ||
175 | { 0, 1 }, | ||
176 | #ifdef HAVE_MULTIDRIVE | ||
177 | { 0, 10 } | ||
178 | #endif | ||
179 | }; | ||
180 | |||
181 | /* Shoot for around 75% usage */ | ||
182 | static long sd_stack [(DEFAULT_STACK_SIZE*2 + 0x1c0)/sizeof(long)]; | ||
183 | static const char sd_thread_name[] = "ata/sd"; | ||
184 | static struct mutex sd_mtx SHAREDBSS_ATTR; | ||
185 | static struct event_queue sd_queue SHAREDBSS_ATTR; | ||
186 | |||
187 | #ifdef HAVE_HOTSWAP | ||
188 | static int sd_first_drive = 0; | ||
189 | #endif | ||
190 | |||
191 | /* Posted when card plugged status has changed */ | ||
192 | #define SD_HOTSWAP 1 | ||
193 | /* Actions taken by sd_thread when card status has changed */ | ||
194 | enum sd_thread_actions | ||
195 | { | ||
196 | SDA_NONE = 0x0, | ||
197 | SDA_UNMOUNTED = 0x1, | ||
198 | SDA_MOUNTED = 0x2 | ||
199 | }; | ||
200 | |||
201 | /* Private Functions */ | ||
202 | |||
203 | static unsigned int check_time[NUM_EC]; | ||
204 | |||
205 | static inline bool sd_check_timeout(long timeout, int id) | ||
206 | { | ||
207 | return !TIME_AFTER(USEC_TIMER, check_time[id] + timeout); | ||
208 | } | ||
209 | |||
210 | static bool sd_poll_status(unsigned int trigger, long timeout) | ||
211 | { | ||
212 | long t = USEC_TIMER; | ||
213 | |||
214 | while ((MMC_STAT & trigger) == 0) | ||
215 | { | ||
216 | long time = USEC_TIMER; | ||
217 | |||
218 | if (TIME_AFTER(time, next_yield)) | ||
219 | { | ||
220 | long ty = USEC_TIMER; | ||
221 | yield(); | ||
222 | timeout += USEC_TIMER - ty; | ||
223 | next_yield = ty + MIN_YIELD_PERIOD; | ||
224 | } | ||
225 | |||
226 | if (TIME_AFTER(time, t + timeout)) | ||
227 | return false; | ||
228 | } | ||
229 | |||
230 | return true; | ||
231 | } | ||
232 | |||
233 | static int sd_command(unsigned int cmd, unsigned long arg1, | ||
234 | unsigned long *response, unsigned int cmdat) | ||
235 | { | ||
236 | int i, words; /* Number of 16 bit words to read from MMC_RES */ | ||
237 | unsigned int data[9]; | ||
238 | |||
239 | MMC_CMD = cmd; | ||
240 | MMC_ARGH = (unsigned int)((arg1 & 0xffff0000) >> 16); | ||
241 | MMC_ARGL = (unsigned int)((arg1 & 0xffff)); | ||
242 | MMC_CMDAT = cmdat; | ||
243 | |||
244 | if (!sd_poll_status(STAT_END_CMD_RES, 100000)) | ||
245 | return -EC_COMMAND; | ||
246 | |||
247 | if ((MMC_STAT & STAT_ERROR_BITS) != 0) | ||
248 | /* Error sending command */ | ||
249 | return -EC_COMMAND - (MMC_STAT & STAT_ERROR_BITS)*100; | ||
250 | |||
251 | if (cmd == SD_GO_IDLE_STATE) | ||
252 | return 0; /* no response here */ | ||
253 | |||
254 | words = (cmdat == CMDAT_RES_TYPE2) ? 9 : 3; | ||
255 | |||
256 | for (i = 0; i < words; i++) /* MMC_RES is read MSB first */ | ||
257 | data[i] = MMC_RES; /* Read most significant 16-bit word */ | ||
258 | |||
259 | if (response == NULL) | ||
260 | { | ||
261 | /* response discarded */ | ||
262 | } | ||
263 | else if (cmdat == CMDAT_RES_TYPE2) | ||
264 | { | ||
265 | /* Response type 2 has the following structure: | ||
266 | * [135:135] Start Bit - '0' | ||
267 | * [134:134] Transmission bit - '0' | ||
268 | * [133:128] Reserved - '111111' | ||
269 | * [127:001] CID or CSD register including internal CRC7 | ||
270 | * [000:000] End Bit - '1' | ||
271 | */ | ||
272 | response[3] = (data[0]<<24) + (data[1]<<8) + (data[2]>>8); | ||
273 | response[2] = (data[2]<<24) + (data[3]<<8) + (data[4]>>8); | ||
274 | response[1] = (data[4]<<24) + (data[5]<<8) + (data[6]>>8); | ||
275 | response[0] = (data[6]<<24) + (data[7]<<8) + (data[8]>>8); | ||
276 | } | ||
277 | else | ||
278 | { | ||
279 | /* Response types 1, 1b, 3, 6, 7 have the following structure: | ||
280 | * Types 4 and 5 are not supported. | ||
281 | * | ||
282 | * [47] Start bit - '0' | ||
283 | * [46] Transmission bit - '0' | ||
284 | * [45:40] R1, R1b, R6, R7: Command index | ||
285 | * R3: Reserved - '111111' | ||
286 | * [39:8] R1, R1b: Card Status | ||
287 | * R3: OCR Register | ||
288 | * R6: [31:16] RCA | ||
289 | * [15: 0] Card Status Bits 23, 22, 19, 12:0 | ||
290 | * [23] COM_CRC_ERROR | ||
291 | * [22] ILLEGAL_COMMAND | ||
292 | * [19] ERROR | ||
293 | * [12:9] CURRENT_STATE | ||
294 | * [8] READY_FOR_DATA | ||
295 | * [7:6] | ||
296 | * [5] SD_APP_CMD | ||
297 | * [4] | ||
298 | * [3] AKE_SEQ_ERROR | ||
299 | * [2] Reserved | ||
300 | * [1:0] Reserved for test mode | ||
301 | * R7: [19:16] Voltage accepted | ||
302 | * [15:8] echo-back of check pattern | ||
303 | * [7:1] R1, R1b: CRC7 | ||
304 | * R3: Reserved - '1111111' | ||
305 | * [0] End Bit - '1' | ||
306 | */ | ||
307 | response[0] = (data[0]<<24) + (data[1]<<8) + (data[2]>>8); | ||
308 | } | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | static int sd_wait_for_state(unsigned int state, int id) | ||
314 | { | ||
315 | unsigned long response = 0; | ||
316 | unsigned int timeout = 0x80000; | ||
317 | |||
318 | check_time[id] = USEC_TIMER; | ||
319 | |||
320 | while (1) | ||
321 | { | ||
322 | int ret = sd_command(SD_SEND_STATUS, currcard->rca, &response, CMDAT_RES_TYPE1); | ||
323 | long us; | ||
324 | |||
325 | if (ret < 0) | ||
326 | return ret*100 - id; | ||
327 | |||
328 | if (((response >> 9) & 0xf) == state) | ||
329 | { | ||
330 | MMC_SD_STATE = state; | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | if (!sd_check_timeout(timeout, id)) | ||
335 | return -EC_WAIT_STATE_FAILED*100 - id; | ||
336 | |||
337 | us = USEC_TIMER; | ||
338 | if (TIME_AFTER(us, next_yield)) | ||
339 | { | ||
340 | yield(); | ||
341 | timeout += USEC_TIMER - us; | ||
342 | next_yield = us + MIN_YIELD_PERIOD; | ||
343 | } | ||
344 | } | ||
345 | } | ||
346 | |||
347 | |||
348 | static inline bool card_detect_target(void) | ||
349 | { | ||
350 | #ifdef HAVE_HOTSWAP | ||
351 | #ifdef SANSA_E200 | ||
352 | return (GPIOA_INPUT_VAL & 0x80) == 0; /* low active */ | ||
353 | #elif defined SANSA_C200 | ||
354 | return (GPIOL_INPUT_VAL & 0x08) != 0; /* high active */ | ||
355 | #endif | ||
356 | #else | ||
357 | return false; | ||
358 | #endif | ||
359 | } | ||
360 | |||
361 | |||
362 | static inline void copy_read_sectors_fast(unsigned char **buf) | ||
363 | { | ||
364 | /* Copy one chunk of 16 words using best method for start alignment */ | ||
365 | switch ( (intptr_t)*buf & 3 ) | ||
366 | { | ||
367 | case 0: | ||
368 | asm volatile ( | ||
369 | "ldmia %[data], { r2-r9 } \r\n" | ||
370 | "orr r2, r2, r3, lsl #16 \r\n" | ||
371 | "orr r4, r4, r5, lsl #16 \r\n" | ||
372 | "orr r6, r6, r7, lsl #16 \r\n" | ||
373 | "orr r8, r8, r9, lsl #16 \r\n" | ||
374 | "stmia %[buf]!, { r2, r4, r6, r8 } \r\n" | ||
375 | "ldmia %[data], { r2-r9 } \r\n" | ||
376 | "orr r2, r2, r3, lsl #16 \r\n" | ||
377 | "orr r4, r4, r5, lsl #16 \r\n" | ||
378 | "orr r6, r6, r7, lsl #16 \r\n" | ||
379 | "orr r8, r8, r9, lsl #16 \r\n" | ||
380 | "stmia %[buf]!, { r2, r4, r6, r8 } \r\n" | ||
381 | : [buf]"+&r"(*buf) | ||
382 | : [data]"r"(&MMC_DATA_FIFO) | ||
383 | : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9" | ||
384 | ); | ||
385 | break; | ||
386 | case 1: | ||
387 | asm volatile ( | ||
388 | "ldmia %[data], { r2-r9 } \r\n" | ||
389 | "orr r3, r2, r3, lsl #16 \r\n" | ||
390 | "strb r3, [%[buf]], #1 \r\n" | ||
391 | "mov r3, r3, lsr #8 \r\n" | ||
392 | "strh r3, [%[buf]], #2 \r\n" | ||
393 | "mov r3, r3, lsr #16 \r\n" | ||
394 | "orr r3, r3, r4, lsl #8 \r\n" | ||
395 | "orr r3, r3, r5, lsl #24 \r\n" | ||
396 | "mov r5, r5, lsr #8 \r\n" | ||
397 | "orr r5, r5, r6, lsl #8 \r\n" | ||
398 | "orr r5, r5, r7, lsl #24 \r\n" | ||
399 | "mov r7, r7, lsr #8 \r\n" | ||
400 | "orr r7, r7, r8, lsl #8 \r\n" | ||
401 | "orr r7, r7, r9, lsl #24 \r\n" | ||
402 | "mov r2, r9, lsr #8 \r\n" | ||
403 | "stmia %[buf]!, { r3, r5, r7 } \r\n" | ||
404 | "ldmia %[data], { r3-r10 } \r\n" | ||
405 | "orr r2, r2, r3, lsl #8 \r\n" | ||
406 | "orr r2, r2, r4, lsl #24 \r\n" | ||
407 | "mov r4, r4, lsr #8 \r\n" | ||
408 | "orr r4, r4, r5, lsl #8 \r\n" | ||
409 | "orr r4, r4, r6, lsl #24 \r\n" | ||
410 | "mov r6, r6, lsr #8 \r\n" | ||
411 | "orr r6, r6, r7, lsl #8 \r\n" | ||
412 | "orr r6, r6, r8, lsl #24 \r\n" | ||
413 | "mov r8, r8, lsr #8 \r\n" | ||
414 | "orr r8, r8, r9, lsl #8 \r\n" | ||
415 | "orr r8, r8, r10, lsl #24 \r\n" | ||
416 | "mov r10, r10, lsr #8 \r\n" | ||
417 | "stmia %[buf]!, { r2, r4, r6, r8 } \r\n" | ||
418 | "strb r10, [%[buf]], #1 \r\n" | ||
419 | : [buf]"+&r"(*buf) | ||
420 | : [data]"r"(&MMC_DATA_FIFO) | ||
421 | : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10" | ||
422 | ); | ||
423 | break; | ||
424 | case 2: | ||
425 | asm volatile ( | ||
426 | "ldmia %[data], { r2-r9 } \r\n" | ||
427 | "strh r2, [%[buf]], #2 \r\n" | ||
428 | "orr r3, r3, r4, lsl #16 \r\n" | ||
429 | "orr r5, r5, r6, lsl #16 \r\n" | ||
430 | "orr r7, r7, r8, lsl #16 \r\n" | ||
431 | "stmia %[buf]!, { r3, r5, r7 } \r\n" | ||
432 | "ldmia %[data], { r2-r8, r10 } \r\n" | ||
433 | "orr r2, r9, r2, lsl #16 \r\n" | ||
434 | "orr r3, r3, r4, lsl #16 \r\n" | ||
435 | "orr r5, r5, r6, lsl #16 \r\n" | ||
436 | "orr r7, r7, r8, lsl #16 \r\n" | ||
437 | "stmia %[buf]!, { r2, r3, r5, r7 } \r\n" | ||
438 | "strh r10, [%[buf]], #2 \r\n" | ||
439 | : [buf]"+&r"(*buf) | ||
440 | : [data]"r"(&MMC_DATA_FIFO) | ||
441 | : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10" | ||
442 | ); | ||
443 | break; | ||
444 | case 3: | ||
445 | asm volatile ( | ||
446 | "ldmia %[data], { r2-r9 } \r\n" | ||
447 | "orr r3, r2, r3, lsl #16 \r\n" | ||
448 | "strb r3, [%[buf]], #1 \r\n" | ||
449 | "mov r3, r3, lsr #8 \r\n" | ||
450 | "orr r3, r3, r4, lsl #24 \r\n" | ||
451 | "mov r4, r4, lsr #8 \r\n" | ||
452 | "orr r5, r4, r5, lsl #8 \r\n" | ||
453 | "orr r5, r5, r6, lsl #24 \r\n" | ||
454 | "mov r6, r6, lsr #8 \r\n" | ||
455 | "orr r7, r6, r7, lsl #8 \r\n" | ||
456 | "orr r7, r7, r8, lsl #24 \r\n" | ||
457 | "mov r8, r8, lsr #8 \r\n" | ||
458 | "orr r2, r8, r9, lsl #8 \r\n" | ||
459 | "stmia %[buf]!, { r3, r5, r7 } \r\n" | ||
460 | "ldmia %[data], { r3-r10 } \r\n" | ||
461 | "orr r2, r2, r3, lsl #24 \r\n" | ||
462 | "mov r3, r3, lsr #8 \r\n" | ||
463 | "orr r4, r3, r4, lsl #8 \r\n" | ||
464 | "orr r4, r4, r5, lsl #24 \r\n" | ||
465 | "mov r5, r5, lsr #8 \r\n" | ||
466 | "orr r6, r5, r6, lsl #8 \r\n" | ||
467 | "orr r6, r6, r7, lsl #24 \r\n" | ||
468 | "mov r7, r7, lsr #8 \r\n" | ||
469 | "orr r8, r7, r8, lsl #8 \r\n" | ||
470 | "orr r8, r8, r9, lsl #24 \r\n" | ||
471 | "mov r9, r9, lsr #8 \r\n" | ||
472 | "orr r10, r9, r10, lsl #8 \r\n" | ||
473 | "stmia %[buf]!, { r2, r4, r6, r8 } \r\n" | ||
474 | "strh r10, [%[buf]], #2 \r\n" | ||
475 | "mov r10, r10, lsr #16 \r\n" | ||
476 | "strb r10, [%[buf]], #1 \r\n" | ||
477 | : [buf]"+&r"(*buf) | ||
478 | : [data]"r"(&MMC_DATA_FIFO) | ||
479 | : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10" | ||
480 | ); | ||
481 | break; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | static inline void copy_read_sectors_slow(unsigned char** buf) | ||
486 | { | ||
487 | int cnt = FIFO_LEN; | ||
488 | int t; | ||
489 | |||
490 | /* Copy one chunk of 16 words */ | ||
491 | asm volatile ( | ||
492 | "1: \r\n" | ||
493 | "ldrh %[t], [%[data]] \r\n" | ||
494 | "strb %[t], [%[buf]], #1 \r\n" | ||
495 | "mov %[t], %[t], lsr #8 \r\n" | ||
496 | "strb %[t], [%[buf]], #1 \r\n" | ||
497 | "subs %[cnt], %[cnt], #1 \r\n" | ||
498 | "bgt 1b \r\n" | ||
499 | : [cnt]"+&r"(cnt), [buf]"+&r"(*buf), | ||
500 | [t]"=&r"(t) | ||
501 | : [data]"r"(&MMC_DATA_FIFO) | ||
502 | ); | ||
503 | } | ||
504 | |||
505 | /* Writes have to be kept slow for now */ | ||
506 | static inline void copy_write_sectors(const unsigned char** buf) | ||
507 | { | ||
508 | int cnt = FIFO_LEN - 1; | ||
509 | unsigned t; | ||
510 | long time; | ||
511 | |||
512 | time = USEC_TIMER + 3; | ||
513 | if (((intptr_t)*buf & 3) == 0) | ||
514 | { | ||
515 | asm volatile ( | ||
516 | "ldmia %[buf]!, { r3, r5, r7, r9 } \r\n" | ||
517 | "mov r4, r3, lsr #16 \r\n" | ||
518 | "mov r6, r5, lsr #16 \r\n" | ||
519 | "mov r8, r7, lsr #16 \r\n" | ||
520 | "mov r10, r9, lsr #16 \r\n" | ||
521 | "stmia %[data], { r3-r10 } \r\n" | ||
522 | "ldmia %[buf]!, { r3, r5, r7, r9 } \r\n" | ||
523 | "mov r4, r3, lsr #16 \r\n" | ||
524 | "mov r6, r5, lsr #16 \r\n" | ||
525 | "mov r8, r7, lsr #16 \r\n" | ||
526 | "mov %[t], r9, lsr #16 \r\n" | ||
527 | "stmia %[data], { r3-r9 } \r\n" | ||
528 | : [buf]"+&r"(*buf), [t]"=&r"(t) | ||
529 | : [data]"r"(&MMC_DATA_FIFO) | ||
530 | : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10" | ||
531 | ); | ||
532 | } | ||
533 | else | ||
534 | { | ||
535 | do | ||
536 | { | ||
537 | t = *(*buf)++; | ||
538 | t |= *(*buf)++ << 8; | ||
539 | MMC_DATA_FIFO = t; | ||
540 | } while (--cnt > 0); /* tail loop is faster */ | ||
541 | t = *(*buf)++; | ||
542 | t |= *(*buf)++ << 8; | ||
543 | } | ||
544 | /* Don't write the last word before at least 3 usec have elapsed since FIFO_EMPTY */ | ||
545 | /* This prevents the 'two bytes inserted' bug. */ | ||
546 | |||
547 | while (!TIME_AFTER(USEC_TIMER, time)); | ||
548 | MMC_DATA_FIFO = t; | ||
549 | } | ||
550 | |||
551 | static int sd_select_bank(unsigned char bank) | ||
552 | { | ||
553 | unsigned char card_data[FIFO_LEN*2];// FIFO_LEN words=FIFO_LEN*2 bytes | ||
554 | const unsigned char* write_buf; | ||
555 | int i, ret; | ||
556 | |||
557 | memset(card_data, 0, sizeof card_data); | ||
558 | |||
559 | ret = sd_wait_for_state(SD_TRAN, EC_TRAN_SEL_BANK); | ||
560 | if (ret < 0) | ||
561 | return ret; | ||
562 | |||
563 | MMC_BLKLEN = 512; | ||
564 | MMC_NUMBLK = 1; | ||
565 | |||
566 | ret = sd_command(35, 0, NULL, /* CMD35 is vendor specific */ | ||
567 | 0x1c00 | CMDAT_WR_RD | CMDAT_DATA_EN | CMDAT_RES_TYPE1); | ||
568 | if (ret < 0) | ||
569 | return ret; | ||
570 | |||
571 | MMC_SD_STATE = SD_PRG; | ||
572 | |||
573 | card_data[0] = bank; | ||
574 | |||
575 | /* Write the card data */ | ||
576 | for (i = 0; i < SD_BLOCK_SIZE/2; i += FIFO_LEN) | ||
577 | { | ||
578 | write_buf = card_data; | ||
579 | /* Wait for the FIFO to empty */ | ||
580 | if (sd_poll_status(STAT_XMIT_FIFO_EMPTY, 10000)) | ||
581 | { | ||
582 | copy_write_sectors(&write_buf); /* Copy one chunk of 16 words */ | ||
583 | /* clear buffer: only the first chunk contains interesting data (bank), the remaining is zero filling */ | ||
584 | memset(card_data, 0, sizeof card_data); | ||
585 | continue; | ||
586 | } | ||
587 | |||
588 | return -EC_FIFO_SEL_BANK_EMPTY; | ||
589 | } | ||
590 | |||
591 | if (!sd_poll_status(STAT_PRG_DONE, 10000)) | ||
592 | return -EC_FIFO_SEL_BANK_DONE; | ||
593 | |||
594 | currcard->current_bank = bank; | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | static void sd_card_mux(int card_no) | ||
600 | { | ||
601 | /* Set the current card mux */ | ||
602 | #if defined(SANSA_E200) | ||
603 | if (card_no == 0) | ||
604 | { | ||
605 | GPO32_VAL |= 0x4; | ||
606 | |||
607 | GPIO_CLEAR_BITWISE(GPIOA_ENABLE, 0x7a); | ||
608 | GPIO_CLEAR_BITWISE(GPIOA_OUTPUT_EN, 0x7a); | ||
609 | GPIO_SET_BITWISE(GPIOD_ENABLE, 0x1f); | ||
610 | GPIO_SET_BITWISE(GPIOD_OUTPUT_VAL, 0x1f); | ||
611 | GPIO_SET_BITWISE(GPIOD_OUTPUT_EN, 0x1f); | ||
612 | |||
613 | outl((inl(0x70000014) & ~(0x3ffff)) | 0x255aa, 0x70000014); | ||
614 | } | ||
615 | else | ||
616 | { | ||
617 | GPO32_VAL &= ~0x4; | ||
618 | |||
619 | GPIO_CLEAR_BITWISE(GPIOD_ENABLE, 0x1f); | ||
620 | GPIO_CLEAR_BITWISE(GPIOD_OUTPUT_EN, 0x1f); | ||
621 | GPIO_SET_BITWISE(GPIOA_ENABLE, 0x7a); | ||
622 | GPIO_SET_BITWISE(GPIOA_OUTPUT_VAL, 0x7a); | ||
623 | GPIO_SET_BITWISE( GPIOA_OUTPUT_EN, 0x7a); | ||
624 | |||
625 | outl(inl(0x70000014) & ~(0x3ffff), 0x70000014); | ||
626 | } | ||
627 | #elif defined(SANSA_C200) | ||
628 | if (card_no == 0) | ||
629 | { | ||
630 | GPO32_VAL |= 0x4; | ||
631 | |||
632 | GPIO_CLEAR_BITWISE(GPIOD_ENABLE, 0x1f); | ||
633 | GPIO_CLEAR_BITWISE(GPIOD_OUTPUT_EN, 0x1f); | ||
634 | GPIO_SET_BITWISE(GPIOA_ENABLE, 0x7a); | ||
635 | GPIO_SET_BITWISE(GPIOA_OUTPUT_VAL, 0x7a); | ||
636 | GPIO_SET_BITWISE( GPIOA_OUTPUT_EN, 0x7a); | ||
637 | |||
638 | outl(inl(0x70000014) & ~(0x3ffff), 0x70000014); | ||
639 | } | ||
640 | else | ||
641 | { | ||
642 | GPO32_VAL &= ~0x4; | ||
643 | |||
644 | GPIO_CLEAR_BITWISE(GPIOA_ENABLE, 0x7a); | ||
645 | GPIO_CLEAR_BITWISE(GPIOA_OUTPUT_EN, 0x7a); | ||
646 | GPIO_SET_BITWISE(GPIOD_ENABLE, 0x1f); | ||
647 | GPIO_SET_BITWISE(GPIOD_OUTPUT_VAL, 0x1f); | ||
648 | GPIO_SET_BITWISE(GPIOD_OUTPUT_EN, 0x1f); | ||
649 | |||
650 | outl((inl(0x70000014) & ~(0x3ffff)) | 0x255aa, 0x70000014); | ||
651 | } | ||
652 | #elif defined(PHILIPS_SA9200) | ||
653 | /* only 1 "card" (no external memory card) */ | ||
654 | (void)card_no; | ||
655 | |||
656 | GPIO_SET_BITWISE(GPIOH_ENABLE, 0x80); | ||
657 | GPIO_SET_BITWISE(GPIOH_OUTPUT_EN, 0x80); | ||
658 | |||
659 | outl(0x255aa, 0x70000014); | ||
660 | |||
661 | GPIO_CLEAR_BITWISE(GPIOA_ENABLE, 0x04); | ||
662 | GPIO_CLEAR_BITWISE(GPIOA_OUTPUT_EN, 0x04); | ||
663 | |||
664 | GPIO_CLEAR_BITWISE(GPIOA_ENABLE, 0x7a); | ||
665 | GPIO_CLEAR_BITWISE(GPIOA_OUTPUT_EN, 0x7a); | ||
666 | |||
667 | GPIO_SET_BITWISE(GPIOH_OUTPUT_VAL, 0x80); | ||
668 | GPIO_SET_BITWISE(GPIOH_OUTPUT_EN, 0x80); | ||
669 | #endif | ||
670 | } | ||
671 | |||
672 | static void sd_init_device(int card_no) | ||
673 | { | ||
674 | /* SD Protocol registers */ | ||
675 | #ifdef HAVE_HOTSWAP | ||
676 | unsigned long response = 0; | ||
677 | #endif | ||
678 | unsigned int i; | ||
679 | unsigned char carddata[512]; | ||
680 | unsigned char *dataptr; | ||
681 | unsigned long temp_reg[4]; | ||
682 | int ret; | ||
683 | |||
684 | /* Enable and initialise controller */ | ||
685 | MMC_CLKRT = 6; /* switch to lowest clock rate */ | ||
686 | |||
687 | /* Initialise card data as blank */ | ||
688 | memset(currcard, 0, sizeof(*currcard)); | ||
689 | |||
690 | /* Switch card mux to card to initialize */ | ||
691 | sd_card_mux(card_no); | ||
692 | |||
693 | /* Init NAND */ | ||
694 | MMC_INIT_1 |= (1 << 15); | ||
695 | MMC_INIT_2 |= (1 << 15); | ||
696 | MMC_INIT_2 &= ~(3 << 12); | ||
697 | MMC_INIT_2 |= (1 << 13); | ||
698 | MMC_INIT_1 &= ~(3 << 12); | ||
699 | MMC_INIT_1 |= (1 << 13); | ||
700 | |||
701 | DEV_EN |= DEV_ATA; /* Enable controller */ | ||
702 | DEV_RS |= DEV_ATA; /* Reset controller */ | ||
703 | DEV_RS &=~DEV_ATA; /* Clear Reset */ | ||
704 | |||
705 | MMC_SD_STATE = SD_TRAN; | ||
706 | |||
707 | MMC_I_MASK = 0xf; /* disable interrupts */ | ||
708 | |||
709 | ret = sd_command(SD_GO_IDLE_STATE, 0, NULL, 0x100); | ||
710 | if (ret < 0) | ||
711 | goto card_init_error; | ||
712 | |||
713 | check_time[EC_POWER_UP] = USEC_TIMER; | ||
714 | |||
715 | #ifdef HAVE_HOTSWAP | ||
716 | /* Check for SDHC: | ||
717 | - non-SDHC cards simply ignore SD_SEND_IF_COND (CMD8) and we get error -219, | ||
718 | which we can just ignore and assume we're dealing with standard SD. | ||
719 | - SDHC cards echo back the argument into the response. This is how we | ||
720 | tell if the card is SDHC. | ||
721 | */ | ||
722 | ret = sd_command(SD_SEND_IF_COND,0x1aa, &response, | ||
723 | CMDAT_DATA_EN | CMDAT_RES_TYPE3); | ||
724 | if ( (ret < 0) && (ret!=-219) ) | ||
725 | goto card_init_error; | ||
726 | #endif | ||
727 | |||
728 | while ((currcard->ocr & (1 << 31)) == 0) /* until card is powered up */ | ||
729 | { | ||
730 | ret = sd_command(SD_APP_CMD, currcard->rca, NULL, CMDAT_RES_TYPE1); | ||
731 | if (ret < 0) | ||
732 | goto card_init_error; | ||
733 | |||
734 | #ifdef HAVE_HOTSWAP | ||
735 | if(response == 0x1aa) | ||
736 | { | ||
737 | /* SDHC */ | ||
738 | ret = sd_command(SD_APP_OP_COND, (1<<30)|0x100000, | ||
739 | &currcard->ocr, CMDAT_RES_TYPE3); | ||
740 | } | ||
741 | else | ||
742 | #endif /* HAVE_HOTSWAP */ | ||
743 | { | ||
744 | /* SD Standard */ | ||
745 | ret = sd_command(SD_APP_OP_COND, 0x100000, &currcard->ocr, | ||
746 | CMDAT_RES_TYPE3); | ||
747 | } | ||
748 | |||
749 | if (ret < 0) | ||
750 | goto card_init_error; | ||
751 | |||
752 | if (!sd_check_timeout(5000000, EC_POWER_UP)) | ||
753 | { | ||
754 | ret = -EC_POWER_UP; | ||
755 | goto card_init_error; | ||
756 | } | ||
757 | } | ||
758 | |||
759 | ret = sd_command(SD_ALL_SEND_CID, 0, temp_reg, CMDAT_RES_TYPE2); | ||
760 | if (ret < 0) | ||
761 | goto card_init_error; | ||
762 | |||
763 | for(i=0; i<4; i++) | ||
764 | currcard->cid[i] = temp_reg[3-i]; | ||
765 | |||
766 | ret = sd_command(SD_SEND_RELATIVE_ADDR, 0, &currcard->rca, CMDAT_RES_TYPE1); | ||
767 | if (ret < 0) | ||
768 | goto card_init_error; | ||
769 | |||
770 | ret = sd_command(SD_SEND_CSD, currcard->rca, temp_reg, CMDAT_RES_TYPE2); | ||
771 | if (ret < 0) | ||
772 | goto card_init_error; | ||
773 | |||
774 | for(i=0; i<4; i++) | ||
775 | currcard->csd[i] = temp_reg[3-i]; | ||
776 | |||
777 | sd_parse_csd(currcard); | ||
778 | |||
779 | MMC_CLKRT = 0; /* switch to highest clock rate */ | ||
780 | |||
781 | ret = sd_command(SD_SELECT_CARD, currcard->rca, NULL, | ||
782 | 0x80 | CMDAT_RES_TYPE1); | ||
783 | if (ret < 0) | ||
784 | goto card_init_error; | ||
785 | |||
786 | ret = sd_command(SD_APP_CMD, currcard->rca, NULL, CMDAT_RES_TYPE1); | ||
787 | if (ret < 0) | ||
788 | goto card_init_error; | ||
789 | |||
790 | ret = sd_command(SD_SET_BUS_WIDTH, currcard->rca | 2, NULL, | ||
791 | CMDAT_RES_TYPE1); /* 4 bit */ | ||
792 | if (ret < 0) | ||
793 | goto card_init_error; | ||
794 | |||
795 | ret = sd_command(SD_SET_BLOCKLEN, currcard->blocksize, NULL, | ||
796 | CMDAT_RES_TYPE1); | ||
797 | if (ret < 0) | ||
798 | goto card_init_error; | ||
799 | |||
800 | MMC_BLKLEN = currcard->blocksize; | ||
801 | |||
802 | /* If this card is >4GB & not SDHC, then we need to enable bank switching */ | ||
803 | if( (currcard->numblocks >= BLOCKS_PER_BANK) && | ||
804 | ((currcard->ocr & (1<<30)) == 0) ) | ||
805 | { | ||
806 | MMC_SD_STATE = SD_TRAN; | ||
807 | MMC_NUMBLK = 1; | ||
808 | |||
809 | ret = sd_command(SD_SWITCH_FUNC, 0x80ffffef, NULL, | ||
810 | 0x1c00 | CMDAT_DATA_EN | CMDAT_RES_TYPE1); | ||
811 | if (ret < 0) | ||
812 | goto card_init_error; | ||
813 | |||
814 | /* Read 512 bytes from the card. | ||
815 | The first 512 bits contain the status information | ||
816 | TODO: Do something useful with this! */ | ||
817 | dataptr = carddata; | ||
818 | for (i = 0; i < SD_BLOCK_SIZE/2; i += FIFO_LEN) | ||
819 | { | ||
820 | /* Wait for the FIFO to be full */ | ||
821 | if (sd_poll_status(STAT_RECV_FIFO_FULL, 100000)) | ||
822 | { | ||
823 | copy_read_sectors_slow(&dataptr); | ||
824 | continue; | ||
825 | } | ||
826 | |||
827 | ret = -EC_FIFO_ENA_BANK_EMPTY; | ||
828 | goto card_init_error; | ||
829 | } | ||
830 | } | ||
831 | |||
832 | currcard->initialized = 1; | ||
833 | return; | ||
834 | |||
835 | /* Card failed to initialize so disable it */ | ||
836 | card_init_error: | ||
837 | currcard->initialized = ret; | ||
838 | } | ||
839 | |||
840 | /* lock must already be aquired */ | ||
841 | static void sd_select_device(int card_no) | ||
842 | { | ||
843 | currcard = &card_info[card_no]; | ||
844 | |||
845 | if (card_no == 0) | ||
846 | { | ||
847 | /* Main card always gets a chance */ | ||
848 | sd_status[0].retry = 0; | ||
849 | } | ||
850 | |||
851 | if (currcard->initialized > 0) | ||
852 | { | ||
853 | /* This card is already initialized - switch to it */ | ||
854 | sd_card_mux(card_no); | ||
855 | return; | ||
856 | } | ||
857 | |||
858 | if (currcard->initialized == 0) | ||
859 | { | ||
860 | /* Card needs (re)init */ | ||
861 | sd_init_device(card_no); | ||
862 | } | ||
863 | } | ||
864 | |||
865 | /* API Functions */ | ||
866 | |||
867 | int sd_read_sectors(IF_MD2(int drive,) unsigned long start, int incount, | ||
868 | void* inbuf) | ||
869 | { | ||
870 | #ifndef HAVE_MULTIDRIVE | ||
871 | const int drive = 0; | ||
872 | #endif | ||
873 | int ret; | ||
874 | unsigned char *buf, *buf_end; | ||
875 | unsigned int bank; | ||
876 | |||
877 | /* TODO: Add DMA support. */ | ||
878 | |||
879 | mutex_lock(&sd_mtx); | ||
880 | sd_enable(true); | ||
881 | led(true); | ||
882 | |||
883 | sd_read_retry: | ||
884 | if (drive != 0 && !card_detect_target()) | ||
885 | { | ||
886 | /* no external sd-card inserted */ | ||
887 | ret = -EC_NOCARD; | ||
888 | goto sd_read_error; | ||
889 | } | ||
890 | |||
891 | sd_select_device(drive); | ||
892 | |||
893 | if (currcard->initialized < 0) | ||
894 | { | ||
895 | ret = currcard->initialized; | ||
896 | goto sd_read_error; | ||
897 | } | ||
898 | |||
899 | last_disk_activity = current_tick; | ||
900 | |||
901 | /* Only switch banks with non-SDHC cards */ | ||
902 | if((currcard->ocr & (1<<30))==0) | ||
903 | { | ||
904 | bank = start / BLOCKS_PER_BANK; | ||
905 | |||
906 | if (currcard->current_bank != bank) | ||
907 | { | ||
908 | ret = sd_select_bank(bank); | ||
909 | if (ret < 0) | ||
910 | goto sd_read_error; | ||
911 | } | ||
912 | |||
913 | start -= bank * BLOCKS_PER_BANK; | ||
914 | } | ||
915 | |||
916 | ret = sd_wait_for_state(SD_TRAN, EC_TRAN_READ_ENTRY); | ||
917 | if (ret < 0) | ||
918 | goto sd_read_error; | ||
919 | |||
920 | MMC_NUMBLK = incount; | ||
921 | |||
922 | #ifdef HAVE_HOTSWAP | ||
923 | if(currcard->ocr & (1<<30) ) | ||
924 | { | ||
925 | /* SDHC */ | ||
926 | ret = sd_command(SD_READ_MULTIPLE_BLOCK, start, NULL, | ||
927 | 0x1c00 | CMDAT_BUSY | CMDAT_DATA_EN | CMDAT_RES_TYPE1); | ||
928 | } | ||
929 | else | ||
930 | #endif | ||
931 | { | ||
932 | ret = sd_command(SD_READ_MULTIPLE_BLOCK, start * SD_BLOCK_SIZE, NULL, | ||
933 | 0x1c00 | CMDAT_BUSY | CMDAT_DATA_EN | CMDAT_RES_TYPE1); | ||
934 | } | ||
935 | if (ret < 0) | ||
936 | goto sd_read_error; | ||
937 | |||
938 | /* TODO: Don't assume SD_BLOCK_SIZE == SECTOR_SIZE */ | ||
939 | |||
940 | buf_end = (unsigned char *)inbuf + incount * currcard->blocksize; | ||
941 | for (buf = inbuf; buf < buf_end;) | ||
942 | { | ||
943 | /* Wait for the FIFO to be full */ | ||
944 | if (sd_poll_status(STAT_RECV_FIFO_FULL, 0x80000)) | ||
945 | { | ||
946 | copy_read_sectors_fast(&buf); /* Copy one chunk of 16 words */ | ||
947 | /* TODO: Switch bank if necessary */ | ||
948 | continue; | ||
949 | } | ||
950 | |||
951 | ret = -EC_FIFO_READ_FULL; | ||
952 | goto sd_read_error; | ||
953 | } | ||
954 | |||
955 | last_disk_activity = current_tick; | ||
956 | |||
957 | ret = sd_command(SD_STOP_TRANSMISSION, 0, NULL, CMDAT_RES_TYPE1); | ||
958 | if (ret < 0) | ||
959 | goto sd_read_error; | ||
960 | |||
961 | ret = sd_wait_for_state(SD_TRAN, EC_TRAN_READ_EXIT); | ||
962 | if (ret < 0) | ||
963 | goto sd_read_error; | ||
964 | |||
965 | while (1) | ||
966 | { | ||
967 | led(false); | ||
968 | sd_enable(false); | ||
969 | mutex_unlock(&sd_mtx); | ||
970 | |||
971 | return ret; | ||
972 | |||
973 | sd_read_error: | ||
974 | if (sd_status[drive].retry < sd_status[drive].retry_max | ||
975 | && ret != -EC_NOCARD) | ||
976 | { | ||
977 | sd_status[drive].retry++; | ||
978 | currcard->initialized = 0; | ||
979 | goto sd_read_retry; | ||
980 | } | ||
981 | } | ||
982 | } | ||
983 | |||
984 | int sd_write_sectors(IF_MD2(int drive,) unsigned long start, int count, | ||
985 | const void* outbuf) | ||
986 | { | ||
987 | /* Write support is not finished yet */ | ||
988 | /* TODO: The standard suggests using ACMD23 prior to writing multiple blocks | ||
989 | to improve performance */ | ||
990 | #ifndef HAVE_MULTIDRIVE | ||
991 | const int drive = 0; | ||
992 | #endif | ||
993 | int ret; | ||
994 | const unsigned char *buf, *buf_end; | ||
995 | unsigned int bank; | ||
996 | |||
997 | mutex_lock(&sd_mtx); | ||
998 | sd_enable(true); | ||
999 | led(true); | ||
1000 | |||
1001 | sd_write_retry: | ||
1002 | if (drive != 0 && !card_detect_target()) | ||
1003 | { | ||
1004 | /* no external sd-card inserted */ | ||
1005 | ret = -EC_NOCARD; | ||
1006 | goto sd_write_error; | ||
1007 | } | ||
1008 | |||
1009 | sd_select_device(drive); | ||
1010 | |||
1011 | if (currcard->initialized < 0) | ||
1012 | { | ||
1013 | ret = currcard->initialized; | ||
1014 | goto sd_write_error; | ||
1015 | } | ||
1016 | |||
1017 | /* Only switch banks with non-SDHC cards */ | ||
1018 | if((currcard->ocr & (1<<30))==0) | ||
1019 | { | ||
1020 | bank = start / BLOCKS_PER_BANK; | ||
1021 | |||
1022 | if (currcard->current_bank != bank) | ||
1023 | { | ||
1024 | ret = sd_select_bank(bank); | ||
1025 | if (ret < 0) | ||
1026 | goto sd_write_error; | ||
1027 | } | ||
1028 | |||
1029 | start -= bank * BLOCKS_PER_BANK; | ||
1030 | } | ||
1031 | |||
1032 | check_time[EC_WRITE_TIMEOUT] = USEC_TIMER; | ||
1033 | |||
1034 | ret = sd_wait_for_state(SD_TRAN, EC_TRAN_WRITE_ENTRY); | ||
1035 | if (ret < 0) | ||
1036 | goto sd_write_error; | ||
1037 | |||
1038 | MMC_NUMBLK = count; | ||
1039 | |||
1040 | #ifdef HAVE_HOTSWAP | ||
1041 | if(currcard->ocr & (1<<30) ) | ||
1042 | { | ||
1043 | /* SDHC */ | ||
1044 | ret = sd_command(SD_WRITE_MULTIPLE_BLOCK, start, NULL, | ||
1045 | CMDAT_WR_RD | CMDAT_DATA_EN | CMDAT_RES_TYPE1); | ||
1046 | } | ||
1047 | else | ||
1048 | #endif | ||
1049 | { | ||
1050 | ret = sd_command(SD_WRITE_MULTIPLE_BLOCK, start*SD_BLOCK_SIZE, NULL, | ||
1051 | CMDAT_WR_RD | CMDAT_DATA_EN | CMDAT_RES_TYPE1); | ||
1052 | } | ||
1053 | if (ret < 0) | ||
1054 | goto sd_write_error; | ||
1055 | |||
1056 | buf_end = outbuf + count * currcard->blocksize - 2*FIFO_LEN; | ||
1057 | |||
1058 | for (buf = outbuf; buf <= buf_end;) | ||
1059 | { | ||
1060 | if (buf == buf_end) | ||
1061 | { | ||
1062 | /* Set MMC_SD_STATE to SD_PRG for the last buffer fill */ | ||
1063 | MMC_SD_STATE = SD_PRG; | ||
1064 | } | ||
1065 | |||
1066 | copy_write_sectors(&buf); /* Copy one chunk of 16 words */ | ||
1067 | /* TODO: Switch bank if necessary */ | ||
1068 | |||
1069 | /* Wait for the FIFO to empty */ | ||
1070 | if (!sd_poll_status(STAT_XMIT_FIFO_EMPTY, 0x80000)) | ||
1071 | { | ||
1072 | ret = -EC_FIFO_WR_EMPTY; | ||
1073 | goto sd_write_error; | ||
1074 | } | ||
1075 | } | ||
1076 | |||
1077 | last_disk_activity = current_tick; | ||
1078 | |||
1079 | if (!sd_poll_status(STAT_PRG_DONE, 0x80000)) | ||
1080 | { | ||
1081 | ret = -EC_FIFO_WR_DONE; | ||
1082 | goto sd_write_error; | ||
1083 | } | ||
1084 | |||
1085 | ret = sd_command(SD_STOP_TRANSMISSION, 0, NULL, CMDAT_RES_TYPE1); | ||
1086 | if (ret < 0) | ||
1087 | goto sd_write_error; | ||
1088 | |||
1089 | ret = sd_wait_for_state(SD_TRAN, EC_TRAN_WRITE_EXIT); | ||
1090 | if (ret < 0) | ||
1091 | goto sd_write_error; | ||
1092 | |||
1093 | while (1) | ||
1094 | { | ||
1095 | led(false); | ||
1096 | sd_enable(false); | ||
1097 | mutex_unlock(&sd_mtx); | ||
1098 | |||
1099 | return ret; | ||
1100 | |||
1101 | sd_write_error: | ||
1102 | if (sd_status[drive].retry < sd_status[drive].retry_max | ||
1103 | && ret != -EC_NOCARD) | ||
1104 | { | ||
1105 | sd_status[drive].retry++; | ||
1106 | currcard->initialized = 0; | ||
1107 | goto sd_write_retry; | ||
1108 | } | ||
1109 | } | ||
1110 | } | ||
1111 | |||
1112 | #ifndef SD_DRIVER_CLOSE | ||
1113 | static void sd_thread(void) NORETURN_ATTR; | ||
1114 | #endif | ||
1115 | static void sd_thread(void) | ||
1116 | { | ||
1117 | struct queue_event ev; | ||
1118 | bool idle_notified = false; | ||
1119 | |||
1120 | while (1) | ||
1121 | { | ||
1122 | queue_wait_w_tmo(&sd_queue, &ev, HZ); | ||
1123 | |||
1124 | switch ( ev.id ) | ||
1125 | { | ||
1126 | #ifdef HAVE_HOTSWAP | ||
1127 | case SYS_HOTSWAP_INSERTED: | ||
1128 | case SYS_HOTSWAP_EXTRACTED: | ||
1129 | fat_lock(); /* lock-out FAT activity first - | ||
1130 | prevent deadlocking via disk_mount that | ||
1131 | would cause a reverse-order attempt with | ||
1132 | another thread */ | ||
1133 | mutex_lock(&sd_mtx); /* lock-out card activity - direct calls | ||
1134 | into driver that bypass the fat cache */ | ||
1135 | |||
1136 | /* We now have exclusive control of fat cache and ata */ | ||
1137 | |||
1138 | disk_unmount(sd_first_drive+1); /* release "by force", ensure file | ||
1139 | descriptors aren't leaked and any busy | ||
1140 | ones are invalid if mounting */ | ||
1141 | |||
1142 | /* Force card init for new card, re-init for re-inserted one or | ||
1143 | * clear if the last attempt to init failed with an error. */ | ||
1144 | card_info[1].initialized = 0; | ||
1145 | sd_status[1].retry = 0; | ||
1146 | |||
1147 | if (ev.id == SYS_HOTSWAP_INSERTED) | ||
1148 | disk_mount(sd_first_drive+1); | ||
1149 | |||
1150 | queue_broadcast(SYS_FS_CHANGED, 0); | ||
1151 | |||
1152 | /* Access is now safe */ | ||
1153 | mutex_unlock(&sd_mtx); | ||
1154 | fat_unlock(); | ||
1155 | break; | ||
1156 | #endif | ||
1157 | case SYS_TIMEOUT: | ||
1158 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) | ||
1159 | { | ||
1160 | idle_notified = false; | ||
1161 | } | ||
1162 | else | ||
1163 | { | ||
1164 | /* never let a timer wrap confuse us */ | ||
1165 | next_yield = USEC_TIMER; | ||
1166 | |||
1167 | if (!idle_notified) | ||
1168 | { | ||
1169 | call_storage_idle_notifys(false); | ||
1170 | idle_notified = true; | ||
1171 | } | ||
1172 | } | ||
1173 | break; | ||
1174 | case SYS_USB_CONNECTED: | ||
1175 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
1176 | /* Wait until the USB cable is extracted again */ | ||
1177 | usb_wait_for_disconnect(&sd_queue); | ||
1178 | break; | ||
1179 | |||
1180 | #ifdef SD_DRIVER_CLOSE | ||
1181 | case Q_CLOSE: | ||
1182 | return; | ||
1183 | #endif | ||
1184 | } | ||
1185 | } | ||
1186 | } | ||
1187 | |||
1188 | #ifdef SD_DRIVER_CLOSE | ||
1189 | void sd_close(void) | ||
1190 | { | ||
1191 | unsigned int thread_id = sd_thread_id; | ||
1192 | |||
1193 | if (thread_id == 0) | ||
1194 | return; | ||
1195 | |||
1196 | sd_thread_id = 0; | ||
1197 | |||
1198 | queue_post(&sd_queue, Q_CLOSE, 0); | ||
1199 | thread_wait(thread_id); | ||
1200 | } | ||
1201 | #endif /* SD_DRIVER_CLOSE */ | ||
1202 | |||
1203 | void sd_enable(bool on) | ||
1204 | { | ||
1205 | if(on) | ||
1206 | { | ||
1207 | DEV_EN |= DEV_ATA; /* Enable controller */ | ||
1208 | } | ||
1209 | else | ||
1210 | { | ||
1211 | DEV_EN &= ~DEV_ATA; /* Disable controller */ | ||
1212 | } | ||
1213 | } | ||
1214 | |||
1215 | |||
1216 | int sd_init(void) | ||
1217 | { | ||
1218 | int ret = 0; | ||
1219 | |||
1220 | if (!initialized) | ||
1221 | mutex_init(&sd_mtx); | ||
1222 | |||
1223 | mutex_lock(&sd_mtx); | ||
1224 | |||
1225 | led(false); | ||
1226 | |||
1227 | if (!initialized) | ||
1228 | { | ||
1229 | initialized = true; | ||
1230 | |||
1231 | /* init controller */ | ||
1232 | #if defined(PHILIPS_SA9200) | ||
1233 | GPIOA_ENABLE = 0x00; | ||
1234 | GPIO_SET_BITWISE(GPIOD_ENABLE, 0x01); | ||
1235 | #else | ||
1236 | outl(inl(0x70000088) & ~(0x4), 0x70000088); | ||
1237 | outl(inl(0x7000008c) & ~(0x4), 0x7000008c); | ||
1238 | GPO32_ENABLE |= 0x4; | ||
1239 | |||
1240 | GPIO_SET_BITWISE(GPIOG_ENABLE, (0x3 << 5)); | ||
1241 | GPIO_SET_BITWISE(GPIOG_OUTPUT_EN, (0x3 << 5)); | ||
1242 | GPIO_SET_BITWISE(GPIOG_OUTPUT_VAL, (0x3 << 5)); | ||
1243 | #endif | ||
1244 | |||
1245 | #ifdef HAVE_HOTSWAP | ||
1246 | /* enable card detection port - mask interrupt first */ | ||
1247 | #ifdef SANSA_E200 | ||
1248 | GPIO_CLEAR_BITWISE(GPIOA_INT_EN, 0x80); | ||
1249 | |||
1250 | GPIO_CLEAR_BITWISE(GPIOA_OUTPUT_EN, 0x80); | ||
1251 | GPIO_SET_BITWISE(GPIOA_ENABLE, 0x80); | ||
1252 | #elif defined SANSA_C200 | ||
1253 | GPIO_CLEAR_BITWISE(GPIOL_INT_EN, 0x08); | ||
1254 | |||
1255 | GPIO_CLEAR_BITWISE(GPIOL_OUTPUT_EN, 0x08); | ||
1256 | GPIO_SET_BITWISE(GPIOL_ENABLE, 0x08); | ||
1257 | #endif | ||
1258 | #endif | ||
1259 | sd_select_device(0); | ||
1260 | |||
1261 | if (currcard->initialized < 0) | ||
1262 | ret = currcard->initialized; | ||
1263 | |||
1264 | queue_init(&sd_queue, true); | ||
1265 | sd_thread_id = create_thread(sd_thread, sd_stack, sizeof(sd_stack), | ||
1266 | 0, sd_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) | ||
1267 | IF_COP(, CPU)); | ||
1268 | |||
1269 | /* enable interupt for the mSD card */ | ||
1270 | sleep(HZ/10); | ||
1271 | #ifdef HAVE_HOTSWAP | ||
1272 | #ifdef SANSA_E200 | ||
1273 | CPU_INT_EN = HI_MASK; | ||
1274 | CPU_HI_INT_EN = GPIO0_MASK; | ||
1275 | |||
1276 | GPIOA_INT_LEV = (0x80 << 8) | (~GPIOA_INPUT_VAL & 0x80); | ||
1277 | |||
1278 | GPIOA_INT_CLR = 0x80; | ||
1279 | |||
1280 | /* enable the card detect interrupt */ | ||
1281 | GPIO_SET_BITWISE(GPIOA_INT_EN, 0x80); | ||
1282 | #elif defined SANSA_C200 | ||
1283 | CPU_INT_EN = HI_MASK; | ||
1284 | CPU_HI_INT_EN = GPIO2_MASK; | ||
1285 | |||
1286 | GPIOL_INT_LEV = (0x08 << 8) | (~GPIOL_INPUT_VAL & 0x08); | ||
1287 | |||
1288 | GPIOL_INT_CLR = 0x08; | ||
1289 | |||
1290 | /* enable the card detect interrupt */ | ||
1291 | GPIO_SET_BITWISE(GPIOL_INT_EN, 0x08); | ||
1292 | #endif | ||
1293 | #endif | ||
1294 | } | ||
1295 | |||
1296 | mutex_unlock(&sd_mtx); | ||
1297 | |||
1298 | return ret; | ||
1299 | } | ||
1300 | |||
1301 | tCardInfo *card_get_info_target(int card_no) | ||
1302 | { | ||
1303 | return &card_info[card_no]; | ||
1304 | } | ||
1305 | #ifdef HAVE_HOTSWAP | ||
1306 | static int sd1_oneshot_callback(struct timeout *tmo) | ||
1307 | { | ||
1308 | (void)tmo; | ||
1309 | |||
1310 | /* This is called only if the state was stable for 300ms - check state | ||
1311 | * and post appropriate event. */ | ||
1312 | if (card_detect_target()) | ||
1313 | queue_broadcast(SYS_HOTSWAP_INSERTED, 0); | ||
1314 | else | ||
1315 | queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0); | ||
1316 | |||
1317 | return 0; | ||
1318 | } | ||
1319 | |||
1320 | /* called on insertion/removal interrupt */ | ||
1321 | void microsd_int(void) | ||
1322 | { | ||
1323 | static struct timeout sd1_oneshot; | ||
1324 | |||
1325 | #ifdef SANSA_E200 | ||
1326 | GPIO_CLEAR_BITWISE(GPIOA_INT_EN, 0x80); | ||
1327 | GPIOA_INT_LEV = (0x80 << 8) | (~GPIOA_INPUT_VAL & 0x80); | ||
1328 | GPIOA_INT_CLR = 0x80; | ||
1329 | GPIO_SET_BITWISE(GPIOA_INT_EN, 0x80); | ||
1330 | |||
1331 | #elif defined SANSA_C200 | ||
1332 | GPIO_CLEAR_BITWISE(GPIOL_INT_EN, 0x08); | ||
1333 | GPIOL_INT_LEV = (0x08 << 8) | (~GPIOL_INPUT_VAL & 0x08); | ||
1334 | GPIOL_INT_CLR = 0x08; | ||
1335 | GPIO_SET_BITWISE(GPIOL_INT_EN, 0x08); | ||
1336 | #endif | ||
1337 | timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0); | ||
1338 | } | ||
1339 | #endif /* HAVE_HOTSWAP */ | ||
1340 | |||
1341 | long sd_last_disk_activity(void) | ||
1342 | { | ||
1343 | return last_disk_activity; | ||
1344 | } | ||
1345 | |||
1346 | #ifdef HAVE_HOTSWAP | ||
1347 | bool sd_removable(IF_MD_NONVOID(int drive)) | ||
1348 | { | ||
1349 | #ifndef HAVE_MULTIDRIVE | ||
1350 | const int drive=0; | ||
1351 | #endif | ||
1352 | return (drive==1); | ||
1353 | } | ||
1354 | |||
1355 | bool sd_present(IF_MD_NONVOID(int drive)) | ||
1356 | { | ||
1357 | #ifndef HAVE_MULTIDRIVE | ||
1358 | const int drive=0; | ||
1359 | #endif | ||
1360 | if(drive==0) | ||
1361 | { | ||
1362 | return true; | ||
1363 | } | ||
1364 | else | ||
1365 | { | ||
1366 | return card_detect_target(); | ||
1367 | } | ||
1368 | } | ||
1369 | #endif | ||
1370 | |||
1371 | #ifdef CONFIG_STORAGE_MULTI | ||
1372 | int sd_num_drives(int first_drive) | ||
1373 | { | ||
1374 | #ifdef HAVE_HOTSWAP | ||
1375 | /* Store which logical drive number(s) we have been assigned */ | ||
1376 | sd_first_drive = first_drive; | ||
1377 | #else | ||
1378 | (void)first_drive; | ||
1379 | #endif | ||
1380 | |||
1381 | #ifdef HAVE_MULTIDRIVE | ||
1382 | return 2; | ||
1383 | #else | ||
1384 | return 1; | ||
1385 | #endif | ||
1386 | } | ||
1387 | #endif | ||
diff --git a/firmware/target/arm/pp/audio-pp.c b/firmware/target/arm/pp/audio-pp.c new file mode 100644 index 0000000000..6b5b082cc7 --- /dev/null +++ b/firmware/target/arm/pp/audio-pp.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "system.h" | ||
22 | #include "cpu.h" | ||
23 | #include "audio.h" | ||
24 | #include "sound.h" | ||
25 | |||
26 | #if INPUT_SRC_CAPS != 0 | ||
27 | void audio_set_output_source(int source) | ||
28 | { | ||
29 | if ((unsigned)source >= AUDIO_NUM_SOURCES) | ||
30 | source = AUDIO_SRC_PLAYBACK; | ||
31 | } /* audio_set_output_source */ | ||
32 | |||
33 | void audio_input_mux(int source, unsigned flags) | ||
34 | { | ||
35 | (void)flags; | ||
36 | /* Prevent pops from unneeded switching */ | ||
37 | static int last_source = AUDIO_SRC_PLAYBACK; | ||
38 | #ifdef HAVE_FMRADIO_REC | ||
39 | bool recording = flags & SRCF_RECORDING; | ||
40 | static bool last_recording = false; | ||
41 | #endif | ||
42 | |||
43 | #if defined(IPOD_COLOR) || defined (IPOD_4G) | ||
44 | /* The usual magic from IPL - I'm guessing this configures the headphone | ||
45 | socket to be input or output. */ | ||
46 | if ((flags & SRCF_RECORDING) && source != AUDIO_SRC_PLAYBACK) | ||
47 | { | ||
48 | /* input */ | ||
49 | GPIO_CLEAR_BITWISE(GPIOI_OUTPUT_VAL, 0x40); | ||
50 | GPIO_CLEAR_BITWISE(GPIOA_OUTPUT_VAL, 0x04); | ||
51 | } | ||
52 | else | ||
53 | { | ||
54 | /* output */ | ||
55 | GPIO_SET_BITWISE(GPIOI_OUTPUT_VAL, 0x40); | ||
56 | GPIO_SET_BITWISE(GPIOA_OUTPUT_VAL, 0x04); | ||
57 | } | ||
58 | #endif /* IPOD_COLOR || IPOD_4G */ | ||
59 | |||
60 | switch (source) | ||
61 | { | ||
62 | default: /* playback - no recording */ | ||
63 | source = AUDIO_SRC_PLAYBACK; | ||
64 | case AUDIO_SRC_PLAYBACK: | ||
65 | #ifdef HAVE_RECORDING | ||
66 | if (source != last_source) | ||
67 | { | ||
68 | audiohw_set_monitor(false); | ||
69 | audiohw_disable_recording(); | ||
70 | } | ||
71 | #endif | ||
72 | break; | ||
73 | #ifdef HAVE_MIC_REC | ||
74 | case AUDIO_SRC_MIC: /* recording only */ | ||
75 | if (source != last_source) | ||
76 | { | ||
77 | audiohw_set_monitor(false); | ||
78 | audiohw_enable_recording(true); /* source mic */ | ||
79 | } | ||
80 | break; | ||
81 | #endif | ||
82 | #ifdef HAVE_LINE_REC | ||
83 | case AUDIO_SRC_LINEIN: /* recording only */ | ||
84 | #if defined(IRIVER_H10) || defined(IRIVER_H10_5GB) | ||
85 | /* Switch line in source to line-in */ | ||
86 | GPIO_SET_BITWISE(GPIOB_OUTPUT_VAL, 0x04); | ||
87 | #endif | ||
88 | if (source != last_source) | ||
89 | { | ||
90 | audiohw_set_monitor(false); | ||
91 | audiohw_enable_recording(false); /* source line */ | ||
92 | } | ||
93 | break; | ||
94 | #endif | ||
95 | #ifdef HAVE_FMRADIO_REC | ||
96 | case AUDIO_SRC_FMRADIO: /* recording and playback */ | ||
97 | #if defined(IRIVER_H10) || defined(IRIVER_H10_5GB) | ||
98 | /* Switch line in source to tuner */ | ||
99 | GPIO_CLEAR_BITWISE(GPIOB_OUTPUT_VAL, 0x04); | ||
100 | /* Set line-in vol to +12dB, which is proper for H10's */ | ||
101 | if (!recording) | ||
102 | audiohw_set_recvol(0x1f, 0x1f, AUDIO_GAIN_LINEIN); | ||
103 | #else /* Set line-in vol to 0dB*/ | ||
104 | if (!recording) | ||
105 | audiohw_set_recvol(0x17, 0x17, AUDIO_GAIN_LINEIN); | ||
106 | #endif | ||
107 | |||
108 | if (source == last_source && recording == last_recording) | ||
109 | break; | ||
110 | |||
111 | last_recording = recording; | ||
112 | |||
113 | #if CONFIG_TUNER & IPOD_REMOTE_TUNER | ||
114 | /* Ipod FM tuner is in the remote connected to line-in */ | ||
115 | audiohw_enable_recording(false); /* source line */ | ||
116 | audiohw_set_monitor(true); /* enable bypass mode */ | ||
117 | #else | ||
118 | if (recording) | ||
119 | { | ||
120 | audiohw_set_monitor(false); /* disable bypass mode */ | ||
121 | audiohw_enable_recording(false); /* select line-in source */ | ||
122 | } | ||
123 | else | ||
124 | { | ||
125 | audiohw_disable_recording(); | ||
126 | audiohw_set_monitor(true); /* enable bypass mode */ | ||
127 | } | ||
128 | #endif | ||
129 | break; | ||
130 | #endif | ||
131 | } /* end switch */ | ||
132 | |||
133 | last_source = source; | ||
134 | } /* audio_input_mux */ | ||
135 | #endif /* INPUT_SRC_CAPS != 0 */ | ||
diff --git a/firmware/target/arm/pp/boot-pp502x-bl-usb.lds b/firmware/target/arm/pp/boot-pp502x-bl-usb.lds new file mode 100644 index 0000000000..e721991c5a --- /dev/null +++ b/firmware/target/arm/pp/boot-pp502x-bl-usb.lds | |||
@@ -0,0 +1,133 @@ | |||
1 | /* Will have been included from boot.lds */ | ||
2 | ENTRY(start) | ||
3 | OUTPUT_FORMAT(elf32-littlearm) | ||
4 | OUTPUT_ARCH(arm) | ||
5 | STARTUP(target/arm/crt0-pp502x-bl-usb.o) | ||
6 | |||
7 | #define DRAMORIG 0x01000000 /* Load at 16 MB */ | ||
8 | #define DRAMSIZE 0x00100000 /* 1MB for bootloader */ | ||
9 | #define MEMEND (MEMORYSIZE*0x100000) /* From virtual mapping at 0 */ | ||
10 | #define NOCACHE_BASE 0x10000000 | ||
11 | #ifndef IRAMORIG | ||
12 | #define IRAMORIG 0x40000000 | ||
13 | #endif | ||
14 | #define IRAMSIZE 0x20000 | ||
15 | #define FLASHORIG 0x001f0000 | ||
16 | #define FLASHSIZE 2M | ||
17 | |||
18 | #define CACHEALIGN_SIZE 16 | ||
19 | |||
20 | MEMORY | ||
21 | { | ||
22 | DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE | ||
23 | IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE | ||
24 | } | ||
25 | |||
26 | SECTIONS | ||
27 | { | ||
28 | . = DRAMORIG; | ||
29 | _loadaddress = . + NOCACHE_BASE; | ||
30 | |||
31 | .text : | ||
32 | { | ||
33 | *(.init.text) | ||
34 | *(.text*) | ||
35 | *(.glue_7) | ||
36 | *(.glue_7t) | ||
37 | . = ALIGN(0x4); | ||
38 | } > DRAM | ||
39 | |||
40 | .rodata : | ||
41 | { | ||
42 | *(.rodata*) | ||
43 | . = ALIGN(0x4); | ||
44 | } > DRAM | ||
45 | |||
46 | .data : | ||
47 | { | ||
48 | *(.data*) | ||
49 | . = ALIGN(0x4); | ||
50 | } > DRAM | ||
51 | |||
52 | /* .ncdata section is placed at uncached physical alias address and is | ||
53 | * loaded at the proper cached virtual address - no copying is | ||
54 | * performed in the init code */ | ||
55 | .ncdata . + NOCACHE_BASE : | ||
56 | { | ||
57 | . = ALIGN(CACHEALIGN_SIZE); | ||
58 | *(.ncdata*) | ||
59 | . = ALIGN(CACHEALIGN_SIZE); | ||
60 | } AT> DRAM | ||
61 | |||
62 | /DISCARD/ . - NOCACHE_BASE : | ||
63 | { | ||
64 | *(.eh_frame) | ||
65 | } > DRAM | ||
66 | |||
67 | _noloaddram = .; | ||
68 | |||
69 | .ibss IRAMORIG (NOLOAD) : | ||
70 | { | ||
71 | _iedata = .; | ||
72 | *(.qharray) | ||
73 | *(.ibss*) | ||
74 | . = ALIGN(0x4); | ||
75 | _iend = .; | ||
76 | } > IRAM | ||
77 | |||
78 | .iram _iend : | ||
79 | { | ||
80 | _iramstart = .; | ||
81 | *(.icode*) | ||
82 | *(.irodata*) | ||
83 | *(.idata*) | ||
84 | _iramend = .; | ||
85 | } > IRAM AT> DRAM | ||
86 | |||
87 | _iramcopy = LOADADDR(.iram); | ||
88 | |||
89 | .loadaddressend : | ||
90 | { | ||
91 | _loadaddressend = . + NOCACHE_BASE; | ||
92 | } AT> DRAM | ||
93 | |||
94 | .stack (NOLOAD) : | ||
95 | { | ||
96 | . = ALIGN(8); | ||
97 | *(.stack) | ||
98 | stackbegin = .; | ||
99 | . += 0x2000; | ||
100 | stackend = .; | ||
101 | } > IRAM | ||
102 | |||
103 | /* .bss and .ncbss are treated as a single section to use one init loop | ||
104 | * to zero them - note "_edata" and "_end" */ | ||
105 | .bss _noloaddram (NOLOAD) : | ||
106 | { | ||
107 | _edata = .; | ||
108 | *(.bss*) | ||
109 | *(COMMON) | ||
110 | } > DRAM | ||
111 | |||
112 | .ncbss . + NOCACHE_BASE (NOLOAD) : | ||
113 | { | ||
114 | . = ALIGN(CACHEALIGN_SIZE); | ||
115 | *(.ncbss*) | ||
116 | . = ALIGN(CACHEALIGN_SIZE); | ||
117 | } AT> DRAM | ||
118 | |||
119 | /* This will be aligned by preceding alignments */ | ||
120 | .endaddr . - NOCACHE_BASE (NOLOAD) : | ||
121 | { | ||
122 | _end = .; | ||
123 | } > DRAM | ||
124 | |||
125 | /* Reference to all DRAM after loaded bootloader image */ | ||
126 | .freebuffer _end (NOLOAD) : | ||
127 | { | ||
128 | . = ALIGN(4); | ||
129 | freebuffer = .; | ||
130 | . = MEMEND-1; | ||
131 | freebufferend = .; | ||
132 | } | ||
133 | } | ||
diff --git a/firmware/target/arm/pp/crt0-pp-bl.S b/firmware/target/arm/pp/crt0-pp-bl.S new file mode 100644 index 0000000000..01681288f9 --- /dev/null +++ b/firmware/target/arm/pp/crt0-pp-bl.S | |||
@@ -0,0 +1,217 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Linus Nielsen Feltzing | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include "cpu.h" | ||
23 | |||
24 | .section .init.text,"ax",%progbits | ||
25 | |||
26 | .global start | ||
27 | start: | ||
28 | |||
29 | /* PortalPlayer bootloader and startup code based on startup.s from the iPodLinux | ||
30 | * loader | ||
31 | * | ||
32 | * Copyright (c) 2003, Daniel Palffy (dpalffy (at) rainstorm.org) | ||
33 | * Copyright (c) 2005, Bernard Leach <leachbj@bouncycastle.org> | ||
34 | * | ||
35 | */ | ||
36 | #if CONFIG_CPU == PP5002 | ||
37 | .equ PROC_ID, 0xc4000000 | ||
38 | .equ CPU_CTRL, 0xcf004054 | ||
39 | .equ CPU_STATUS, 0xcf004050 | ||
40 | .equ COP_CTRL, 0xcf004058 | ||
41 | .equ COP_STATUS, 0xcf004050 | ||
42 | .equ IIS_CONFIG, 0xc0002500 | ||
43 | .equ SLEEP, 0xca | ||
44 | .equ WAKE, 0xce | ||
45 | .equ CPUSLEEPING, 0x8000 | ||
46 | .equ COPSLEEPING, 0x4000 | ||
47 | .equ CACHE_CTRL, 0xcf004024 | ||
48 | .equ CACHE_ENAB, 0x2 /* Actually the CACHE_CTL_INIT flag */ | ||
49 | #else | ||
50 | .equ PROC_ID, 0x60000000 | ||
51 | .equ CPU_CTRL, 0x60007000 | ||
52 | .equ CPU_STATUS, 0x60007000 | ||
53 | .equ COP_CTRL, 0x60007004 | ||
54 | .equ COP_STATUS, 0x60007004 | ||
55 | .equ IIS_CONFIG, 0x70002800 | ||
56 | .equ SLEEP, 0x80000000 | ||
57 | .equ WAKE, 0x0 | ||
58 | .equ CPUSLEEPING, 0x80000000 | ||
59 | .equ COPSLEEPING, 0x80000000 | ||
60 | .equ CACHE_CTRL, 0x6000c000 | ||
61 | .equ CACHE_ENAB, 0x1 | ||
62 | #endif | ||
63 | |||
64 | msr cpsr_c, #0xdf /* enter sys mode, disable IRQ */ | ||
65 | #ifndef E200R_INSTALLER | ||
66 | /* 1 - Copy the bootloader to IRAM */ | ||
67 | /* get the high part of our execute address */ | ||
68 | bic r0, pc, #0xff /* r4 = pc & 0xffffff00 */ | ||
69 | |||
70 | /* Copy bootloader to safe area - 0x40000000 (IRAM) */ | ||
71 | mov r1, #0x40000000 | ||
72 | ldr r2, =_dataend | ||
73 | 1: | ||
74 | cmp r2, r1 | ||
75 | ldrhi r3, [r0], #4 | ||
76 | strhi r3, [r1], #4 | ||
77 | bhi 1b | ||
78 | |||
79 | #ifndef IPOD_ARCH | ||
80 | /* For builds on targets with mi4 firmware, scramble writes data to | ||
81 | 0xe0-0xeb, so jump past that. pad_skip must then exist at an | ||
82 | address >= 0xec */ | ||
83 | b pad_skip | ||
84 | |||
85 | .space 60*4 | ||
86 | |||
87 | pad_skip: | ||
88 | #endif /* IPOD_ARCH */ | ||
89 | |||
90 | |||
91 | /* 2 - Jump both CPU and COP there */ | ||
92 | ldr pc, =start_loc /* jump to the relocated start_loc: */ | ||
93 | #endif /* E200R_INSTALLER */ | ||
94 | |||
95 | start_loc: | ||
96 | /* Find out which processor we are */ | ||
97 | ldr r0, =PROC_ID | ||
98 | ldrb r0, [r0] | ||
99 | cmp r0, #0x55 | ||
100 | beq cpu | ||
101 | |||
102 | cop: | ||
103 | /* put us (co-processor) to sleep */ | ||
104 | ldr r0, =COP_CTRL | ||
105 | mov r1, #SLEEP | ||
106 | str r1, [r0] | ||
107 | nop | ||
108 | nop | ||
109 | |||
110 | /* Invalidate cache */ | ||
111 | mov r0, #1 | ||
112 | bl cache_op | ||
113 | |||
114 | ldr r0, =startup_loc | ||
115 | ldr pc, [r0] | ||
116 | |||
117 | cpu: | ||
118 | /* Wait for COP to be sleeping */ | ||
119 | ldr r0, =COP_STATUS | ||
120 | 1: | ||
121 | ldr r1, [r0] | ||
122 | tst r1, #COPSLEEPING | ||
123 | beq 1b | ||
124 | |||
125 | /* Initialise bss section to zero */ | ||
126 | ldr r0, =_edata | ||
127 | ldr r1, =_end | ||
128 | mov r2, #0 | ||
129 | 1: | ||
130 | cmp r1, r0 | ||
131 | strhi r2, [r0], #4 | ||
132 | bhi 1b | ||
133 | |||
134 | /* Set up some stack and munge it with 0xdeadbeef */ | ||
135 | ldr sp, =stackend | ||
136 | ldr r0, =stackbegin | ||
137 | ldr r1, =0xdeadbeef | ||
138 | 1: | ||
139 | cmp sp, r0 | ||
140 | strhi r1, [r0], #4 | ||
141 | bhi 1b | ||
142 | |||
143 | /* execute the loader - this will load an image to 0x10000000 */ | ||
144 | bl main | ||
145 | |||
146 | /* store actual startup location returned by main() */ | ||
147 | ldr r1, =startup_loc | ||
148 | str r0, [r1] | ||
149 | |||
150 | /* flush cache */ | ||
151 | mov r0, #0 | ||
152 | bl cache_op | ||
153 | |||
154 | /* Wake up the coprocessor before executing the firmware */ | ||
155 | ldr r0, =COP_CTRL | ||
156 | mov r1, #WAKE | ||
157 | str r1, [r0] | ||
158 | |||
159 | #if defined(SANSA_C200) || defined(PHILIPS_HDD1630) | ||
160 | /* Magic for loading the c200 OF */ | ||
161 | ldr r0, =0xb00d10ad | ||
162 | mov r1, #0x700 | ||
163 | ldr r2, =0xfff0 | ||
164 | mov r3, #0x7 | ||
165 | #endif | ||
166 | |||
167 | #if defined(PHILIPS_HDD6330) | ||
168 | /* Magic for loading the HDD6XX0 OF */ | ||
169 | ldr r0, =0xb00d10ad | ||
170 | mov r1, #0x800 | ||
171 | ldr r2, =0xfff0 | ||
172 | mov r3, #0x7 | ||
173 | #endif | ||
174 | |||
175 | ldr r4, =startup_loc | ||
176 | ldr pc, [r4] | ||
177 | |||
178 | startup_loc: | ||
179 | .word 0x0 | ||
180 | |||
181 | #ifdef IPOD_ARCH | ||
182 | .align 8 /* starts at 0x100 */ | ||
183 | .global boot_table | ||
184 | boot_table: | ||
185 | /* here comes the boot table, don't move its offset - preceding | ||
186 | code+data must stay <= 256 bytes */ | ||
187 | .space 400 | ||
188 | #endif | ||
189 | |||
190 | cache_op: | ||
191 | ldr r2, =CACHE_CTRL | ||
192 | ldr r1, [r2] | ||
193 | tst r1, #CACHE_ENAB | ||
194 | bxeq lr | ||
195 | cmp r0, #0 | ||
196 | #ifdef CPU_PP502x | ||
197 | ldr r0, =0xf000f044 | ||
198 | ldr r1, [r0] | ||
199 | orrne r1, r1, #0x6 | ||
200 | orreq r1, r1, #0x2 | ||
201 | str r1, [r0] | ||
202 | 1: | ||
203 | ldr r1, [r2] | ||
204 | tst r1, #0x8000 | ||
205 | bne 1b | ||
206 | #elif CONFIG_CPU == PP5002 | ||
207 | ldrne r0, =0xf0004000 | ||
208 | ldreq r0, =0xf000c000 | ||
209 | add r1, r0, #0x2000 | ||
210 | mov r2, #0 | ||
211 | 1: | ||
212 | cmp r1, r0 | ||
213 | strhi r2, [r0], #16 | ||
214 | bhi 1b | ||
215 | #endif /* CPU type */ | ||
216 | bx lr | ||
217 | |||
diff --git a/firmware/target/arm/pp/crt0-pp.S b/firmware/target/arm/pp/crt0-pp.S new file mode 100644 index 0000000000..5a9835a71f --- /dev/null +++ b/firmware/target/arm/pp/crt0-pp.S | |||
@@ -0,0 +1,430 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Linus Nielsen Feltzing | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include "cpu.h" | ||
23 | |||
24 | .section .init.text,"ax",%progbits | ||
25 | |||
26 | .global start | ||
27 | start: | ||
28 | |||
29 | /* PortalPlayer bootloader and startup code based on startup.s from the iPodLinux | ||
30 | * loader | ||
31 | * | ||
32 | * Copyright (c) 2003, Daniel Palffy (dpalffy (at) rainstorm.org) | ||
33 | * Copyright (c) 2005, Bernard Leach <leachbj@bouncycastle.org> | ||
34 | * | ||
35 | */ | ||
36 | #if CONFIG_CPU == PP5002 | ||
37 | .equ PROC_ID, 0xc4000000 | ||
38 | .equ CPU_ICLR, 0xcf001028 | ||
39 | .equ CPU_CTRL, 0xcf004054 | ||
40 | .equ COP_ICLR, 0xcf001038 | ||
41 | .equ COP_CTRL, 0xcf004058 | ||
42 | .equ CPU_STATUS, 0xcf004050 | ||
43 | .equ COP_STATUS, 0xcf004050 | ||
44 | .equ SLEEP, 0x000000ca | ||
45 | .equ WAKE, 0x000000ce | ||
46 | .equ CPUSLEEPING, 0x00008000 | ||
47 | .equ COPSLEEPING, 0x00004000 | ||
48 | .equ CACHE_CTRL, 0xcf004024 | ||
49 | .equ MMAP_LOG, 0xf000f000 /* MMAP0 */ | ||
50 | .equ MMAP_PHYS, 0xf000f004 | ||
51 | #if MEMORYSIZE > 32 | ||
52 | .equ MMAP_MASK, 0x00003c00 | ||
53 | #else | ||
54 | .equ MMAP_MASK, 0x00003e00 | ||
55 | #endif | ||
56 | .equ MMAP_FLAGS, 0x00003f84 | ||
57 | #else | ||
58 | .equ PROC_ID, 0x60000000 | ||
59 | .equ CPU_ICLR, 0x60004028 | ||
60 | .equ CPU_CTRL, 0x60007000 | ||
61 | .equ CPU_STATUS, 0x60007000 | ||
62 | .equ COP_ICLR, 0x60004038 | ||
63 | .equ COP_CTRL, 0x60007004 | ||
64 | .equ COP_STATUS, 0x60007004 | ||
65 | .equ SLEEP, 0x80000000 | ||
66 | .equ WAKE, 0x00000000 | ||
67 | .equ CPUSLEEPING, 0x80000000 | ||
68 | .equ COPSLEEPING, 0x80000000 | ||
69 | .equ CACHE_CTRL, 0x6000c000 | ||
70 | .equ MMAP_LOG, 0xf000f000 /* MMAP0 */ | ||
71 | .equ MMAP_PHYS, 0xf000f004 | ||
72 | #if MEMORYSIZE > 32 | ||
73 | .equ MMAP_MASK, 0x00003c00 | ||
74 | #else | ||
75 | .equ MMAP_MASK, 0x00003e00 | ||
76 | #endif | ||
77 | .equ MMAP_FLAGS, 0x00000f84 | ||
78 | #endif | ||
79 | |||
80 | msr cpsr_c, #0xd3 /* enter supervisor mode, disable IRQ/FIQ */ | ||
81 | b pad_skip | ||
82 | |||
83 | .space 6*4 /* pad to offset 0x20 */ | ||
84 | |||
85 | .ascii "Rockbox" /* signature for bootloader checking osos */ | ||
86 | .byte 1 /* osos boot version, only 1 exists for now */ | ||
87 | |||
88 | .space 56*4 /* (more than enough) space for exception vectors and mi4 magic */ | ||
89 | |||
90 | pad_skip: | ||
91 | /* Find out which processor we are - r0 should be preserved for the | ||
92 | * duration of the init to avoid constant reloading of the processor ID. | ||
93 | * For each stage, CPU proceeds first, then COP. | ||
94 | */ | ||
95 | ldr r0, =PROC_ID | ||
96 | ldrb r0, [r0] | ||
97 | |||
98 | /* We need to remap memory from wherever SDRAM is mapped natively, to | ||
99 | base address 0, so we can put our exception vectors there. We don't | ||
100 | want to do this remapping while executing from SDRAM, so we copy the | ||
101 | remapping code to IRAM, then execute from there. Hence, the following | ||
102 | code is compiled for address 0, but is currently executing at either | ||
103 | 0x28000000 or 0x10000000, depending on chipset version. Do not use any | ||
104 | absolute addresses until remapping has been done. */ | ||
105 | |||
106 | /* Cores are stepped though the init in turn: CPU then COP. The the remap | ||
107 | stage is completed by each core in turn and then the COP waits for the | ||
108 | CPU to finish initializing its kernel where the CPU will wake the COP | ||
109 | and wait for the COP to finish. This ensures no threading activity | ||
110 | starts until it is safe. */ | ||
111 | cmp r0, #0x55 | ||
112 | |||
113 | /* mask all interrupt sources before setting anything up */ | ||
114 | ldreq r2, =CPU_ICLR | ||
115 | ldrne r2, =COP_ICLR | ||
116 | mvn r1, #0 | ||
117 | str r1, [r2] | ||
118 | |||
119 | /* put us (co-processor) to sleep and wait for CPU to remap */ | ||
120 | ldrne r2, =COP_CTRL | ||
121 | movne r1, #SLEEP | ||
122 | strne r1, [r2] | ||
123 | nop | ||
124 | nop | ||
125 | nop | ||
126 | |||
127 | /* wait for co-processor to sleep then CPU can begin its remapping */ | ||
128 | ldreq r2, =COP_STATUS | ||
129 | 1: | ||
130 | ldreq r1, [r2] | ||
131 | tsteq r1, #COPSLEEPING | ||
132 | beq 1b | ||
133 | |||
134 | /* disable cache and local interrupt vectors - it is really not desireable | ||
135 | to have them enabled here */ | ||
136 | ldr r2, =CACHE_CTRL | ||
137 | mov r1, #0 | ||
138 | str r1, [r2] | ||
139 | |||
140 | #if defined(IPOD_VIDEO) | ||
141 | /* detect 32mb vs 64mb model */ | ||
142 | /* we do this here because after SDRAM is remapped, we already assumed */ | ||
143 | /* its size to be whatever we were compiled for. */ | ||
144 | |||
145 | mov r2, #0x12000000 | ||
146 | mov r3, #64 | ||
147 | strb r3, [r2, #-1] /* first write 64 to last byte of first 32MB bank */ | ||
148 | |||
149 | mov r2, #0x14000000 | ||
150 | mov r3, #32 | ||
151 | strb r3, [r2, #-1] /* now write 32 to last byte of second 32MB bank */ | ||
152 | |||
153 | /* now the last word of the first 32MB bank tells you the RAM size */ | ||
154 | /* since on a 32MB model both writes will touch the same actual location */ | ||
155 | /* this is read later on in boot */ | ||
156 | #endif | ||
157 | |||
158 | mov r2, #0x40000000 | ||
159 | ldr r3, =remap_start | ||
160 | ldr r4, =remap_end | ||
161 | |||
162 | and r6, pc, #0xff000000 /* adjust for execute address */ | ||
163 | orr r3, r3, r6 | ||
164 | orr r4, r4, r6 | ||
165 | |||
166 | /* copy the code to 0x40000000 */ | ||
167 | 1: | ||
168 | ldr r5, [r3], #4 | ||
169 | str r5, [r2], #4 | ||
170 | cmp r3, r4 | ||
171 | blo 1b | ||
172 | |||
173 | ldr r4, =MMAP_FLAGS | ||
174 | orr r4, r4, r6 /* adjust for execute address */ | ||
175 | ldr r3, =MMAP_PHYS | ||
176 | ldr r2, =MMAP_MASK /* ldr is more flexible */ | ||
177 | ldr r1, =MMAP_LOG | ||
178 | mov pc, #0x40000000 | ||
179 | |||
180 | remap_start: | ||
181 | str r2, [r1] | ||
182 | str r4, [r3] | ||
183 | ldr r1, L_post_remap | ||
184 | bx r1 | ||
185 | L_post_remap: | ||
186 | .word remap_end | ||
187 | remap_end: | ||
188 | |||
189 | cmp r0, #0x55 | ||
190 | ldr r4, =COP_CTRL | ||
191 | /* Wakeup co-processor to let it do remappings */ | ||
192 | moveq r3, #WAKE | ||
193 | /* Sleep us (co-processor) and wait for CPU to do kernel initialization */ | ||
194 | movne r3, #SLEEP | ||
195 | str r3, [r4] | ||
196 | nop | ||
197 | nop | ||
198 | nop | ||
199 | |||
200 | /* Jump to co-processor init */ | ||
201 | ldrne pc, =cop_init | ||
202 | |||
203 | cpu_init: | ||
204 | /* Wait for COP to go to sleep before proceeding */ | ||
205 | ldr r4, =COP_STATUS | ||
206 | 1: | ||
207 | ldr r3, [r4] | ||
208 | tst r3, #COPSLEEPING | ||
209 | beq 1b | ||
210 | |||
211 | /* Vectors and IRAM copy is done first since they are reclaimed for | ||
212 | * other uninitialized sections */ | ||
213 | |||
214 | /* Copy exception handler code to address 0 */ | ||
215 | ldr r2, =_vectorsstart | ||
216 | ldr r3, =_vectorsend | ||
217 | ldr r4, =_vectorscopy | ||
218 | 1: | ||
219 | cmp r3, r2 | ||
220 | ldrhi r5, [r4], #4 | ||
221 | strhi r5, [r2], #4 | ||
222 | bhi 1b | ||
223 | |||
224 | /* Copy the IRAM */ | ||
225 | ldr r2, =_iramcopy | ||
226 | ldr r3, =_iramstart | ||
227 | ldr r4, =_iramend | ||
228 | 1: | ||
229 | cmp r4, r3 | ||
230 | ldrhi r5, [r2], #4 | ||
231 | strhi r5, [r3], #4 | ||
232 | bhi 1b | ||
233 | |||
234 | #ifdef HAVE_INIT_ATTR | ||
235 | /* copy init code to codec buffer */ | ||
236 | ldr r2, =_initstart | ||
237 | ldr r3, =_initend | ||
238 | ldr r4, =_initcopy | ||
239 | |||
240 | 1: | ||
241 | cmp r3, r2 | ||
242 | ldrhi r5, [r4], #4 | ||
243 | strhi r5, [r2], #4 | ||
244 | bhi 1b | ||
245 | #endif | ||
246 | |||
247 | /* Zero out IBSS */ | ||
248 | ldr r2, =_iedata | ||
249 | ldr r3, =_iend | ||
250 | mov r4, #0 | ||
251 | 1: | ||
252 | cmp r3, r2 | ||
253 | strhi r4, [r2], #4 | ||
254 | bhi 1b | ||
255 | |||
256 | /* Initialise bss section to zero */ | ||
257 | ldr r2, =_edata | ||
258 | ldr r3, =_end | ||
259 | mov r4, #0 | ||
260 | 1: | ||
261 | cmp r3, r2 | ||
262 | strhi r4, [r2], #4 | ||
263 | bhi 1b | ||
264 | |||
265 | /* Load stack munge value */ | ||
266 | ldr r4, =0xdeadbeef | ||
267 | |||
268 | #if NUM_CORES > 1 | ||
269 | /* Set up idle stack and munge it with 0xdeadbeef */ | ||
270 | ldr r2, =cpu_idlestackbegin | ||
271 | ldr r3, =cpu_idlestackend | ||
272 | 1: | ||
273 | cmp r3, r2 | ||
274 | strhi r4, [r2], #4 | ||
275 | bhi 1b | ||
276 | #endif | ||
277 | |||
278 | /* Set up stack for IRQ mode */ | ||
279 | msr cpsr_c, #0x92 /* IRQ disabled, FIQ enabled */ | ||
280 | ldr sp, =irq_stack | ||
281 | /* Set up stack for FIQ mode */ | ||
282 | msr cpsr_c, #0xd1 /* IRQ/FIQ disabled */ | ||
283 | ldr sp, =fiq_stack | ||
284 | |||
285 | /* Let svc, abort and undefined modes use irq stack */ | ||
286 | msr cpsr_c, #0xd3 /* IRQ/FIQ disabled */ | ||
287 | ldr sp, =irq_stack | ||
288 | msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */ | ||
289 | ldr sp, =irq_stack | ||
290 | msr cpsr_c, #0xdb /* IRQ/FIQ disabled */ | ||
291 | ldr sp, =irq_stack | ||
292 | |||
293 | /* Switch to sys mode */ | ||
294 | msr cpsr_c, #0xdf | ||
295 | |||
296 | /* Set up some stack and munge it with 0xdeadbeef */ | ||
297 | ldr r2, =stackbegin | ||
298 | ldr sp, =stackend | ||
299 | 1: | ||
300 | cmp sp, r2 | ||
301 | strhi r4, [r2], #4 | ||
302 | bhi 1b | ||
303 | |||
304 | /* Delay waking the COP until thread initialization is complete unless dual-core | ||
305 | support is not enabled in which case the cop_main function does not perform | ||
306 | any kernel or thread initialization. It's just a trivial sleep loop. */ | ||
307 | #if NUM_CORES == 1 | ||
308 | ldr r4, =COP_CTRL | ||
309 | mov r3, #WAKE | ||
310 | str r3, [r4] | ||
311 | #endif | ||
312 | |||
313 | ldr pc, =main | ||
314 | /* main() should never return */ | ||
315 | |||
316 | cop_init: | ||
317 | #if NUM_CORES > 1 | ||
318 | /* Wait for CPU to go to sleep at the end of its kernel init */ | ||
319 | ldr r4, =CPU_STATUS | ||
320 | 1: | ||
321 | ldr r3, [r4] | ||
322 | tst r3, #CPUSLEEPING | ||
323 | beq 1b | ||
324 | #endif | ||
325 | |||
326 | /* Set up stack for IRQ mode */ | ||
327 | msr cpsr_c, #0x92 /* IRQ disabled, FIQ enabled */ | ||
328 | ldr sp, =cop_irq_stack | ||
329 | /* Set up stack for FIQ mode */ | ||
330 | msr cpsr_c, #0xd1 /* IRQ/FIQ disabled */ | ||
331 | ldr sp, =cop_fiq_stack | ||
332 | |||
333 | /* Let svc, abort and undefined modes use irq stack */ | ||
334 | msr cpsr_c, #0xd3 /* IRQ/FIQ disabled */ | ||
335 | ldr sp, =cop_irq_stack | ||
336 | msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */ | ||
337 | ldr sp, =cop_irq_stack | ||
338 | msr cpsr_c, #0xdb /* IRQ/FIQ disabled */ | ||
339 | ldr sp, =cop_irq_stack | ||
340 | |||
341 | /* Switch to sys mode */ | ||
342 | msr cpsr_c, #0xdf | ||
343 | |||
344 | /* Set up idle stack for COP and munge it with 0xdeadbeef */ | ||
345 | ldr sp, =cop_idlestackend | ||
346 | ldr r2, =cop_idlestackbegin | ||
347 | ldr r4, =0xdeadbeef | ||
348 | 2: | ||
349 | cmp sp, r2 | ||
350 | strhi r4, [r2], #4 | ||
351 | bhi 2b | ||
352 | |||
353 | /* Run cop_main() in apps/main.c */ | ||
354 | ldr pc, =cop_main | ||
355 | |||
356 | /* Exception handlers. Will be copied to address 0 after memory remapping */ | ||
357 | .section .vectors,"aw" | ||
358 | ldr pc, [pc, #24] | ||
359 | ldr pc, [pc, #24] | ||
360 | ldr pc, [pc, #24] | ||
361 | ldr pc, [pc, #24] | ||
362 | ldr pc, [pc, #24] | ||
363 | ldr pc, [pc, #24] | ||
364 | ldr pc, [pc, #24] | ||
365 | ldr pc, [pc, #24] | ||
366 | |||
367 | /* Exception vectors */ | ||
368 | .global vectors | ||
369 | vectors: | ||
370 | .word start | ||
371 | .word undef_instr_handler | ||
372 | .word software_int_handler | ||
373 | .word prefetch_abort_handler | ||
374 | .word data_abort_handler | ||
375 | .word reserved_handler | ||
376 | .word irq_handler | ||
377 | .word fiq_handler | ||
378 | |||
379 | .text | ||
380 | |||
381 | /* All illegal exceptions call into UIE with exception address as first | ||
382 | parameter. This is calculated differently depending on which exception | ||
383 | we're in. Second parameter is exception number, used for a string lookup | ||
384 | in UIE. | ||
385 | */ | ||
386 | undef_instr_handler: | ||
387 | sub r0, lr, #4 | ||
388 | mov r1, #0 | ||
389 | b UIE | ||
390 | |||
391 | /* We run sys mode most of the time, and should never see a software | ||
392 | exception being thrown. Make it illegal and call UIE. | ||
393 | */ | ||
394 | software_int_handler: | ||
395 | reserved_handler: | ||
396 | sub r0, lr, #4 | ||
397 | mov r1, #4 | ||
398 | b UIE | ||
399 | |||
400 | prefetch_abort_handler: | ||
401 | sub r0, lr, #4 | ||
402 | mov r1, #1 | ||
403 | b UIE | ||
404 | |||
405 | data_abort_handler: | ||
406 | sub r0, lr, #8 | ||
407 | mov r1, #2 | ||
408 | b UIE | ||
409 | |||
410 | /* Align stacks to cache line boundary */ | ||
411 | .balign 32 | ||
412 | |||
413 | /* 256 words of IRQ stack */ | ||
414 | .space 256*4 | ||
415 | irq_stack: | ||
416 | |||
417 | /* 256 words of COP IRQ stack */ | ||
418 | .space 256*4 | ||
419 | cop_irq_stack: | ||
420 | |||
421 | /* 256 words of FIQ stack */ | ||
422 | .space 256*4 | ||
423 | fiq_stack: | ||
424 | |||
425 | /* We'll need this soon - just reserve the symbol */ | ||
426 | #if 0 | ||
427 | /* 256 words of COP FIQ stack */ | ||
428 | .space 256*4 | ||
429 | #endif | ||
430 | cop_fiq_stack: | ||
diff --git a/firmware/target/arm/pp/crt0-pp502x-bl-usb.S b/firmware/target/arm/pp/crt0-pp502x-bl-usb.S new file mode 100644 index 0000000000..7b0489b2a8 --- /dev/null +++ b/firmware/target/arm/pp/crt0-pp502x-bl-usb.S | |||
@@ -0,0 +1,367 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Linus Nielsen Feltzing | ||
11 | * Copyright (C) 2010 by Michael Sevakis | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #include "config.h" | ||
23 | #include "cpu.h" | ||
24 | |||
25 | /* PortalPlayer bootloader and startup code based on startup.s from the iPodLinux | ||
26 | * loader | ||
27 | * | ||
28 | * Copyright (c) 2003, Daniel Palffy (dpalffy (at) rainstorm.org) | ||
29 | * Copyright (c) 2005, Bernard Leach <leachbj@bouncycastle.org> | ||
30 | * | ||
31 | */ | ||
32 | .equ PROC_ID, 0x60000000 | ||
33 | .equ CPU_IDIS, 0x60004028 | ||
34 | .equ CPU_CTRL, 0x60007000 | ||
35 | .equ CPU_STATUS, 0x60007000 | ||
36 | .equ COP_IDIS, 0x60004038 | ||
37 | .equ COP_CTRL, 0x60007004 | ||
38 | .equ COP_STATUS, 0x60007004 | ||
39 | .equ CPU_SLEEPING,0x80000000 | ||
40 | .equ COP_SLEEPING,0x80000000 | ||
41 | .equ SLEEP, 0x80000000 | ||
42 | .equ WAKE, 0x00000000 | ||
43 | .equ MMAP_LOG, 0xf000f000 /* MMAP0 */ | ||
44 | .equ MMAP_PHYS, 0xf000f004 | ||
45 | .equ INT_VECT_TBL,0x6000f100 | ||
46 | .equ CACHE_CTRL, 0x6000c000 | ||
47 | .equ CACHE_ENAB, 0x1 | ||
48 | .equ CACHE_OP_COMMIT_DISCARD, 0x1 | ||
49 | .equ CACHE_OP_COMMIT , 0x0 | ||
50 | #if MEMORYSIZE > 32 | ||
51 | .equ MMAP_MASK, 0x00003c00 | ||
52 | #else | ||
53 | .equ MMAP_MASK, 0x00003e00 | ||
54 | #endif | ||
55 | .equ MMAP_FLAGS, 0x00000f84 | ||
56 | |||
57 | /* | ||
58 | * Entry point | ||
59 | */ | ||
60 | .section .init.text,"ax",%progbits | ||
61 | .global start | ||
62 | start: | ||
63 | b newstart | ||
64 | |||
65 | #ifdef IPOD_ARCH | ||
66 | .align 8 /* starts at 0x100 */ | ||
67 | .global boot_table | ||
68 | boot_table: | ||
69 | /* here comes the boot table, don't move its offset - preceding | ||
70 | code+data must stay <= 256 bytes */ | ||
71 | .space 400 | ||
72 | #else /* !IPOD_ARCH */ | ||
73 | /* (more than enough) space for exception vectors and mi4 magic */ | ||
74 | .space 68*4 | ||
75 | #endif /* IPOD_ARCH */ | ||
76 | |||
77 | newstart: | ||
78 | msr cpsr_c, #0xd3 /* enter supervisor mode, disable IRQ/FIQ */ | ||
79 | adr r4, start /* cache initial load address */ | ||
80 | |||
81 | /* Copy startup stub to IRAM since we need to both move the bootloader's | ||
82 | * location, which could overlap itself, and setup the memory mapper. */ | ||
83 | adr r0, start_stub_begin | ||
84 | mov r1, #0x40000000 | ||
85 | adr r2, start_stub_end | ||
86 | 1: | ||
87 | ldr r3, [r0], #4 | ||
88 | str r3, [r1], #4 | ||
89 | cmp r0, r2 | ||
90 | blo 1b | ||
91 | mov pc, #0x40000000 | ||
92 | |||
93 | start_stub_begin: | ||
94 | ldr r0, =PROC_ID | ||
95 | ldrb r0, [r0] | ||
96 | cmp r0, #0x55 | ||
97 | beq cpu | ||
98 | |||
99 | cop: | ||
100 | mov r0, #CACHE_OP_COMMIT_DISCARD | ||
101 | bl cache_operation | ||
102 | |||
103 | ldr r1, =COP_CTRL | ||
104 | mov r0, #SLEEP | ||
105 | |||
106 | /* sleep us (co-processor) while bootloader is copied */ | ||
107 | str r0, [r1] | ||
108 | nop | ||
109 | nop | ||
110 | nop | ||
111 | |||
112 | /* branch to final physical load address */ | ||
113 | ldr r2, =1f | ||
114 | and r4, r4, #0xfc000000 | ||
115 | add pc, r2, r4 | ||
116 | 1: | ||
117 | /* wait for bootloader to finish */ | ||
118 | str r0, [r1] | ||
119 | nop | ||
120 | nop | ||
121 | nop | ||
122 | |||
123 | /* branch to the address returned by main() */ | ||
124 | adr r0, startup_loc | ||
125 | ldr pc, [r0] | ||
126 | |||
127 | cpu: | ||
128 | /* wait for COP to sleep */ | ||
129 | ldr r1, =COP_STATUS | ||
130 | 1: | ||
131 | ldr r0, [r1] | ||
132 | tst r0, #COP_SLEEPING | ||
133 | beq 1b | ||
134 | |||
135 | mov r0, #CACHE_OP_COMMIT_DISCARD | ||
136 | bl cache_operation | ||
137 | |||
138 | /* move bootloader to the correct load address if needed */ | ||
139 | ldr r1, =_loadaddress | ||
140 | cmp r4, r1 | ||
141 | ldrne r2, =_loadaddressend | ||
142 | movne r0, r4 | ||
143 | sublo r3, r2, r1 /* size */ | ||
144 | addlo r0, r0, r3 /* initial load end addr */ | ||
145 | 1: /* lower to higher move - copy up */ | ||
146 | cmphi r2, r1 | ||
147 | ldrhi r3, [r0], #4 | ||
148 | strhi r3, [r1], #4 | ||
149 | bhi 1b | ||
150 | 1: /* higher to lower move - copy down */ | ||
151 | cmplo r1, r2 | ||
152 | ldrlo r3, [r0, #-4]! | ||
153 | strlo r3, [r2, #-4]! | ||
154 | blo 1b | ||
155 | |||
156 | mov r0, #CACHE_OP_COMMIT | ||
157 | bl cache_operation | ||
158 | |||
159 | and r4, r4, #0xfc000000 | ||
160 | |||
161 | ldr r0, =MMAP_FLAGS | ||
162 | orr r0, r0, r4 /* adjust for execute address */ | ||
163 | ldr r1, =MMAP_MASK | ||
164 | ldr r2, =MMAP_LOG | ||
165 | ldr r3, =MMAP_PHYS | ||
166 | str r1, [r2] /* MMAP_LOG = MMAP_MASK */ | ||
167 | str r0, [r3] /* MMAP_PHYS = MMAP_FLAGS | SDRAM base addr */ | ||
168 | |||
169 | /* wake the COP to jump it to the correct place */ | ||
170 | ldr r1, =COP_CTRL | ||
171 | mov r0, #WAKE | ||
172 | str r0, [r1] | ||
173 | |||
174 | /* wait for COP to halt then loading may proceed */ | ||
175 | ldr r1, =COP_STATUS | ||
176 | 1: | ||
177 | ldr r0, [r1] | ||
178 | tst r0, #COP_SLEEPING | ||
179 | beq 1b | ||
180 | |||
181 | ldr r0, =start_stub_end | ||
182 | add pc, r0, r4 | ||
183 | |||
184 | cache_operation: /* (bool commit_discard) */ | ||
185 | ldr r2, =CACHE_CTRL | ||
186 | ldr r1, [r2] | ||
187 | tst r1, #CACHE_ENAB | ||
188 | bxeq lr | ||
189 | cmp r0, #CACHE_OP_COMMIT | ||
190 | ldr r0, =0xf000f044 | ||
191 | ldr r1, [r0] | ||
192 | orrne r1, r1, #0x6 | ||
193 | orreq r1, r1, #0x2 | ||
194 | str r1, [r0] | ||
195 | 1: | ||
196 | ldr r1, [r2] | ||
197 | tst r1, #0x8000 | ||
198 | bne 1b | ||
199 | bx lr | ||
200 | .ltorg /* constants used in stub come with us to IRAM */ | ||
201 | start_stub_end: | ||
202 | /* now executing from final physical address */ | ||
203 | |||
204 | /* copy the vector addresses to the table */ | ||
205 | ldr r0, =INT_VECT_TBL | ||
206 | adr r1, vectorsstart | ||
207 | adr r2, vectorsend | ||
208 | 1: | ||
209 | cmp r2, r1 | ||
210 | ldrhi r3, [r1], #4 | ||
211 | strhi r3, [r0], #4 | ||
212 | bhi 1b | ||
213 | |||
214 | /* Copy the IRAM */ | ||
215 | ldr r0, =_iramcopy | ||
216 | ldr r1, =_iramstart | ||
217 | ldr r2, =_iramend | ||
218 | 1: | ||
219 | cmp r2, r1 | ||
220 | ldrhi r3, [r0], #4 | ||
221 | strhi r3, [r1], #4 | ||
222 | bhi 1b | ||
223 | |||
224 | mov r0, #0 | ||
225 | |||
226 | /* Zero out IBSS */ | ||
227 | ldr r1, =_iedata | ||
228 | ldr r2, =_iend | ||
229 | 1: | ||
230 | cmp r2, r1 | ||
231 | strhi r0, [r1], #4 | ||
232 | bhi 1b | ||
233 | |||
234 | /* Initialise bss/ncbss sections to zero */ | ||
235 | ldr r1, =_edata | ||
236 | ldr r2, =_end | ||
237 | 1: | ||
238 | cmp r2, r1 | ||
239 | strhi r0, [r1], #4 | ||
240 | bhi 1b | ||
241 | |||
242 | /* Set up stack for IRQ mode */ | ||
243 | msr cpsr_c, #0xd2 /* IRQ/FIQ disabled */ | ||
244 | ldr sp, =irq_stack | ||
245 | /* Let svc, abort and undefined modes use irq stack */ | ||
246 | msr cpsr_c, #0xd3 | ||
247 | ldr sp, =irq_stack | ||
248 | msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */ | ||
249 | ldr sp, =irq_stack | ||
250 | msr cpsr_c, #0xdb /* IRQ/FIQ disabled */ | ||
251 | ldr sp, =irq_stack | ||
252 | |||
253 | /* Switch back to sys mode */ | ||
254 | msr cpsr_c, #0xdf | ||
255 | |||
256 | /* Set up some stack and munge it with 0xdeadbeef */ | ||
257 | ldr r0, =0xdeadbeef | ||
258 | ldr r1, =stackbegin | ||
259 | ldr sp, =stackend | ||
260 | 1: | ||
261 | cmp sp, r1 | ||
262 | strhi r0, [r1], #4 | ||
263 | bhi 1b | ||
264 | |||
265 | /* execute the loader - this will load an image to 0x10000000 */ | ||
266 | ldr r0, =main | ||
267 | mov lr, pc | ||
268 | bx r0 | ||
269 | |||
270 | /* store actual startup location returned by main() */ | ||
271 | ldr r1, =startup_loc | ||
272 | str r0, [r1] | ||
273 | |||
274 | /* write back anything loaded + startup_loc */ | ||
275 | mov r0, #CACHE_OP_COMMIT | ||
276 | bl cache_operation | ||
277 | |||
278 | mov r0, #0 | ||
279 | |||
280 | /* disable memory mapper */ | ||
281 | ldr r1, =MMAP_LOG | ||
282 | ldr r2, =MMAP_PHYS | ||
283 | str r0, [r1] | ||
284 | str r0, [r2] | ||
285 | |||
286 | /* bring COP back to life */ | ||
287 | ldr r1, =COP_CTRL | ||
288 | mov r0, #WAKE | ||
289 | str r0, [r1] | ||
290 | |||
291 | /* after this point, r0-r3 are reserved for OF magic */ | ||
292 | |||
293 | #if defined(SANSA_C200) || defined(PHILIPS_HDD1630) | ||
294 | /* Magic for loading the c200 OF */ | ||
295 | ldr r0, =0xb00d10ad | ||
296 | mov r1, #0x700 | ||
297 | ldr r2, =0xfff0 | ||
298 | mov r3, #0x7 | ||
299 | #endif | ||
300 | |||
301 | #if defined(PHILIPS_HDD6330) | ||
302 | /* Magic for loading the HDD6XX0 OF */ | ||
303 | ldr r0, =0xb00d10ad | ||
304 | mov r1, #0x800 | ||
305 | ldr r2, =0xfff0 | ||
306 | mov r3, #0x7 | ||
307 | #endif | ||
308 | |||
309 | /* branch to the address returned by main() */ | ||
310 | adr r4, startup_loc | ||
311 | ldr pc, [r4] | ||
312 | |||
313 | startup_loc: | ||
314 | .word 0x00000000 | ||
315 | |||
316 | /* exception handlers: will be copied to local vector table */ | ||
317 | vectorsstart: | ||
318 | .word newstart | ||
319 | .word undef_instr_handler | ||
320 | .word software_int_handler | ||
321 | .word prefetch_abort_handler | ||
322 | .word data_abort_handler | ||
323 | .word reserved_handler | ||
324 | .word irq_handler | ||
325 | .word fiq_handler | ||
326 | vectorsend: | ||
327 | |||
328 | .text | ||
329 | |||
330 | /* All illegal exceptions call into UIE with exception address as first | ||
331 | parameter. This is calculated differently depending on which exception | ||
332 | we're in. Second parameter is exception number, used for a string lookup | ||
333 | in UIE. | ||
334 | */ | ||
335 | undef_instr_handler: | ||
336 | sub r0, lr, #4 | ||
337 | mov r1, #0 | ||
338 | b UIE | ||
339 | |||
340 | /* We run sys mode most of the time, and should never see a software | ||
341 | exception being thrown. Make it illegal and call UIE. | ||
342 | */ | ||
343 | software_int_handler: | ||
344 | reserved_handler: | ||
345 | sub r0, lr, #4 | ||
346 | mov r1, #4 | ||
347 | b UIE | ||
348 | |||
349 | prefetch_abort_handler: | ||
350 | sub r0, lr, #4 | ||
351 | mov r1, #1 | ||
352 | b UIE | ||
353 | |||
354 | data_abort_handler: | ||
355 | sub r0, lr, #8 | ||
356 | mov r1, #2 | ||
357 | b UIE | ||
358 | |||
359 | /* should never happen in the bootloader */ | ||
360 | fiq_handler: | ||
361 | subs pc, lr, #4 | ||
362 | |||
363 | /* 256 words of IRQ stack */ | ||
364 | .section .bss | ||
365 | .balign 16 | ||
366 | .space 256*4 | ||
367 | irq_stack: | ||
diff --git a/firmware/target/arm/pp/debug-pp.c b/firmware/target/arm/pp/debug-pp.c new file mode 100644 index 0000000000..5f252db417 --- /dev/null +++ b/firmware/target/arm/pp/debug-pp.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "config.h" | ||
23 | #include "system.h" | ||
24 | #include <stdbool.h> | ||
25 | #include "font.h" | ||
26 | #include "lcd.h" | ||
27 | #include "button.h" | ||
28 | #include "powermgmt.h" | ||
29 | #include "adc.h" | ||
30 | #include "iap.h" | ||
31 | #include "hwcompat.h" | ||
32 | #include "debug-target.h" | ||
33 | |||
34 | static int perfcheck(void) | ||
35 | { | ||
36 | int result; | ||
37 | |||
38 | asm ( | ||
39 | "mrs r2, CPSR \n" | ||
40 | "orr r0, r2, #0xc0 \n" /* disable IRQ and FIQ */ | ||
41 | "msr CPSR_c, r0 \n" | ||
42 | "mov %[res], #0 \n" | ||
43 | "ldr r0, [%[timr]] \n" | ||
44 | "add r0, r0, %[tmo] \n" | ||
45 | "1: \n" | ||
46 | "add %[res], %[res], #1 \n" | ||
47 | "ldr r1, [%[timr]] \n" | ||
48 | "cmp r1, r0 \n" | ||
49 | "bmi 1b \n" | ||
50 | "msr CPSR_c, r2 \n" /* reset IRQ and FIQ state */ | ||
51 | : | ||
52 | [res]"=&r"(result) | ||
53 | : | ||
54 | [timr]"r"(&USEC_TIMER), | ||
55 | [tmo]"r"( | ||
56 | #if CONFIG_CPU == PP5002 | ||
57 | 16000 | ||
58 | #else /* PP5020/5022/5024 */ | ||
59 | 10226 | ||
60 | #endif | ||
61 | ) | ||
62 | : | ||
63 | "r0", "r1", "r2" | ||
64 | ); | ||
65 | return result; | ||
66 | } | ||
67 | |||
68 | bool dbg_ports(void) | ||
69 | { | ||
70 | int line; | ||
71 | |||
72 | lcd_clear_display(); | ||
73 | lcd_setfont(FONT_SYSFIXED); | ||
74 | |||
75 | while(1) | ||
76 | { | ||
77 | line = 0; | ||
78 | #if defined(CPU_PP502x) | ||
79 | #if (LCD_HEIGHT >= 176) /* Only for displays with appropriate height. */ | ||
80 | lcd_puts(0, line++, "GPIO ENABLE:"); | ||
81 | lcd_putsf(0, line++, "A: %02x E: %02x I: %02x", | ||
82 | (unsigned int)GPIOA_ENABLE, | ||
83 | (unsigned int)GPIOE_ENABLE, | ||
84 | (unsigned int)GPIOI_ENABLE); | ||
85 | lcd_putsf(0, line++, "B: %02x F: %02x J: %02x", | ||
86 | (unsigned int)GPIOB_ENABLE, | ||
87 | (unsigned int)GPIOF_ENABLE, | ||
88 | (unsigned int)GPIOJ_ENABLE); | ||
89 | lcd_putsf(0, line++, "C: %02x G: %02x K: %02x", | ||
90 | (unsigned int)GPIOC_ENABLE, | ||
91 | (unsigned int)GPIOG_ENABLE, | ||
92 | (unsigned int)GPIOK_ENABLE); | ||
93 | lcd_putsf(0, line++, "D: %02x H: %02x L: %02x", | ||
94 | (unsigned int)GPIOD_ENABLE, | ||
95 | (unsigned int)GPIOH_ENABLE, | ||
96 | (unsigned int)GPIOL_ENABLE); | ||
97 | line++; | ||
98 | #endif | ||
99 | lcd_puts(0, line++, "GPIO INPUT VAL:"); | ||
100 | lcd_putsf(0, line++, "A: %02x E: %02x I: %02x", | ||
101 | (unsigned int)GPIOA_INPUT_VAL, | ||
102 | (unsigned int)GPIOE_INPUT_VAL, | ||
103 | (unsigned int)GPIOI_INPUT_VAL); | ||
104 | lcd_putsf(0, line++, "B: %02x F: %02x J: %02x", | ||
105 | (unsigned int)GPIOB_INPUT_VAL, | ||
106 | (unsigned int)GPIOF_INPUT_VAL, | ||
107 | (unsigned int)GPIOJ_INPUT_VAL); | ||
108 | lcd_putsf(0, line++, "C: %02x G: %02x K: %02x", | ||
109 | (unsigned int)GPIOC_INPUT_VAL, | ||
110 | (unsigned int)GPIOG_INPUT_VAL, | ||
111 | (unsigned int)GPIOK_INPUT_VAL); | ||
112 | lcd_putsf(0, line++, "D: %02x H: %02x L: %02x", | ||
113 | (unsigned int)GPIOD_INPUT_VAL, | ||
114 | (unsigned int)GPIOH_INPUT_VAL, | ||
115 | (unsigned int)GPIOL_INPUT_VAL); | ||
116 | line++; | ||
117 | lcd_putsf(0, line++, "GPO32_VAL: %08lx", GPO32_VAL); | ||
118 | lcd_putsf(0, line++, "GPO32_EN: %08lx", GPO32_ENABLE); | ||
119 | lcd_putsf(0, line++, "DEV_EN: %08lx", DEV_EN); | ||
120 | lcd_putsf(0, line++, "DEV_EN2: %08lx", DEV_EN2); | ||
121 | lcd_putsf(0, line++, "DEV_EN3: %08lx", inl(0x60006044)); /* to be verified */ | ||
122 | lcd_putsf(0, line++, "DEV_INIT1: %08lx", DEV_INIT1); | ||
123 | lcd_putsf(0, line++, "DEV_INIT2: %08lx", DEV_INIT2); | ||
124 | #ifdef ADC_ACCESSORY | ||
125 | lcd_putsf(0, line++, "ACCESSORY: %d", adc_read(ADC_ACCESSORY)); | ||
126 | #endif | ||
127 | #if defined(IPOD_VIDEO) || defined(IPOD_NANO) | ||
128 | lcd_putsf(0, line++, "4066_ISTAT: %d", adc_read(ADC_4066_ISTAT)); | ||
129 | #endif | ||
130 | |||
131 | #if defined(IPOD_ACCESSORY_PROTOCOL) | ||
132 | const unsigned char *serbuf = iap_get_serbuf(); | ||
133 | lcd_putsf(0, line++, "IAP: %02x %02x %02x %02x %02x %02x %02x %02x", | ||
134 | serbuf[0], serbuf[1], serbuf[2], serbuf[3], serbuf[4], serbuf[5], | ||
135 | serbuf[6], serbuf[7]); | ||
136 | #endif | ||
137 | |||
138 | #if defined(IRIVER_H10) || defined(IRIVER_H10_5GB) | ||
139 | line++; | ||
140 | lcd_putsf(0, line++, "BATT: %03x UNK1: %03x", | ||
141 | adc_read(ADC_BATTERY), adc_read(ADC_UNKNOWN_1)); | ||
142 | lcd_putsf(0, line++, "REM: %03x PAD: %03x", | ||
143 | adc_read(ADC_REMOTE), adc_read(ADC_SCROLLPAD)); | ||
144 | #elif defined(PHILIPS_HDD1630) || defined(PHILIPS_HDD6330) | ||
145 | line++; | ||
146 | lcd_putsf(0, line++, "BATT: %03x UNK1: %03x", | ||
147 | adc_read(ADC_BATTERY), adc_read(ADC_UNKNOWN_1)); | ||
148 | #elif defined(SANSA_E200) || defined(PHILIPS_SA9200) | ||
149 | lcd_putsf(0, line++, "ADC_BVDD: %4d", adc_read(ADC_BVDD)); | ||
150 | lcd_putsf(0, line++, "ADC_RTCSUP: %4d", adc_read(ADC_RTCSUP)); | ||
151 | lcd_putsf(0, line++, "ADC_UVDD: %4d", adc_read(ADC_UVDD)); | ||
152 | lcd_putsf(0, line++, "ADC_CHG_IN: %4d", adc_read(ADC_CHG_IN)); | ||
153 | lcd_putsf(0, line++, "ADC_CVDD: %4d", adc_read(ADC_CVDD)); | ||
154 | lcd_putsf(0, line++, "ADC_BATTEMP: %4d", adc_read(ADC_BATTEMP)); | ||
155 | lcd_putsf(0, line++, "ADC_MICSUP1: %4d", adc_read(ADC_MICSUP1)); | ||
156 | lcd_putsf(0, line++, "ADC_MICSUP2: %4d", adc_read(ADC_MICSUP2)); | ||
157 | lcd_putsf(0, line++, "ADC_VBE1: %4d", adc_read(ADC_VBE1)); | ||
158 | lcd_putsf(0, line++, "ADC_VBE2: %4d", adc_read(ADC_VBE2)); | ||
159 | lcd_putsf(0, line++, "ADC_I_MICSUP1:%4d", adc_read(ADC_I_MICSUP1)); | ||
160 | #if !defined(PHILIPS_SA9200) | ||
161 | lcd_putsf(0, line++, "ADC_I_MICSUP2:%4d", adc_read(ADC_I_MICSUP2)); | ||
162 | lcd_putsf(0, line++, "ADC_VBAT: %4d", adc_read(ADC_VBAT)); | ||
163 | #endif | ||
164 | #endif | ||
165 | |||
166 | #elif CONFIG_CPU == PP5002 | ||
167 | lcd_putsf(0, line++, "GPIO_A: %02x GPIO_B: %02x", | ||
168 | (unsigned int)GPIOA_INPUT_VAL, (unsigned int)GPIOB_INPUT_VAL); | ||
169 | lcd_putsf(0, line++, "GPIO_C: %02x GPIO_D: %02x", | ||
170 | (unsigned int)GPIOC_INPUT_VAL, (unsigned int)GPIOD_INPUT_VAL); | ||
171 | |||
172 | lcd_putsf(0, line++, "DEV_EN: %08lx", DEV_EN); | ||
173 | lcd_putsf(0, line++, "CLOCK_ENABLE: %08lx", CLOCK_ENABLE); | ||
174 | lcd_putsf(0, line++, "CLOCK_SOURCE: %08lx", CLOCK_SOURCE); | ||
175 | lcd_putsf(0, line++, "PLL_CONTROL: %08lx", PLL_CONTROL); | ||
176 | lcd_putsf(0, line++, "PLL_DIV: %08lx", PLL_DIV); | ||
177 | lcd_putsf(0, line++, "PLL_MULT: %08lx", PLL_MULT); | ||
178 | lcd_putsf(0, line++, "TIMING1_CTL: %08lx", TIMING1_CTL); | ||
179 | lcd_putsf(0, line++, "TIMING2_CTL: %08lx", TIMING2_CTL); | ||
180 | #endif | ||
181 | lcd_update(); | ||
182 | if (button_get_w_tmo(HZ/10) == (DEBUG_CANCEL|BUTTON_REL)) | ||
183 | { | ||
184 | lcd_setfont(FONT_UI); | ||
185 | return false; | ||
186 | } | ||
187 | } | ||
188 | return false; | ||
189 | } | ||
190 | |||
191 | bool dbg_hw_info(void) | ||
192 | { | ||
193 | int line = 0; | ||
194 | #if defined(CPU_PP502x) | ||
195 | char pp_version[] = { (PP_VER2 >> 24) & 0xff, (PP_VER2 >> 16) & 0xff, | ||
196 | (PP_VER2 >> 8) & 0xff, (PP_VER2) & 0xff, | ||
197 | (PP_VER1 >> 24) & 0xff, (PP_VER1 >> 16) & 0xff, | ||
198 | (PP_VER1 >> 8) & 0xff, (PP_VER1) & 0xff, '\0' }; | ||
199 | #elif CONFIG_CPU == PP5002 | ||
200 | char pp_version[] = { (PP_VER4 >> 8) & 0xff, PP_VER4 & 0xff, | ||
201 | (PP_VER3 >> 8) & 0xff, PP_VER3 & 0xff, | ||
202 | (PP_VER2 >> 8) & 0xff, PP_VER2 & 0xff, | ||
203 | (PP_VER1 >> 8) & 0xff, PP_VER1 & 0xff, '\0' }; | ||
204 | #endif | ||
205 | |||
206 | lcd_setfont(FONT_SYSFIXED); | ||
207 | lcd_clear_display(); | ||
208 | |||
209 | lcd_puts(0, line++, "[Hardware info]"); | ||
210 | |||
211 | #ifdef IPOD_ARCH | ||
212 | lcd_putsf(0, line++, "HW rev: 0x%08lx", IPOD_HW_REVISION); | ||
213 | #endif | ||
214 | |||
215 | #if defined(IPOD_COLOR) || defined(IPOD_NANO) | ||
216 | extern int lcd_type; /* Defined in lcd-colornano.c */ | ||
217 | |||
218 | lcd_putsf(0, line++, "LCD type: %d", lcd_type); | ||
219 | #endif | ||
220 | |||
221 | lcd_putsf(0, line++, "PP version: %s", pp_version); | ||
222 | |||
223 | lcd_putsf(0, line++, "Est. clock (kHz): %d", perfcheck()); | ||
224 | |||
225 | lcd_update(); | ||
226 | |||
227 | /* wait for exit */ | ||
228 | while (button_get_w_tmo(HZ/10) != (DEBUG_CANCEL|BUTTON_REL)); | ||
229 | |||
230 | lcd_setfont(FONT_UI); | ||
231 | return false; | ||
232 | } | ||
diff --git a/firmware/target/arm/pp/i2c-pp.c b/firmware/target/arm/pp/i2c-pp.c new file mode 100644 index 0000000000..58740b5c66 --- /dev/null +++ b/firmware/target/arm/pp/i2c-pp.c | |||
@@ -0,0 +1,314 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * PP502X and PP5002 I2C driver | ||
11 | * | ||
12 | * Based on code from the ipodlinux project - http://ipodlinux.org/ | ||
13 | * Adapted for Rockbox in November 2005 | ||
14 | * | ||
15 | * Original file: linux/arch/armnommu/mach-ipod/hardware.c | ||
16 | * | ||
17 | * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org) | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or | ||
20 | * modify it under the terms of the GNU General Public License | ||
21 | * as published by the Free Software Foundation; either version 2 | ||
22 | * of the License, or (at your option) any later version. | ||
23 | * | ||
24 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
25 | * KIND, either express or implied. | ||
26 | * | ||
27 | ****************************************************************************/ | ||
28 | |||
29 | #include "cpu.h" | ||
30 | #include "kernel.h" | ||
31 | #include "thread.h" | ||
32 | #include "logf.h" | ||
33 | #include "system.h" | ||
34 | #include "i2c.h" | ||
35 | #include "i2c-pp.h" | ||
36 | #include "ascodec.h" | ||
37 | #include "as3514.h" | ||
38 | |||
39 | #define I2C_CTRL (*(volatile unsigned char*)(I2C_BASE+0x00)) | ||
40 | #define I2C_ADDR (*(volatile unsigned char*)(I2C_BASE+0x04)) | ||
41 | #define I2C_DATA(X) (*(volatile unsigned char*)(I2C_BASE+0xc+(4*X))) | ||
42 | #define I2C_STATUS (*(volatile unsigned char*)(I2C_BASE+0x1c)) | ||
43 | |||
44 | /* I2C_CTRL bit definitions */ | ||
45 | #define I2C_SEND 0x80 | ||
46 | |||
47 | /* I2C_STATUS bit definitions */ | ||
48 | #define I2C_BUSY (1<<6) | ||
49 | |||
50 | /* Local functions definitions */ | ||
51 | static struct mutex i2c_mtx SHAREDBSS_ATTR; | ||
52 | |||
53 | #define POLL_TIMEOUT (HZ) | ||
54 | |||
55 | static int pp_i2c_wait_not_busy(void) | ||
56 | { | ||
57 | unsigned long timeout; | ||
58 | timeout = current_tick + POLL_TIMEOUT; | ||
59 | while (TIME_BEFORE(current_tick, timeout)) { | ||
60 | if (!(I2C_STATUS & I2C_BUSY)) { | ||
61 | return 0; | ||
62 | } | ||
63 | yield(); | ||
64 | } | ||
65 | |||
66 | return -1; | ||
67 | } | ||
68 | |||
69 | static int pp_i2c_read_bytes(unsigned int addr, int len, unsigned char *data) | ||
70 | { | ||
71 | int i; | ||
72 | |||
73 | if (len < 1 || len > 4) | ||
74 | { | ||
75 | return -1; | ||
76 | } | ||
77 | |||
78 | if (pp_i2c_wait_not_busy() < 0) | ||
79 | { | ||
80 | return -2; | ||
81 | } | ||
82 | |||
83 | { | ||
84 | int old_irq_level = disable_irq_save(); | ||
85 | |||
86 | /* clear top 15 bits, left shift 1, or in 0x1 for a read */ | ||
87 | I2C_ADDR = ((addr << 17) >> 16) | 0x1; | ||
88 | |||
89 | I2C_CTRL |= 0x20; | ||
90 | |||
91 | I2C_CTRL = (I2C_CTRL & ~0x6) | ((len-1) << 1); | ||
92 | |||
93 | I2C_CTRL |= I2C_SEND; | ||
94 | |||
95 | restore_irq(old_irq_level); | ||
96 | |||
97 | if (pp_i2c_wait_not_busy() < 0) | ||
98 | { | ||
99 | return -2; | ||
100 | } | ||
101 | |||
102 | old_irq_level = disable_irq_save(); | ||
103 | |||
104 | if (data) | ||
105 | { | ||
106 | for ( i = 0; i < len; i++ ) | ||
107 | *data++ = I2C_DATA(i); | ||
108 | } | ||
109 | |||
110 | restore_irq(old_irq_level); | ||
111 | } | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static int pp_i2c_send_bytes(unsigned int addr, int len, unsigned char *data) | ||
117 | { | ||
118 | int i; | ||
119 | |||
120 | if (len < 1 || len > 4) | ||
121 | { | ||
122 | return -1; | ||
123 | } | ||
124 | |||
125 | if (pp_i2c_wait_not_busy() < 0) | ||
126 | { | ||
127 | return -2; | ||
128 | } | ||
129 | |||
130 | { | ||
131 | int old_irq_level = disable_irq_save(); | ||
132 | |||
133 | /* clear top 15 bits, left shift 1 */ | ||
134 | I2C_ADDR = (addr << 17) >> 16; | ||
135 | |||
136 | I2C_CTRL &= ~0x20; | ||
137 | |||
138 | for ( i = 0; i < len; i++ ) | ||
139 | { | ||
140 | I2C_DATA(i) = *data++; | ||
141 | } | ||
142 | |||
143 | I2C_CTRL = (I2C_CTRL & ~0x6) | ((len-1) << 1); | ||
144 | |||
145 | I2C_CTRL |= I2C_SEND; | ||
146 | |||
147 | restore_irq(old_irq_level); | ||
148 | } | ||
149 | |||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int pp_i2c_send_byte(unsigned int addr, int data0) | ||
154 | { | ||
155 | unsigned char data[1]; | ||
156 | |||
157 | data[0] = data0; | ||
158 | |||
159 | return pp_i2c_send_bytes(addr, 1, data); | ||
160 | } | ||
161 | |||
162 | /* Public functions */ | ||
163 | void i2c_lock(void) | ||
164 | { | ||
165 | mutex_lock(&i2c_mtx); | ||
166 | } | ||
167 | |||
168 | void i2c_unlock(void) | ||
169 | { | ||
170 | mutex_unlock(&i2c_mtx); | ||
171 | } | ||
172 | |||
173 | int i2c_readbytes(unsigned int dev_addr, int addr, int len, unsigned char *data) | ||
174 | { | ||
175 | int i, n; | ||
176 | |||
177 | mutex_lock(&i2c_mtx); | ||
178 | |||
179 | if (addr >= 0) | ||
180 | pp_i2c_send_byte(dev_addr, addr); | ||
181 | |||
182 | i = 0; | ||
183 | while (len > 0) | ||
184 | { | ||
185 | n = (len < 4) ? len : 4; | ||
186 | |||
187 | if (pp_i2c_read_bytes(dev_addr, n, data + i) < 0) | ||
188 | break; | ||
189 | |||
190 | len -= n; | ||
191 | i += n; | ||
192 | } | ||
193 | |||
194 | mutex_unlock(&i2c_mtx); | ||
195 | |||
196 | return i; | ||
197 | } | ||
198 | |||
199 | int i2c_readbyte(unsigned int dev_addr, int addr) | ||
200 | { | ||
201 | unsigned char data; | ||
202 | |||
203 | mutex_lock(&i2c_mtx); | ||
204 | pp_i2c_send_byte(dev_addr, addr); | ||
205 | pp_i2c_read_bytes(dev_addr, 1, &data); | ||
206 | mutex_unlock(&i2c_mtx); | ||
207 | |||
208 | return (int)data; | ||
209 | } | ||
210 | |||
211 | int i2c_sendbytes(unsigned int addr, int len, const unsigned char *data) | ||
212 | { | ||
213 | int i, n; | ||
214 | |||
215 | mutex_lock(&i2c_mtx); | ||
216 | |||
217 | i = 0; | ||
218 | while (len > 0) | ||
219 | { | ||
220 | n = (len < 4) ? len : 4; | ||
221 | |||
222 | if (pp_i2c_send_bytes(addr, n, (unsigned char *)(data + i)) < 0) | ||
223 | break; | ||
224 | |||
225 | len -= n; | ||
226 | i += n; | ||
227 | } | ||
228 | |||
229 | mutex_unlock(&i2c_mtx); | ||
230 | |||
231 | return i; | ||
232 | } | ||
233 | |||
234 | int pp_i2c_send(unsigned int addr, int data0, int data1) | ||
235 | { | ||
236 | int retval; | ||
237 | unsigned char data[2]; | ||
238 | |||
239 | data[0] = data0; | ||
240 | data[1] = data1; | ||
241 | |||
242 | mutex_lock(&i2c_mtx); | ||
243 | retval = pp_i2c_send_bytes(addr, 2, data); | ||
244 | mutex_unlock(&i2c_mtx); | ||
245 | |||
246 | return retval; | ||
247 | } | ||
248 | |||
249 | void i2c_init(void) | ||
250 | { | ||
251 | /* From ipodlinux */ | ||
252 | mutex_init(&i2c_mtx); | ||
253 | |||
254 | #ifdef IPOD_MINI | ||
255 | /* GPIO port C disable port 0x10 */ | ||
256 | GPIOC_ENABLE &= ~0x10; | ||
257 | |||
258 | /* GPIO port C disable port 0x20 */ | ||
259 | GPIOC_ENABLE &= ~0x20; | ||
260 | #endif | ||
261 | |||
262 | #if CONFIG_I2C == I2C_PP5002 | ||
263 | DEV_EN |= 0x2; | ||
264 | #else | ||
265 | DEV_EN |= DEV_I2C; /* Enable I2C */ | ||
266 | #endif | ||
267 | DEV_RS |= DEV_I2C; /* Start I2C Reset */ | ||
268 | DEV_RS &=~DEV_I2C; /* End I2C Reset */ | ||
269 | |||
270 | #if CONFIG_I2C == I2C_PP5020 | ||
271 | outl(0x0, 0x600060a4); | ||
272 | #if defined(PHILIPS_HDD1630) || defined(PHILIPS_HDD6330) || \ | ||
273 | defined(SAMSUNG_YH820) || defined(SAMSUNG_YH920) || \ | ||
274 | defined(SAMSUNG_YH925) || defined(PBELL_VIBE500) | ||
275 | outl(inl(0x600060a4) | 0x20, 0x600060a4); | ||
276 | outl(inl(0x7000c020) | 0x3, 0x7000c020); | ||
277 | outl(0x55, 0x7000c02c); | ||
278 | outl(0x54, 0x7000c030); | ||
279 | #else | ||
280 | outl(0x80 | (0 << 8), 0x600060a4); | ||
281 | #endif | ||
282 | #elif CONFIG_I2C == I2C_PP5024 | ||
283 | #if defined(SANSA_E200) || defined(PHILIPS_SA9200) | ||
284 | /* Sansa OF sets this to 0x20 first, communicates with the AS3514 | ||
285 | then sets it to 0x23 - this still works fine though */ | ||
286 | outl(0x0, 0x600060a4); | ||
287 | outl(0x23, 0x600060a4); | ||
288 | #elif defined(SANSA_C200) | ||
289 | /* This is the init sequence from the Sansa c200 bootloader. | ||
290 | I'm not sure what's really necessary. */ | ||
291 | pp_i2c_wait_not_busy(); | ||
292 | |||
293 | outl(0, 0x600060a4); | ||
294 | outl(0x64, 0x600060a4); | ||
295 | |||
296 | outl(0x55, 0x7000c02c); | ||
297 | outl(0x54, 0x7000c030); | ||
298 | |||
299 | outl(0, 0x600060a4); | ||
300 | outl(0x1e, 0x600060a4); | ||
301 | |||
302 | ascodec_write(AS3514_SUPERVISOR, 5); | ||
303 | #elif defined(PHILIPS_SA9200) | ||
304 | outl(0x0, 0x600060a4); | ||
305 | outl(inl(0x600060a4) | 0x20, 0x600060a4); | ||
306 | |||
307 | outl(inl(0x7000c020) | 0x3, 0x7000c020); | ||
308 | outl(0x55, 0x7000c02c); | ||
309 | outl(0x54, 0x7000c030); | ||
310 | #endif | ||
311 | #endif | ||
312 | |||
313 | i2c_readbyte(0x8, 0); | ||
314 | } | ||
diff --git a/firmware/target/arm/pp/i2s-pp.c b/firmware/target/arm/pp/i2s-pp.c new file mode 100644 index 0000000000..83f39515c4 --- /dev/null +++ b/firmware/target/arm/pp/i2s-pp.c | |||
@@ -0,0 +1,95 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Portalplayer specific code for I2S | ||
11 | * | ||
12 | * Based on code from the ipodlinux project - http://ipodlinux.org/ | ||
13 | * Adapted for Rockbox in December 2005 | ||
14 | * | ||
15 | * Original file: linux/arch/armnommu/mach-ipod/audio.c | ||
16 | * | ||
17 | * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org) | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or | ||
20 | * modify it under the terms of the GNU General Public License | ||
21 | * as published by the Free Software Foundation; either version 2 | ||
22 | * of the License, or (at your option) any later version. | ||
23 | * | ||
24 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
25 | * KIND, either express or implied. | ||
26 | * | ||
27 | ****************************************************************************/ | ||
28 | #include "system.h" | ||
29 | #include "cpu.h" | ||
30 | #include "i2s.h" | ||
31 | #if defined (SANSA_E200) || defined (SANSA_C200) | ||
32 | #include "audiohw.h" | ||
33 | #include "pcm_sampr.h" | ||
34 | #endif | ||
35 | |||
36 | #if CONFIG_CPU == PP5002 | ||
37 | void i2s_reset(void) | ||
38 | { | ||
39 | /* I2S device reset */ | ||
40 | DEV_RS |= DEV_I2S; | ||
41 | DEV_RS &= ~DEV_I2S; | ||
42 | |||
43 | /* I2S controller enable */ | ||
44 | IISCONFIG |= IIS_ENABLE; | ||
45 | |||
46 | /* reset DAC and ADC fifo */ | ||
47 | IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR; | ||
48 | } | ||
49 | #else /* PP502X */ | ||
50 | |||
51 | /* | ||
52 | * Reset the I2S BIT.FORMAT I2S, 16bit, FIFO.FORMAT 32bit | ||
53 | */ | ||
54 | void i2s_reset(void) | ||
55 | { | ||
56 | /* I2S soft reset */ | ||
57 | IISCONFIG |= IIS_RESET; | ||
58 | IISCONFIG &= ~IIS_RESET; | ||
59 | |||
60 | /* BIT.FORMAT */ | ||
61 | IISCONFIG = ((IISCONFIG & ~IIS_FORMAT_MASK) | IIS_FORMAT_IIS); | ||
62 | /* BIT.SIZE */ | ||
63 | IISCONFIG = ((IISCONFIG & ~IIS_SIZE_MASK) | IIS_SIZE_16BIT); | ||
64 | |||
65 | /* FIFO.FORMAT */ | ||
66 | /* If BIT.SIZE < FIFO.FORMAT low bits will be 0 */ | ||
67 | #ifdef HAVE_AS3514 | ||
68 | /* AS3514 can only operate as I2S Slave */ | ||
69 | IISCONFIG |= IIS_MASTER; | ||
70 | |||
71 | /* Set I2S to 44.1kHz */ | ||
72 | #ifdef PHILIPS_SA9200 | ||
73 | /* values taken from the SA9200 OF */ | ||
74 | IISCLK = (IISCLK & ~0x1ff) | 31; | ||
75 | IISDIV = (IISDIV & ~0xc0000000) | (2 << 30); | ||
76 | IISDIV = (IISDIV & ~0x3f) | 16; | ||
77 | #elif defined (SANSA_E200) || defined (SANSA_C200) | ||
78 | audiohw_set_sampr_dividers(HW_FREQ_DEFAULT); | ||
79 | #else | ||
80 | IISCLK = (IISCLK & ~0x1ff) | 33; | ||
81 | IISDIV = 7; | ||
82 | #endif | ||
83 | #endif /* HAVE_AS3514 */ | ||
84 | |||
85 | IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16_2); | ||
86 | |||
87 | /* RX_ATN_LVL = when 12 slots full */ | ||
88 | /* TX_ATN_LVL = DMA request when 4 slots empty */ | ||
89 | IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_4; | ||
90 | |||
91 | /* Rx.CLR = 1, TX.CLR = 1 */ | ||
92 | IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR; | ||
93 | } | ||
94 | |||
95 | #endif /* CONFIG_CPU == */ | ||
diff --git a/firmware/target/arm/pp/kernel-pp.c b/firmware/target/arm/pp/kernel-pp.c new file mode 100644 index 0000000000..2a00254173 --- /dev/null +++ b/firmware/target/arm/pp/kernel-pp.c | |||
@@ -0,0 +1,64 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Björn Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include "system.h" | ||
23 | #include "kernel.h" | ||
24 | |||
25 | #if !defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_USB_MODE) | ||
26 | void TIMER1(void) | ||
27 | { | ||
28 | /* Run through the list of tick tasks (using main core) */ | ||
29 | TIMER1_VAL; /* Read value to ack IRQ */ | ||
30 | |||
31 | /* Run through the list of tick tasks using main CPU core - | ||
32 | wake up the COP through its control interface to provide pulse */ | ||
33 | call_tick_tasks(); | ||
34 | |||
35 | #if NUM_CORES > 1 | ||
36 | /* Pulse the COP */ | ||
37 | core_wake(COP); | ||
38 | #endif /* NUM_CORES */ | ||
39 | } | ||
40 | #endif | ||
41 | |||
42 | /* Must be last function called init kernel/thread initialization */ | ||
43 | void tick_start(unsigned int interval_in_ms) | ||
44 | { | ||
45 | #if !defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_USB_MODE) | ||
46 | TIMER1_CFG = 0x0; | ||
47 | TIMER1_VAL; | ||
48 | /* enable timer */ | ||
49 | TIMER1_CFG = 0xc0000000 | (interval_in_ms*1000 - 1); | ||
50 | /* unmask interrupt source */ | ||
51 | CPU_INT_EN = TIMER1_MASK; | ||
52 | #else | ||
53 | /* We don't enable interrupts in the bootloader */ | ||
54 | (void)interval_in_ms; | ||
55 | #endif | ||
56 | } | ||
57 | |||
58 | #ifdef HAVE_BOOTLOADER_USB_MODE | ||
59 | void tick_stop(void) | ||
60 | { | ||
61 | CPU_INT_DIS = TIMER1_MASK; | ||
62 | TIMER1_CFG = 0; | ||
63 | } | ||
64 | #endif | ||
diff --git a/firmware/target/arm/pp/pcm-pp.c b/firmware/target/arm/pp/pcm-pp.c new file mode 100644 index 0000000000..3854206ae8 --- /dev/null +++ b/firmware/target/arm/pp/pcm-pp.c | |||
@@ -0,0 +1,734 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdlib.h> | ||
22 | #include "system.h" | ||
23 | #include "kernel.h" | ||
24 | #include "logf.h" | ||
25 | #include "audio.h" | ||
26 | #include "sound.h" | ||
27 | #include "pcm.h" | ||
28 | #include "pcm_sampr.h" | ||
29 | #include "pcm-internal.h" | ||
30 | |||
31 | /** DMA **/ | ||
32 | |||
33 | #ifdef CPU_PP502x | ||
34 | /* 16-bit, L-R packed into 32 bits with left in the least significant halfword */ | ||
35 | #define SAMPLE_SIZE 16 | ||
36 | /* DMA Requests from IIS, Memory to peripheral, single transfer, | ||
37 | wait for DMA request, interrupt on complete */ | ||
38 | #define DMA_PLAY_CONFIG ((DMA_REQ_IIS << DMA_CMD_REQ_ID_POS) | \ | ||
39 | DMA_CMD_RAM_TO_PER | DMA_CMD_SINGLE | \ | ||
40 | DMA_CMD_WAIT_REQ | DMA_CMD_INTR) | ||
41 | /* DMA status cannot be viewed from outside code in control because that can | ||
42 | * clear the interrupt from outside the handler and prevent the handler from | ||
43 | * from being called. Split up transfers to a reasonable size that is good as | ||
44 | * a timer, obtaining a keyclick position and peaking yet still keeps the | ||
45 | * FIQ count low. | ||
46 | */ | ||
47 | #define MAX_DMA_CHUNK_SIZE (pcm_curr_sampr >> 6) /* ~1/256 seconds */ | ||
48 | #else | ||
49 | /* 32-bit, one left 32-bit sample followed by one right 32-bit sample */ | ||
50 | #define SAMPLE_SIZE 32 | ||
51 | #endif | ||
52 | |||
53 | struct dma_data | ||
54 | { | ||
55 | /* NOTE: The order of size and p is important if you use assembler | ||
56 | optimised fiq handler, so don't change it. */ | ||
57 | union | ||
58 | { | ||
59 | unsigned long addr; | ||
60 | uint32_t *p16; /* For packed 16-16 stereo pairs */ | ||
61 | uint16_t *p32; /* For individual samples converted to 32-bit */ | ||
62 | }; | ||
63 | size_t size; | ||
64 | #if NUM_CORES > 1 | ||
65 | unsigned core; | ||
66 | #endif | ||
67 | int locked; | ||
68 | int state; | ||
69 | }; | ||
70 | |||
71 | extern void *fiq_function; | ||
72 | |||
73 | /* Dispatch to the proper handler and leave the main vector table alone */ | ||
74 | void fiq_handler(void) ICODE_ATTR __attribute__((naked)); | ||
75 | void fiq_handler(void) | ||
76 | { | ||
77 | asm volatile ( | ||
78 | "ldr pc, [pc, #-4] \n" | ||
79 | "fiq_function: \n" | ||
80 | ".word 0 \n" | ||
81 | ); | ||
82 | } | ||
83 | |||
84 | #ifdef HAVE_PCM_DMA_ADDRESS | ||
85 | void * pcm_dma_addr(void *addr) | ||
86 | { | ||
87 | if (addr != NULL && (unsigned long)addr < UNCACHED_BASE_ADDR) | ||
88 | addr = UNCACHED_ADDR(addr); | ||
89 | return addr; | ||
90 | } | ||
91 | #endif | ||
92 | |||
93 | /* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */ | ||
94 | |||
95 | /**************************************************************************** | ||
96 | ** Playback DMA transfer | ||
97 | **/ | ||
98 | static struct dma_data dma_play_data IBSS_ATTR = | ||
99 | { | ||
100 | /* Initialize to a locked, stopped state */ | ||
101 | { .addr = 0 }, | ||
102 | .size = 0, | ||
103 | #if NUM_CORES > 1 | ||
104 | .core = 0x00, | ||
105 | #endif | ||
106 | .locked = 0, | ||
107 | .state = 0 | ||
108 | }; | ||
109 | |||
110 | void pcm_dma_apply_settings(void) | ||
111 | { | ||
112 | audiohw_set_frequency(pcm_fsel); | ||
113 | } | ||
114 | |||
115 | #if defined(CPU_PP502x) | ||
116 | /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ | ||
117 | void ICODE_ATTR __attribute__((interrupt("FIQ"))) fiq_playback(void) | ||
118 | { | ||
119 | bool new_buffer = false; | ||
120 | register size_t size; | ||
121 | |||
122 | DMA0_STATUS; /* Clear any pending interrupt */ | ||
123 | |||
124 | size = (DMA0_CMD & 0xffff) + 4; /* Get size of trasfer that caused this | ||
125 | interrupt */ | ||
126 | dma_play_data.addr += size; | ||
127 | dma_play_data.size -= size; | ||
128 | |||
129 | while (1) | ||
130 | { | ||
131 | if (dma_play_data.size > 0) { | ||
132 | size = MAX_DMA_CHUNK_SIZE; | ||
133 | /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less | ||
134 | * than a FIFO's worth of data after this transfer? */ | ||
135 | if (size + 16*4 > dma_play_data.size) | ||
136 | size = dma_play_data.size; | ||
137 | |||
138 | /* Set the new DMA values and activate channel */ | ||
139 | DMA0_RAM_ADDR = dma_play_data.addr; | ||
140 | DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START; | ||
141 | |||
142 | if (new_buffer) | ||
143 | pcm_play_dma_started_callback(); | ||
144 | return; | ||
145 | } | ||
146 | |||
147 | new_buffer = true; | ||
148 | |||
149 | /* Buffer empty. Try to get more. */ | ||
150 | pcm_play_get_more_callback((void **)&dma_play_data.addr, | ||
151 | &dma_play_data.size); | ||
152 | |||
153 | if (dma_play_data.size == 0) { | ||
154 | /* No more data */ | ||
155 | return; | ||
156 | } | ||
157 | |||
158 | if (dma_play_data.addr < UNCACHED_BASE_ADDR) { | ||
159 | /* Flush any pending cache writes */ | ||
160 | dma_play_data.addr = UNCACHED_ADDR(dma_play_data.addr); | ||
161 | commit_discard_idcache(); | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | #else | ||
166 | /* ASM optimised FIQ handler. Checks for the minimum allowed loop cycles by | ||
167 | * evalutation of free IISFIFO-slots against available source buffer words. | ||
168 | * Through this it is possible to move the check for IIS_TX_FREE_COUNT outside | ||
169 | * the loop and do some further optimization. Right after the loops (source | ||
170 | * buffer -> IISFIFO) are done we need to check whether we have to exit FIQ | ||
171 | * handler (this must be done, if all free FIFO slots were filled) or we will | ||
172 | * have to get some new source data. Important information kept from former | ||
173 | * ASM implementation (not used anymore): GCC fails to make use of the fact | ||
174 | * that FIQ mode has registers r8-r14 banked, and so does not need to be saved. | ||
175 | * This routine uses only these registers, and so will never touch the stack | ||
176 | * unless it actually needs to do so when calling pcm_callback_for_more. | ||
177 | * C version is still included below for reference and testing. | ||
178 | */ | ||
179 | #if 1 | ||
180 | void fiq_playback(void) ICODE_ATTR __attribute__((naked)); | ||
181 | void fiq_playback(void) | ||
182 | { | ||
183 | /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual | ||
184 | * FIQ handler. r11 contains address of p (also set in crt0.S). Most other | ||
185 | * addresses we need are generated by using offsets with these two. | ||
186 | * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG. | ||
187 | * r8 and r9 contains local copies of p and size respectively. | ||
188 | * r0-r3 and r12 is a working register. | ||
189 | */ | ||
190 | asm volatile ( | ||
191 | "stmfd sp!, { r0-r4, lr } \n" /* stack scratch regs and lr */ | ||
192 | |||
193 | "mov r4, #0 \n" /* Was the callback called? */ | ||
194 | #if CONFIG_CPU == PP5002 | ||
195 | "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */ | ||
196 | "ldr r12, [r12] \n" | ||
197 | #endif | ||
198 | "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */ | ||
199 | "cmp r9, #0 \n" /* is size 0? */ | ||
200 | "beq .more_data \n" /* if so, ask pcmbuf for more data */ | ||
201 | |||
202 | #if SAMPLE_SIZE == 16 | ||
203 | ".check_fifo: \n" | ||
204 | "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ | ||
205 | "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 16 (PP502x) */ | ||
206 | |||
207 | "mov r1, r0, lsr #16 \n" /* number of free FIFO slots */ | ||
208 | "cmp r1, r9, lsr #2 \n" /* number of words from source */ | ||
209 | "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ | ||
210 | "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ | ||
211 | |||
212 | "subs r1, r1, #2 \n" | ||
213 | ".fifo_loop_2: \n" | ||
214 | "ldmgeia r8!, {r2, r12} \n" /* load four samples */ | ||
215 | "strge r2 , [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ | ||
216 | "strge r12, [r10, %[wr]] \n" /* write sample 2-3 to IISFIFO_WR */ | ||
217 | "subges r1, r1, #2 \n" /* one more loop? */ | ||
218 | "bge .fifo_loop_2 \n" /* yes, continue */ | ||
219 | |||
220 | "tst r1, #1 \n" /* two samples (one word) left? */ | ||
221 | "ldrne r12, [r8], #4 \n" /* load two samples */ | ||
222 | "strne r12, [r10, %[wr]] \n" /* write sample 0-1 to IISFIFO_WR */ | ||
223 | #elif SAMPLE_SIZE == 32 | ||
224 | ".check_fifo: \n" | ||
225 | "ldr r0, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */ | ||
226 | "and r0, r0, %[mask] \n" /* r0 = IIS_TX_FREE_COUNT << 23 (PP5002) */ | ||
227 | |||
228 | "movs r1, r0, lsr #24 \n" /* number of free pairs of FIFO slots */ | ||
229 | "beq .fifo_fill_complete \n" /* no complete pair? -> exit */ | ||
230 | "cmp r1, r9, lsr #2 \n" /* number of words from source */ | ||
231 | "movgt r1, r9, lsr #2 \n" /* r1 = amount of allowed loops */ | ||
232 | "sub r9, r9, r1, lsl #2 \n" /* r1 words will be written in following loop */ | ||
233 | |||
234 | ".fifo_loop: \n" | ||
235 | "ldr r12, [r8], #4 \n" /* load two samples */ | ||
236 | "mov r2 , r12, lsl #16 \n" /* put left sample at the top bits */ | ||
237 | "str r2 , [r10, %[wr]] \n" /* write top sample to IISFIFO_WR */ | ||
238 | "str r12, [r10, %[wr]] \n" /* write low sample to IISFIFO_WR*/ | ||
239 | "subs r1, r1, #1 \n" /* one more loop? */ | ||
240 | "bgt .fifo_loop \n" /* yes, continue */ | ||
241 | |||
242 | ".fifo_fill_complete: \n" | ||
243 | #endif | ||
244 | "cmp r4, #0 \n" /* If fill came after get_more... */ | ||
245 | "beq .still_old_buffer \n" | ||
246 | "mov r4, #0 \n" | ||
247 | "ldr r2, =pcm_play_dma_started \n" | ||
248 | "ldrne r2, [r2] \n" | ||
249 | "cmp r2, #0 \n" | ||
250 | "movne lr, pc \n" | ||
251 | "bxne r2 \n" | ||
252 | |||
253 | ".still_old_buffer: \n" | ||
254 | "cmp r9, #0 \n" /* either FIFO is full or source buffer is empty */ | ||
255 | "bgt .exit \n" /* if source buffer is not empty, FIFO must be full */ | ||
256 | |||
257 | ".more_data: \n" | ||
258 | "mov r4, #1 \n" /* Remember we did this */ | ||
259 | "ldr r2, =pcm_play_get_more_callback \n" | ||
260 | "mov r0, r11 \n" /* r0 = &p */ | ||
261 | "add r1, r11, #4 \n" /* r1 = &size */ | ||
262 | "mov lr, pc \n" /* call pcm_play_get_more_callback */ | ||
263 | "bx r2 \n" | ||
264 | "ldmia r11, { r8-r9 } \n" /* load new p and size */ | ||
265 | "cmp r9, #0 \n" | ||
266 | "bne .check_fifo \n" /* size != 0? refill */ | ||
267 | |||
268 | ".exit: \n" /* (r9=0 if stopping, look above) */ | ||
269 | "stmia r11, { r8-r9 } \n" /* save p and size */ | ||
270 | "ldmfd sp!, { r0-r4, lr } \n" | ||
271 | "subs pc, lr, #4 \n" /* FIQ specific return sequence */ | ||
272 | ".ltorg \n" | ||
273 | : /* These must only be integers! No regs */ | ||
274 | : [mask]"i"(IIS_TX_FREE_MASK), | ||
275 | [cfg]"i"((int)&IISFIFO_CFG - (int)&IISCONFIG), | ||
276 | [wr]"i"((int)&IISFIFO_WR - (int)&IISCONFIG) | ||
277 | ); | ||
278 | } | ||
279 | #else /* C version for reference */ | ||
280 | void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR; | ||
281 | /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ | ||
282 | void fiq_playback(void) | ||
283 | { | ||
284 | bool new_buffer = false; | ||
285 | |||
286 | #if CONFIG_CPU == PP5002 | ||
287 | inl(0xcf001040); | ||
288 | #endif | ||
289 | |||
290 | do { | ||
291 | while (dma_play_data.size > 0) { | ||
292 | if (IIS_TX_FREE_COUNT < 2) { | ||
293 | if (new_buffer) { | ||
294 | new_buffer = false; | ||
295 | pcm_play_dma_started_callback(); | ||
296 | } | ||
297 | return; | ||
298 | } | ||
299 | #if SAMPLE_SIZE == 16 | ||
300 | IISFIFO_WR = *dma_play_data.p16++; | ||
301 | #elif SAMPLE_SIZE == 32 | ||
302 | IISFIFO_WR = *dma_play_data.p32++ << 16; | ||
303 | IISFIFO_WR = *dma_play_data.p32++ << 16; | ||
304 | #endif | ||
305 | dma_play_data.size -= 4; | ||
306 | } | ||
307 | |||
308 | if (new_buffer) { | ||
309 | new_buffer = false; | ||
310 | pcm_play_dma_started_callback(); | ||
311 | } | ||
312 | |||
313 | /* p is empty, get some more data */ | ||
314 | pcm_play_get_more_callback((void **)&dma_play_data.addr, | ||
315 | &dma_play_data.size); | ||
316 | new_buffer = true; | ||
317 | } while (dma_play_data.size); | ||
318 | |||
319 | /* No more data */ | ||
320 | } | ||
321 | #endif /* ASM / C selection */ | ||
322 | #endif /* CPU_PP502x */ | ||
323 | |||
324 | /* For the locks, FIQ must be disabled because the handler manipulates | ||
325 | IISCONFIG and the operation is not atomic - dual core support | ||
326 | will require other measures */ | ||
327 | void pcm_play_lock(void) | ||
328 | { | ||
329 | int status = disable_fiq_save(); | ||
330 | |||
331 | if (++dma_play_data.locked == 1) { | ||
332 | #ifdef CPU_PP502x | ||
333 | CPU_INT_DIS = DMA_MASK; | ||
334 | #else | ||
335 | IIS_IRQTX_REG &= ~IIS_IRQTX; | ||
336 | #endif | ||
337 | } | ||
338 | |||
339 | restore_fiq(status); | ||
340 | } | ||
341 | |||
342 | void pcm_play_unlock(void) | ||
343 | { | ||
344 | int status = disable_fiq_save(); | ||
345 | |||
346 | if (--dma_play_data.locked == 0 && dma_play_data.state != 0) { | ||
347 | #ifdef CPU_PP502x | ||
348 | CPU_INT_EN = DMA_MASK; | ||
349 | #else | ||
350 | IIS_IRQTX_REG |= IIS_IRQTX; | ||
351 | #endif | ||
352 | } | ||
353 | |||
354 | restore_fiq(status); | ||
355 | } | ||
356 | |||
357 | static void play_start_pcm(void) | ||
358 | { | ||
359 | fiq_function = fiq_playback; | ||
360 | |||
361 | #ifdef CPU_PP502x | ||
362 | /* Not at least MAX_DMA_CHUNK_SIZE left or there would be less than a | ||
363 | * FIFO's worth of data after this transfer? */ | ||
364 | size_t size = MAX_DMA_CHUNK_SIZE; | ||
365 | if (size + 16*4 > dma_play_data.size) | ||
366 | size = dma_play_data.size; | ||
367 | |||
368 | DMA0_RAM_ADDR = dma_play_data.addr; | ||
369 | DMA0_CMD = DMA_PLAY_CONFIG | (size - 4) | DMA_CMD_START; | ||
370 | dma_play_data.state = 1; | ||
371 | #else | ||
372 | IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */ | ||
373 | |||
374 | /* Fill the FIFO or start when data is used up */ | ||
375 | while (1) { | ||
376 | if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) { | ||
377 | IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */ | ||
378 | dma_play_data.state = 1; | ||
379 | return; | ||
380 | } | ||
381 | |||
382 | #if SAMPLE_SIZE == 16 | ||
383 | IISFIFO_WR = *dma_play_data.p16++; | ||
384 | #elif SAMPLE_SIZE == 32 | ||
385 | IISFIFO_WR = *dma_play_data.p32++ << 16; | ||
386 | IISFIFO_WR = *dma_play_data.p32++ << 16; | ||
387 | #endif | ||
388 | dma_play_data.size -= 4; | ||
389 | } | ||
390 | #endif | ||
391 | } | ||
392 | |||
393 | static void play_stop_pcm(void) | ||
394 | { | ||
395 | #ifdef CPU_PP502x | ||
396 | unsigned long status = DMA0_STATUS; /* Snapshot- resume from this point */ | ||
397 | unsigned long cmd = DMA0_CMD; | ||
398 | size_t size = 0; | ||
399 | |||
400 | /* Stop transfer */ | ||
401 | DMA0_CMD = cmd & ~(DMA_CMD_START | DMA_CMD_INTR); | ||
402 | |||
403 | /* Wait for not busy + clear int */ | ||
404 | while (DMA0_STATUS & (DMA_STATUS_BUSY | DMA_STATUS_INTR)); | ||
405 | |||
406 | if (status & DMA_STATUS_BUSY) { | ||
407 | /* Transfer was interrupted - leave what's left */ | ||
408 | size = (cmd & 0xfffc) - (status & 0xfffc); | ||
409 | } | ||
410 | else if (status & DMA_STATUS_INTR) { | ||
411 | /* Transfer was finished - DMA0_STATUS will have been reloaded | ||
412 | * automatically with size in DMA0_CMD. Setup to restart on next | ||
413 | * segment. */ | ||
414 | size = (cmd & 0xfffc) + 4; | ||
415 | } | ||
416 | /* else not an active state - size = 0 */ | ||
417 | |||
418 | dma_play_data.addr += size; | ||
419 | dma_play_data.size -= size; | ||
420 | |||
421 | if (dma_play_data.size == 0) | ||
422 | dma_play_data.addr = 0; /* Entire buffer has completed. */ | ||
423 | #else | ||
424 | /* Disable TX interrupt */ | ||
425 | IIS_IRQTX_REG &= ~IIS_IRQTX; | ||
426 | #endif | ||
427 | |||
428 | /* Wait for FIFO to empty */ | ||
429 | while (!IIS_TX_IS_EMPTY); | ||
430 | |||
431 | dma_play_data.state = 0; | ||
432 | } | ||
433 | |||
434 | void pcm_play_dma_start(const void *addr, size_t size) | ||
435 | { | ||
436 | #if NUM_CORES > 1 | ||
437 | /* This will become more important later - and different ! */ | ||
438 | dma_play_data.core = processor_id(); /* save initiating core */ | ||
439 | #endif | ||
440 | |||
441 | pcm_play_dma_stop(); | ||
442 | |||
443 | #ifdef CPU_PP502x | ||
444 | if ((unsigned long)addr < UNCACHED_BASE_ADDR) { | ||
445 | /* Flush any pending cache writes */ | ||
446 | addr = UNCACHED_ADDR(addr); | ||
447 | commit_discard_idcache(); | ||
448 | } | ||
449 | |||
450 | dma_play_data.addr = (unsigned long)addr; | ||
451 | dma_play_data.size = size; | ||
452 | DMA0_PER_ADDR = (unsigned long)&IISFIFO_WR; | ||
453 | DMA0_FLAGS = DMA_FLAGS_UNK26; | ||
454 | DMA0_INCR = DMA_INCR_RANGE_FIXED | DMA_INCR_WIDTH_32BIT; | ||
455 | #else | ||
456 | dma_play_data.addr = (unsigned long)addr; | ||
457 | dma_play_data.size = size; | ||
458 | #endif | ||
459 | |||
460 | play_start_pcm(); | ||
461 | } | ||
462 | |||
463 | /* Stops the DMA transfer and interrupt */ | ||
464 | void pcm_play_dma_stop(void) | ||
465 | { | ||
466 | play_stop_pcm(); | ||
467 | dma_play_data.addr = 0; | ||
468 | dma_play_data.size = 0; | ||
469 | #if NUM_CORES > 1 | ||
470 | dma_play_data.core = 0; /* no core in control */ | ||
471 | #endif | ||
472 | } | ||
473 | |||
474 | void pcm_play_dma_pause(bool pause) | ||
475 | { | ||
476 | if (pause) { | ||
477 | play_stop_pcm(); | ||
478 | } else { | ||
479 | play_start_pcm(); | ||
480 | } | ||
481 | } | ||
482 | |||
483 | size_t pcm_get_bytes_waiting(void) | ||
484 | { | ||
485 | return dma_play_data.size & ~3; | ||
486 | } | ||
487 | |||
488 | void pcm_play_dma_init(void) | ||
489 | { | ||
490 | /* Initialize default register values. */ | ||
491 | audiohw_init(); | ||
492 | |||
493 | #ifdef CPU_PP502x | ||
494 | /* Enable DMA controller */ | ||
495 | DMA_MASTER_CONTROL |= DMA_MASTER_CONTROL_EN; | ||
496 | /* FIQ priority for DMA */ | ||
497 | CPU_INT_PRIORITY |= DMA_MASK; | ||
498 | /* Enable request?? Not setting or clearing everything doesn't seem to | ||
499 | * prevent it operating. Perhaps important for reliability (how requests | ||
500 | * are handled). */ | ||
501 | DMA_REQ_STATUS |= 1ul << DMA_REQ_IIS; | ||
502 | DMA0_STATUS; | ||
503 | #else | ||
504 | /* Set up banked registers for FIQ mode */ | ||
505 | |||
506 | /* Use non-banked registers for scratch. */ | ||
507 | register volatile void *iiscfg asm("r0") = &IISCONFIG; | ||
508 | register volatile void *dmapd asm("r1") = &dma_play_data; | ||
509 | |||
510 | asm volatile ( | ||
511 | "mrs r2, cpsr \n" /* Save mode and interrupt status */ | ||
512 | "msr cpsr_c, #0xd1 \n" /* Switch to FIQ mode */ | ||
513 | "mov r8, #0 \n" | ||
514 | "mov r9, #0 \n" | ||
515 | "mov r10, %[iiscfg] \n" | ||
516 | "mov r11, %[dmapd] \n" | ||
517 | "msr cpsr_c, r2 \n" | ||
518 | : | ||
519 | : [iiscfg]"r"(iiscfg), [dmapd]"r"(dmapd) | ||
520 | : "r2"); | ||
521 | |||
522 | /* FIQ priority for I2S */ | ||
523 | CPU_INT_PRIORITY |= IIS_MASK; | ||
524 | CPU_INT_EN = IIS_MASK; | ||
525 | #endif | ||
526 | |||
527 | IISCONFIG |= IIS_TXFIFOEN; | ||
528 | } | ||
529 | |||
530 | void pcm_play_dma_postinit(void) | ||
531 | { | ||
532 | audiohw_postinit(); | ||
533 | } | ||
534 | |||
535 | const void * pcm_play_dma_get_peak_buffer(int *count) | ||
536 | { | ||
537 | unsigned long addr, size; | ||
538 | |||
539 | int status = disable_fiq_save(); | ||
540 | addr = dma_play_data.addr; | ||
541 | size = dma_play_data.size; | ||
542 | restore_fiq(status); | ||
543 | |||
544 | *count = size >> 2; | ||
545 | return (void *)((addr + 2) & ~3); | ||
546 | } | ||
547 | |||
548 | /**************************************************************************** | ||
549 | ** Recording DMA transfer | ||
550 | **/ | ||
551 | #ifdef HAVE_RECORDING | ||
552 | /* PCM recording interrupt routine lockout */ | ||
553 | static struct dma_data dma_rec_data IBSS_ATTR = | ||
554 | { | ||
555 | /* Initialize to a locked, stopped state */ | ||
556 | { .addr = 0 }, | ||
557 | .size = 0, | ||
558 | #if NUM_CORES > 1 | ||
559 | .core = 0x00, | ||
560 | #endif | ||
561 | .locked = 0, | ||
562 | .state = 0 | ||
563 | }; | ||
564 | |||
565 | /* For the locks, FIQ must be disabled because the handler manipulates | ||
566 | IISCONFIG and the operation is not atomic - dual core support | ||
567 | will require other measures */ | ||
568 | void pcm_rec_lock(void) | ||
569 | { | ||
570 | int status = disable_fiq_save(); | ||
571 | |||
572 | if (++dma_rec_data.locked == 1) | ||
573 | IIS_IRQRX_REG &= ~IIS_IRQRX; | ||
574 | |||
575 | restore_fiq(status); | ||
576 | } | ||
577 | |||
578 | void pcm_rec_unlock(void) | ||
579 | { | ||
580 | int status = disable_fiq_save(); | ||
581 | |||
582 | if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0) | ||
583 | IIS_IRQRX_REG |= IIS_IRQRX; | ||
584 | |||
585 | restore_fiq(status); | ||
586 | } | ||
587 | |||
588 | /* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */ | ||
589 | void fiq_record(void) ICODE_ATTR __attribute__((interrupt ("FIQ"))); | ||
590 | |||
591 | #if defined(SANSA_C200) || defined(SANSA_E200) | ||
592 | void fiq_record(void) | ||
593 | { | ||
594 | register int32_t value; | ||
595 | |||
596 | if (audio_channels == 2) { | ||
597 | /* RX is stereo */ | ||
598 | while (dma_rec_data.size > 0) { | ||
599 | if (IIS_RX_FULL_COUNT < 2) { | ||
600 | return; | ||
601 | } | ||
602 | |||
603 | /* Discard every other sample since ADC clock is 1/2 LRCK */ | ||
604 | value = IISFIFO_RD; | ||
605 | IISFIFO_RD; | ||
606 | |||
607 | *dma_rec_data.p16++ = value; | ||
608 | dma_rec_data.size -= 4; | ||
609 | |||
610 | /* TODO: Figure out how to do IIS loopback */ | ||
611 | if (audio_output_source != AUDIO_SRC_PLAYBACK) { | ||
612 | if (IIS_TX_FREE_COUNT >= 16) { | ||
613 | /* Resync the output FIFO - it ran dry */ | ||
614 | IISFIFO_WR = 0; | ||
615 | IISFIFO_WR = 0; | ||
616 | } | ||
617 | IISFIFO_WR = value; | ||
618 | IISFIFO_WR = value; | ||
619 | } | ||
620 | } | ||
621 | } | ||
622 | else { | ||
623 | /* RX is left channel mono */ | ||
624 | while (dma_rec_data.size > 0) { | ||
625 | if (IIS_RX_FULL_COUNT < 2) { | ||
626 | return; | ||
627 | } | ||
628 | |||
629 | /* Discard every other sample since ADC clock is 1/2 LRCK */ | ||
630 | value = IISFIFO_RD; | ||
631 | IISFIFO_RD; | ||
632 | |||
633 | value = (uint16_t)value | (value << 16); | ||
634 | |||
635 | *dma_rec_data.p16++ = value; | ||
636 | dma_rec_data.size -= 4; | ||
637 | |||
638 | if (audio_output_source != AUDIO_SRC_PLAYBACK) { | ||
639 | if (IIS_TX_FREE_COUNT >= 16) { | ||
640 | /* Resync the output FIFO - it ran dry */ | ||
641 | IISFIFO_WR = 0; | ||
642 | IISFIFO_WR = 0; | ||
643 | } | ||
644 | |||
645 | value = *((int32_t *)dma_rec_data.p16 - 1); | ||
646 | IISFIFO_WR = value; | ||
647 | IISFIFO_WR = value; | ||
648 | } | ||
649 | } | ||
650 | } | ||
651 | |||
652 | pcm_rec_more_ready_callback(0, (void *)&dma_rec_data.addr, | ||
653 | &dma_rec_data.size); | ||
654 | } | ||
655 | |||
656 | #else | ||
657 | void fiq_record(void) | ||
658 | { | ||
659 | while (dma_rec_data.size > 0) { | ||
660 | if (IIS_RX_FULL_COUNT < 2) { | ||
661 | return; | ||
662 | } | ||
663 | |||
664 | #if SAMPLE_SIZE == 16 | ||
665 | *dma_rec_data.p16++ = IISFIFO_RD; | ||
666 | #elif SAMPLE_SIZE == 32 | ||
667 | *dma_rec_data.p32++ = IISFIFO_RD >> 16; | ||
668 | *dma_rec_data.p32++ = IISFIFO_RD >> 16; | ||
669 | #endif | ||
670 | dma_rec_data.size -= 4; | ||
671 | } | ||
672 | |||
673 | pcm_rec_more_ready_callback(0, (void *)&dma_rec_data.addr, | ||
674 | &dma_rec_data.size); | ||
675 | } | ||
676 | |||
677 | #endif /* SANSA_E200 */ | ||
678 | |||
679 | void pcm_rec_dma_stop(void) | ||
680 | { | ||
681 | /* disable interrupt */ | ||
682 | IIS_IRQRX_REG &= ~IIS_IRQRX; | ||
683 | |||
684 | dma_rec_data.state = 0; | ||
685 | dma_rec_data.size = 0; | ||
686 | #if NUM_CORES > 1 | ||
687 | dma_rec_data.core = 0x00; | ||
688 | #endif | ||
689 | |||
690 | /* disable fifo */ | ||
691 | IISCONFIG &= ~IIS_RXFIFOEN; | ||
692 | IISFIFO_CFG |= IIS_RXCLR; | ||
693 | } | ||
694 | |||
695 | void pcm_rec_dma_start(void *addr, size_t size) | ||
696 | { | ||
697 | pcm_rec_dma_stop(); | ||
698 | |||
699 | dma_rec_data.addr = (unsigned long)addr; | ||
700 | dma_rec_data.size = size; | ||
701 | #if NUM_CORES > 1 | ||
702 | /* This will become more important later - and different ! */ | ||
703 | dma_rec_data.core = processor_id(); /* save initiating core */ | ||
704 | #endif | ||
705 | /* setup FIQ handler */ | ||
706 | fiq_function = fiq_record; | ||
707 | |||
708 | /* interrupt on full fifo, enable record fifo interrupt */ | ||
709 | dma_rec_data.state = 1; | ||
710 | |||
711 | /* enable RX FIFO */ | ||
712 | IISCONFIG |= IIS_RXFIFOEN; | ||
713 | |||
714 | /* enable IIS interrupt as FIQ */ | ||
715 | CPU_INT_PRIORITY |= IIS_MASK; | ||
716 | CPU_INT_EN = IIS_MASK; | ||
717 | } | ||
718 | |||
719 | void pcm_rec_dma_close(void) | ||
720 | { | ||
721 | pcm_rec_dma_stop(); | ||
722 | } /* pcm_close_recording */ | ||
723 | |||
724 | void pcm_rec_dma_init(void) | ||
725 | { | ||
726 | pcm_rec_dma_stop(); | ||
727 | } /* pcm_init */ | ||
728 | |||
729 | const void * pcm_rec_dma_get_peak_buffer(void) | ||
730 | { | ||
731 | return (void *)((unsigned long)dma_rec_data.addr & ~3); | ||
732 | } /* pcm_rec_dma_get_peak_buffer */ | ||
733 | |||
734 | #endif /* HAVE_RECORDING */ | ||
diff --git a/firmware/target/arm/pp/system-pp5002.c b/firmware/target/arm/pp/system-pp5002.c new file mode 100644 index 0000000000..3186d3739a --- /dev/null +++ b/firmware/target/arm/pp/system-pp5002.c | |||
@@ -0,0 +1,227 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Alan Korr | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "system.h" | ||
22 | |||
23 | #ifndef BOOTLOADER | ||
24 | #include "adc-target.h" | ||
25 | #include "button-target.h" | ||
26 | |||
27 | extern void TIMER1(void); | ||
28 | extern void TIMER2(void); | ||
29 | |||
30 | void __attribute__((interrupt("IRQ"))) irq_handler(void) | ||
31 | { | ||
32 | if(CURRENT_CORE == CPU) | ||
33 | { | ||
34 | if (CPU_INT_STAT & TIMER1_MASK) | ||
35 | TIMER1(); | ||
36 | else if (CPU_INT_STAT & TIMER2_MASK) | ||
37 | TIMER2(); | ||
38 | else if (CPU_INT_STAT & GPIO_MASK) | ||
39 | { | ||
40 | if (GPIOA_INT_STAT) | ||
41 | ipod_3g_button_int(); | ||
42 | #ifdef IPOD_1G2G | ||
43 | if (GPIOB_INT_STAT & 0x04) | ||
44 | ipod_2g_adc_int(); | ||
45 | #endif | ||
46 | } | ||
47 | } | ||
48 | else | ||
49 | { | ||
50 | if (COP_INT_STAT & TIMER2_MASK) | ||
51 | TIMER2(); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | #endif | ||
56 | |||
57 | #ifndef BOOTLOADER | ||
58 | void ICODE_ATTR __attribute__((naked)) commit_dcache(void) | ||
59 | { | ||
60 | asm volatile( | ||
61 | "mov r0, #0xf0000000 \n" | ||
62 | "add r0, r0, #0xc000 \n" /* r0 = CACHE_FLUSH_BASE */ | ||
63 | "add r1, r0, #0x2000 \n" /* r1 = CACHE_FLUSH_BASE + CACHE_SIZE */ | ||
64 | "mov r2, #0 \n" | ||
65 | "1: \n" | ||
66 | "str r2, [r0], #16 \n" /* Commit */ | ||
67 | "cmp r0, r1 \n" | ||
68 | "blo 1b \n" | ||
69 | "bx lr \n" | ||
70 | ); | ||
71 | } | ||
72 | |||
73 | void ICODE_ATTR __attribute__((naked)) commit_discard_idcache(void) | ||
74 | { | ||
75 | asm volatile( | ||
76 | "mov r0, #0xf0000000 \n" | ||
77 | "add r2, r0, #0x4000 \n" /* r1 = CACHE_INVALIDATE_BASE */ | ||
78 | "add r0, r0, #0xc000 \n" /* r0 = CACHE_FLUSH_BASE */ | ||
79 | "add r1, r0, #0x2000 \n" /* r2 = CACHE_FLUSH_BASE + CACHE_SIZE */ | ||
80 | "mov r3, #0 \n" | ||
81 | "1: \n" | ||
82 | "str r3, [r0], #16 \n" /* Commit */ | ||
83 | "str r3, [r2], #16 \n" /* Discard */ | ||
84 | "cmp r0, r1 \n" | ||
85 | "blo 1b \n" | ||
86 | "bx lr \n" | ||
87 | ); | ||
88 | } | ||
89 | |||
90 | void commit_discard_dcache(void) __attribute__((alias("commit_discard_idcache"))); | ||
91 | |||
92 | static void ipod_init_cache(void) | ||
93 | { | ||
94 | /* Initialising the cache in the iPod bootloader prevents Rockbox from starting */ | ||
95 | PROC_STAT &= ~0x700; | ||
96 | outl(0x4000, 0xcf004020); | ||
97 | |||
98 | CACHE_CTL = CACHE_CTL_INIT; | ||
99 | |||
100 | asm volatile( | ||
101 | "mov r0, #0xf0000000 \n" | ||
102 | "add r0, r0, #0x4000 \n" /* r0 = CACHE_INVALIDATE_BASE */ | ||
103 | "add r1, r0, #0x2000 \n" /* r1 = CACHE_INVALIDATE_BASE + CACHE_SIZE */ | ||
104 | "mov r2, #0 \n" | ||
105 | "1: \n" | ||
106 | "str r2, [r0], #16 \n" /* Invalidate */ | ||
107 | "cmp r0, r1 \n" | ||
108 | "blo 1b \n" | ||
109 | : : : "r0", "r1", "r2" | ||
110 | ); | ||
111 | |||
112 | /* Cache if (addr & mask) >> 16 == (mask & match) >> 16: | ||
113 | * yes: 0x00000000 - 0x03ffffff | ||
114 | * no: 0x04000000 - 0x1fffffff | ||
115 | * yes: 0x20000000 - 0x23ffffff | ||
116 | * no: 0x24000000 - 0x3fffffff <= range containing uncached alias | ||
117 | */ | ||
118 | CACHE_MASK = 0x00001c00; | ||
119 | CACHE_OPERATION = 0x3fc0; | ||
120 | |||
121 | CACHE_CTL = CACHE_CTL_INIT | CACHE_CTL_RUN; | ||
122 | } | ||
123 | |||
124 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
125 | void set_cpu_frequency(long frequency) | ||
126 | #else | ||
127 | static void pp_set_cpu_frequency(long frequency) | ||
128 | #endif | ||
129 | { | ||
130 | cpu_frequency = frequency; | ||
131 | |||
132 | PLL_CONTROL |= 0x6000; /* make sure some enable bits are set */ | ||
133 | CLOCK_ENABLE = 0x01; /* select source #1 */ | ||
134 | |||
135 | switch (frequency) | ||
136 | { | ||
137 | case CPUFREQ_MAX: | ||
138 | PLL_UNLOCK = 0xd19b; /* unlock frequencies > 66MHz */ | ||
139 | CLOCK_SOURCE = 0xa9; /* source #1: 24 Mhz, source #2..#4: PLL */ | ||
140 | PLL_CONTROL = 0xe000; /* PLL enabled */ | ||
141 | PLL_DIV = 3; /* 10/3 * 24MHz */ | ||
142 | PLL_MULT = 10; | ||
143 | udelay(200); /* wait for relock */ | ||
144 | break; | ||
145 | |||
146 | case CPUFREQ_NORMAL: | ||
147 | CLOCK_SOURCE = 0xa9; /* source #1: 24 Mhz, source #2..#4: PLL */ | ||
148 | PLL_CONTROL = 0xe000; /* PLL enabled */ | ||
149 | PLL_DIV = 4; /* 5/4 * 24MHz */ | ||
150 | PLL_MULT = 5; | ||
151 | udelay(200); /* wait for relock */ | ||
152 | break; | ||
153 | |||
154 | case CPUFREQ_SLEEP: | ||
155 | CLOCK_SOURCE = 0x51; /* source #2: 32kHz, #1, #2, #4: 24MHz */ | ||
156 | PLL_CONTROL = 0x6000; /* PLL disabled */ | ||
157 | udelay(10000); /* let 32kHz source stabilize? */ | ||
158 | break; | ||
159 | |||
160 | default: | ||
161 | CLOCK_SOURCE = 0x55; /* source #1..#4: 24 Mhz */ | ||
162 | PLL_CONTROL = 0x6000; /* PLL disabled */ | ||
163 | cpu_frequency = CPUFREQ_DEFAULT; | ||
164 | break; | ||
165 | } | ||
166 | CLOCK_ENABLE = 0x02; /* select source #2 */ | ||
167 | } | ||
168 | #endif /* !BOOTLOADER */ | ||
169 | |||
170 | void system_init(void) | ||
171 | { | ||
172 | #ifndef BOOTLOADER | ||
173 | if (CURRENT_CORE == CPU) | ||
174 | { | ||
175 | /* Remap the flash ROM on CPU, keep hidden from COP: | ||
176 | * 0x00000000-0x03ffffff = 0x20000000-0x23ffffff */ | ||
177 | MMAP1_LOGICAL = 0x20003c00; | ||
178 | MMAP1_PHYSICAL = 0x00003f84; | ||
179 | |||
180 | #if defined(IPOD_1G2G) || defined(IPOD_3G) | ||
181 | DEV_EN = 0x0b9f; /* don't clock unused PP5002 hardware components */ | ||
182 | outl(0x0035, 0xcf005004); /* DEV_EN2 ? */ | ||
183 | #endif | ||
184 | |||
185 | INT_FORCED_CLR = -1; | ||
186 | CPU_INT_DIS = -1; | ||
187 | COP_INT_DIS = -1; | ||
188 | |||
189 | GPIOA_INT_EN = 0; | ||
190 | GPIOB_INT_EN = 0; | ||
191 | GPIOC_INT_EN = 0; | ||
192 | GPIOD_INT_EN = 0; | ||
193 | |||
194 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
195 | #if NUM_CORES > 1 | ||
196 | cpu_boost_init(); | ||
197 | #endif | ||
198 | #else | ||
199 | pp_set_cpu_frequency(CPUFREQ_MAX); | ||
200 | #endif | ||
201 | } | ||
202 | ipod_init_cache(); | ||
203 | #endif | ||
204 | } | ||
205 | |||
206 | void system_reboot(void) | ||
207 | { | ||
208 | DEV_RS |= 4; | ||
209 | while (1); | ||
210 | } | ||
211 | |||
212 | void system_exception_wait(void) | ||
213 | { | ||
214 | /* FIXME: we just need the right buttons */ | ||
215 | CPU_INT_DIS = -1; | ||
216 | COP_INT_DIS = -1; | ||
217 | |||
218 | /* Halt */ | ||
219 | sleep_core(CURRENT_CORE); | ||
220 | while (1); | ||
221 | } | ||
222 | |||
223 | int system_memory_guard(int newmode) | ||
224 | { | ||
225 | (void)newmode; | ||
226 | return 0; | ||
227 | } | ||
diff --git a/firmware/target/arm/pp/system-pp502x.c b/firmware/target/arm/pp/system-pp502x.c new file mode 100644 index 0000000000..847e8a462e --- /dev/null +++ b/firmware/target/arm/pp/system-pp502x.c | |||
@@ -0,0 +1,607 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Alan Korr | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "system.h" | ||
22 | #include "thread.h" | ||
23 | #include "i2s.h" | ||
24 | #include "i2c-pp.h" | ||
25 | #include "as3514.h" | ||
26 | #ifdef HAVE_HOTSWAP | ||
27 | #include "sd-pp-target.h" | ||
28 | #endif | ||
29 | #include "button-target.h" | ||
30 | #include "usb_drv.h" | ||
31 | #ifdef HAVE_REMOTE_LCD | ||
32 | #include "lcd-remote-target.h" | ||
33 | #endif | ||
34 | |||
35 | #if !defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_USB_MODE) | ||
36 | extern void TIMER1(void); | ||
37 | extern void TIMER2(void); | ||
38 | extern void SERIAL0(void); | ||
39 | |||
40 | #if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1) | ||
41 | static struct corelock cpufreq_cl SHAREDBSS_ATTR; | ||
42 | #endif | ||
43 | |||
44 | #if defined(IPOD_VIDEO) && !defined(BOOTLOADER) | ||
45 | unsigned char probed_ramsize; | ||
46 | #endif | ||
47 | |||
48 | void __attribute__((interrupt("IRQ"))) irq_handler(void) | ||
49 | { | ||
50 | if(CURRENT_CORE == CPU) | ||
51 | { | ||
52 | if (CPU_INT_STAT & TIMER1_MASK) { | ||
53 | TIMER1(); | ||
54 | } | ||
55 | else if (CPU_INT_STAT & TIMER2_MASK) { | ||
56 | TIMER2(); | ||
57 | } | ||
58 | #ifdef HAVE_USBSTACK | ||
59 | /* Rather high priority - place near front */ | ||
60 | else if (CPU_INT_STAT & USB_MASK) { | ||
61 | usb_drv_int(); | ||
62 | } | ||
63 | #endif | ||
64 | #if defined(IPOD_MINI) /* Mini 1st gen only, mini 2nd gen uses iPod 4G code */ | ||
65 | else if (CPU_HI_INT_STAT & GPIO0_MASK) { | ||
66 | if ((GPIOA_INT_STAT & 0x3f) || (GPIOB_INT_STAT & 0x30)) | ||
67 | ipod_mini_button_int(); | ||
68 | if (GPIOC_INT_STAT & 0x02) | ||
69 | firewire_insert_int(); | ||
70 | if (GPIOD_INT_STAT & 0x08) | ||
71 | usb_insert_int(); | ||
72 | } | ||
73 | /* end IPOD_MINI */ | ||
74 | #elif CONFIG_KEYPAD == IPOD_4G_PAD /* except Mini 1st gen, handled above */ | ||
75 | else if (CPU_HI_INT_STAT & I2C_MASK) { | ||
76 | ipod_4g_button_int(); | ||
77 | } | ||
78 | #if defined(IPOD_COLOR) || defined(IPOD_MINI2G) || defined(IPOD_4G) | ||
79 | else if (CPU_HI_INT_STAT & GPIO0_MASK) { | ||
80 | if (GPIOC_INT_STAT & 0x02) | ||
81 | firewire_insert_int(); | ||
82 | if (GPIOD_INT_STAT & 0x08) | ||
83 | usb_insert_int(); | ||
84 | } | ||
85 | #elif defined(IPOD_NANO) || defined(IPOD_VIDEO) | ||
86 | else if (CPU_HI_INT_STAT & GPIO2_MASK) { | ||
87 | if (GPIOL_INT_STAT & 0x10) | ||
88 | usb_insert_int(); | ||
89 | } | ||
90 | #endif | ||
91 | /* end CONFIG_KEYPAD == IPOD_4G_PAD */ | ||
92 | #elif defined(IRIVER_H10) || defined(IRIVER_H10_5GB) | ||
93 | else if (CPU_HI_INT_STAT & GPIO2_MASK) { | ||
94 | if (GPIOL_INT_STAT & 0x04) | ||
95 | usb_insert_int(); | ||
96 | } | ||
97 | /* end IRIVER_H10 || IRIVER_H10_5GB */ | ||
98 | #elif defined(SANSA_E200) | ||
99 | else if (CPU_HI_INT_STAT & GPIO0_MASK) { | ||
100 | #ifdef HAVE_HOTSWAP | ||
101 | if (GPIOA_INT_STAT & 0x80) | ||
102 | microsd_int(); | ||
103 | #endif | ||
104 | if (GPIOB_INT_STAT & 0x10) | ||
105 | usb_insert_int(); | ||
106 | } | ||
107 | else if (CPU_HI_INT_STAT & GPIO1_MASK) { | ||
108 | if (GPIOF_INT_STAT & 0xff) | ||
109 | button_int(); | ||
110 | if (GPIOH_INT_STAT & 0xc0) | ||
111 | clickwheel_int(); | ||
112 | } | ||
113 | /* end SANSA_E200 */ | ||
114 | #elif defined(SANSA_C200) | ||
115 | else if (CPU_HI_INT_STAT & GPIO1_MASK) { | ||
116 | if (GPIOH_INT_STAT & 0x02) | ||
117 | usb_insert_int(); | ||
118 | } | ||
119 | #ifdef HAVE_HOTSWAP | ||
120 | else if (CPU_HI_INT_STAT & GPIO2_MASK) { | ||
121 | if (GPIOL_INT_STAT & 0x08) | ||
122 | microsd_int(); | ||
123 | } | ||
124 | #endif | ||
125 | /* end SANSA_C200 */ | ||
126 | #elif defined(MROBE_100) | ||
127 | else if (CPU_HI_INT_STAT & GPIO0_MASK) { | ||
128 | if (GPIOD_INT_STAT & 0x02) | ||
129 | button_int(); | ||
130 | if (GPIOD_INT_STAT & 0x80) | ||
131 | headphones_int(); | ||
132 | |||
133 | } | ||
134 | else if (CPU_HI_INT_STAT & GPIO2_MASK) { | ||
135 | if (GPIOL_INT_STAT & 0x04) | ||
136 | usb_insert_int(); | ||
137 | } | ||
138 | /* end MROBE_100 */ | ||
139 | #elif defined(PHILIPS_SA9200) | ||
140 | else if (CPU_HI_INT_STAT & GPIO0_MASK) { | ||
141 | if (GPIOD_INT_STAT & 0x02) | ||
142 | button_int(); | ||
143 | if (GPIOB_INT_STAT & 0x40) | ||
144 | usb_insert_int(); | ||
145 | } | ||
146 | /* end PHILIPS_SA9200 */ | ||
147 | #elif defined(PHILIPS_HDD1630) || defined(PHILIPS_HDD6330) | ||
148 | else if (CPU_HI_INT_STAT & GPIO0_MASK) { | ||
149 | if (GPIOA_INT_STAT & 0x20) | ||
150 | button_int(); | ||
151 | } | ||
152 | else if (CPU_HI_INT_STAT & GPIO1_MASK) { | ||
153 | if (GPIOE_INT_STAT & 0x04) | ||
154 | usb_insert_int(); | ||
155 | } | ||
156 | /* end PHILIPS_HDD1630 || PHILIPS_HDD6330 */ | ||
157 | #elif defined(SAMSUNG_YH820) || defined(SAMSUNG_YH920) || defined(SAMSUNG_YH925) | ||
158 | else if (CPU_HI_INT_STAT & GPIO0_MASK) { | ||
159 | if (GPIOD_INT_STAT & 0x10) | ||
160 | usb_insert_int(); | ||
161 | } | ||
162 | /* end SAMSUNG_YHxxx */ | ||
163 | #elif defined(PBELL_VIBE500) | ||
164 | else if (CPU_HI_INT_STAT & GPIO0_MASK) { | ||
165 | if (GPIOA_INT_STAT & 0x20) | ||
166 | button_int(); | ||
167 | } | ||
168 | else if (CPU_HI_INT_STAT & GPIO2_MASK) { | ||
169 | if (GPIOL_INT_STAT & 0x04) | ||
170 | usb_insert_int(); | ||
171 | } | ||
172 | /* end PBELL_VIBE500 */ | ||
173 | #endif | ||
174 | #ifdef IPOD_ACCESSORY_PROTOCOL | ||
175 | else if (CPU_HI_INT_STAT & SER0_MASK) { | ||
176 | SERIAL0(); | ||
177 | } | ||
178 | #endif | ||
179 | } else { | ||
180 | if (COP_INT_STAT & TIMER2_MASK) | ||
181 | TIMER2(); | ||
182 | } | ||
183 | } | ||
184 | #endif /* BOOTLOADER || HAVE_BOOTLOADER_USB_MODE */ | ||
185 | |||
186 | #if !defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_USB_MODE) | ||
187 | static void disable_all_interrupts(void) | ||
188 | { | ||
189 | COP_HI_INT_DIS = -1; | ||
190 | CPU_HI_INT_DIS = -1; | ||
191 | HI_INT_FORCED_CLR = -1; | ||
192 | |||
193 | COP_INT_DIS = -1; | ||
194 | CPU_INT_DIS = -1; | ||
195 | INT_FORCED_CLR = -1; | ||
196 | |||
197 | GPIOA_INT_EN = 0; | ||
198 | GPIOB_INT_EN = 0; | ||
199 | GPIOC_INT_EN = 0; | ||
200 | GPIOD_INT_EN = 0; | ||
201 | GPIOE_INT_EN = 0; | ||
202 | GPIOF_INT_EN = 0; | ||
203 | GPIOG_INT_EN = 0; | ||
204 | GPIOH_INT_EN = 0; | ||
205 | GPIOI_INT_EN = 0; | ||
206 | GPIOJ_INT_EN = 0; | ||
207 | GPIOK_INT_EN = 0; | ||
208 | GPIOL_INT_EN = 0; | ||
209 | } | ||
210 | |||
211 | void ICODE_ATTR commit_dcache(void) | ||
212 | { | ||
213 | if (CACHE_CTL & CACHE_CTL_ENABLE) | ||
214 | { | ||
215 | CACHE_OPERATION |= CACHE_OP_FLUSH; | ||
216 | while ((CACHE_CTL & CACHE_CTL_BUSY) != 0); | ||
217 | nop; nop; nop; nop; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | void ICODE_ATTR commit_discard_idcache(void) | ||
222 | { | ||
223 | if (CACHE_CTL & CACHE_CTL_ENABLE) | ||
224 | { | ||
225 | CACHE_OPERATION |= CACHE_OP_FLUSH | CACHE_OP_INVALIDATE; | ||
226 | while ((CACHE_CTL & CACHE_CTL_BUSY) != 0); | ||
227 | nop; nop; nop; nop; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | void commit_discard_dcache(void) __attribute__((alias("commit_discard_idcache"))); | ||
232 | |||
233 | static void init_cache(void) | ||
234 | { | ||
235 | /* Initialising the cache in the iPod bootloader may prevent Rockbox from starting | ||
236 | * depending on the model */ | ||
237 | |||
238 | /* cache init mode */ | ||
239 | CACHE_CTL &= ~(CACHE_CTL_ENABLE | CACHE_CTL_RUN); | ||
240 | CACHE_CTL |= CACHE_CTL_INIT; | ||
241 | |||
242 | #ifndef BOOTLOADER | ||
243 | /* what's this do? */ | ||
244 | CACHE_PRIORITY |= CURRENT_CORE == CPU ? 0x10 : 0x20; | ||
245 | #endif | ||
246 | |||
247 | /* Cache if (addr & mask) >> 16 == (mask & match) >> 16: | ||
248 | * yes: 0x00000000 - 0x03ffffff | ||
249 | * no: 0x04000000 - 0x1fffffff | ||
250 | * yes: 0x20000000 - 0x23ffffff | ||
251 | * no: 0x24000000 - 0x3fffffff | ||
252 | */ | ||
253 | CACHE_MASK = 0x00001c00; | ||
254 | CACHE_OPERATION = 0xfc0; | ||
255 | |||
256 | /* enable cache */ | ||
257 | CACHE_CTL |= CACHE_CTL_INIT | CACHE_CTL_ENABLE | CACHE_CTL_RUN; | ||
258 | nop; nop; nop; nop; | ||
259 | } | ||
260 | #endif /* BOOTLOADER || HAVE_BOOTLOADER_USB_MODE */ | ||
261 | |||
262 | /* We need this for Sansas since we boost the cpu in their bootloader */ | ||
263 | #if !defined(BOOTLOADER) || (defined(SANSA_E200) || defined(SANSA_C200) || \ | ||
264 | defined(PHILIPS_SA9200)) | ||
265 | void scale_suspend_core(bool suspend) ICODE_ATTR; | ||
266 | void scale_suspend_core(bool suspend) | ||
267 | { | ||
268 | unsigned int core = CURRENT_CORE; | ||
269 | IF_COP( unsigned int othercore = 1 - core; ) | ||
270 | static int oldstatus IBSS_ATTR; | ||
271 | |||
272 | if (suspend) | ||
273 | { | ||
274 | oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); | ||
275 | IF_COP( PROC_CTL(othercore) = 0x40000000; nop; ) | ||
276 | PROC_CTL(core) = 0x48000003; nop; | ||
277 | } | ||
278 | else | ||
279 | { | ||
280 | PROC_CTL(core) = 0x4800001f; nop; | ||
281 | IF_COP( PROC_CTL(othercore) = 0x00000000; nop; ) | ||
282 | restore_interrupt(oldstatus); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
287 | void set_cpu_frequency(long frequency) ICODE_ATTR; | ||
288 | void set_cpu_frequency(long frequency) | ||
289 | #else | ||
290 | static void pp_set_cpu_frequency(long frequency) | ||
291 | #endif | ||
292 | { | ||
293 | #if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1) | ||
294 | corelock_lock(&cpufreq_cl); | ||
295 | #endif | ||
296 | |||
297 | switch (frequency) | ||
298 | { | ||
299 | /* Note1: The PP5022 PLL must be run at >= 96MHz | ||
300 | * Bits 20..21 select the post divider (1/2/4/8). | ||
301 | * PP5026 is similar to PP5022 except it doesn't | ||
302 | * have this limitation (and the post divider?) | ||
303 | * Note2: CLOCK_SOURCE is set via 0=32kHz, 1=16MHz, | ||
304 | * 2=24MHz, 3=33MHz, 4=48MHz, 5=SLOW, 6=FAST, 7=PLL. | ||
305 | * SLOW = 24MHz / (DIV_SLOW + 1), DIV = Bits 16-19 | ||
306 | * FAST = PLL / (DIV_FAST + 1), DIV = Bits 20-23 */ | ||
307 | case CPUFREQ_SLEEP: | ||
308 | cpu_frequency = CPUFREQ_SLEEP; | ||
309 | PLL_CONTROL |= 0x0c000000; | ||
310 | scale_suspend_core(true); | ||
311 | CLOCK_SOURCE = 0x20000000; /* source #1, #2, #3, #4: 32kHz (#2 active) */ | ||
312 | scale_suspend_core(false); | ||
313 | PLL_CONTROL &= ~0x80000000; /* disable PLL */ | ||
314 | DEV_INIT2 &= ~INIT_PLL; /* disable PLL power */ | ||
315 | break; | ||
316 | |||
317 | case CPUFREQ_MAX: | ||
318 | cpu_frequency = CPUFREQ_MAX; | ||
319 | DEV_INIT2 |= INIT_PLL; /* enable PLL power */ | ||
320 | PLL_CONTROL |= 0x88000000; /* enable PLL */ | ||
321 | scale_suspend_core(true); | ||
322 | CLOCK_SOURCE = 0x20002222; /* source #1, #2, #3, #4: 24MHz (#2 active) */ | ||
323 | DEV_TIMING1 = 0x00000303; | ||
324 | scale_suspend_core(false); | ||
325 | #if defined(IPOD_MINI2G) | ||
326 | MLCD_SCLK_DIV = 0x00000001; /* Mono LCD bridge serial clock divider */ | ||
327 | #elif defined(IPOD_NANO) | ||
328 | IDE0_CFG |= 0x10000000; /* set ">65MHz" bit */ | ||
329 | #endif | ||
330 | #if CONFIG_CPU == PP5020 | ||
331 | PLL_CONTROL = 0x8a020a03; /* 80 MHz = 10/3 * 24MHz */ | ||
332 | PLL_STATUS = 0xd19b; /* unlock frequencies > 66MHz */ | ||
333 | PLL_CONTROL = 0x8a020a03; /* repeat setup */ | ||
334 | udelay(500); /* wait for relock */ | ||
335 | #elif (CONFIG_CPU == PP5022) || (CONFIG_CPU == PP5024) | ||
336 | PLL_CONTROL = 0x8a121403; /* 80 MHz = (20/3 * 24MHz) / 2 */ | ||
337 | while (!(PLL_STATUS & 0x80000000)); /* wait for relock */ | ||
338 | #endif | ||
339 | scale_suspend_core(true); | ||
340 | DEV_TIMING1 = 0x00000808; | ||
341 | CLOCK_SOURCE = 0x20007777; /* source #1, #2, #3, #4: PLL (#2 active) */ | ||
342 | scale_suspend_core(false); | ||
343 | break; | ||
344 | #if 0 /******** CPUFREQ_NORMAL = 24MHz without PLL ********/ | ||
345 | case CPUFREQ_NORMAL: | ||
346 | cpu_frequency = CPUFREQ_NORMAL; | ||
347 | PLL_CONTROL |= 0x08000000; | ||
348 | scale_suspend_core(true); | ||
349 | CLOCK_SOURCE = 0x20002222; /* source #1, #2, #3, #4: 24MHz (#2 active) */ | ||
350 | DEV_TIMING1 = 0x00000303; | ||
351 | #if defined(IPOD_MINI2G) | ||
352 | MLCD_SCLK_DIV = 0x00000000; /* Mono LCD bridge serial clock divider */ | ||
353 | #elif defined(IPOD_NANO) | ||
354 | IDE0_CFG &= ~0x10000000; /* clear ">65MHz" bit */ | ||
355 | #endif | ||
356 | scale_suspend_core(false); | ||
357 | PLL_CONTROL &= ~0x80000000; /* disable PLL */ | ||
358 | DEV_INIT2 &= ~INIT_PLL; /* disable PLL power */ | ||
359 | break; | ||
360 | #else /******** CPUFREQ_NORMAL = 30MHz with PLL ********/ | ||
361 | case CPUFREQ_NORMAL: | ||
362 | cpu_frequency = CPUFREQ_NORMAL; | ||
363 | DEV_INIT2 |= INIT_PLL; /* enable PLL power */ | ||
364 | PLL_CONTROL |= 0x88000000; /* enable PLL */ | ||
365 | scale_suspend_core(true); | ||
366 | CLOCK_SOURCE = 0x20002222; /* source #1, #2, #3, #4: 24MHz (#2 active) */ | ||
367 | DEV_TIMING1 = 0x00000303; | ||
368 | scale_suspend_core(false); | ||
369 | #if defined(IPOD_MINI2G) | ||
370 | MLCD_SCLK_DIV = 0x00000000; /* Mono LCD bridge serial clock divider */ | ||
371 | #elif defined(IPOD_NANO) | ||
372 | IDE0_CFG &= ~0x10000000; /* clear ">65MHz" bit */ | ||
373 | #endif | ||
374 | #if CONFIG_CPU == PP5020 | ||
375 | PLL_CONTROL = 0x8a020504; /* 30 MHz = 5/4 * 24MHz */ | ||
376 | udelay(500); /* wait for relock */ | ||
377 | #elif (CONFIG_CPU == PP5022) || (CONFIG_CPU == PP5024) | ||
378 | PLL_CONTROL = 0x8a220501; /* 30 MHz = (5/1 * 24MHz) / 4 */ | ||
379 | while (!(PLL_STATUS & 0x80000000)); /* wait for relock */ | ||
380 | #endif | ||
381 | scale_suspend_core(true); | ||
382 | DEV_TIMING1 = 0x00000303; | ||
383 | CLOCK_SOURCE = 0x20007777; /* source #1, #2, #3, #4: PLL (#2 active) */ | ||
384 | scale_suspend_core(false); | ||
385 | break; | ||
386 | #endif /******** CPUFREQ_NORMAL end ********/ | ||
387 | default: | ||
388 | cpu_frequency = CPUFREQ_DEFAULT; | ||
389 | PLL_CONTROL |= 0x08000000; | ||
390 | scale_suspend_core(true); | ||
391 | CLOCK_SOURCE = 0x20002222; /* source #1, #2, #3, #4: 24MHz (#2 active) */ | ||
392 | DEV_TIMING1 = 0x00000303; | ||
393 | #if defined(IPOD_MINI2G) | ||
394 | MLCD_SCLK_DIV = 0x00000000; /* Mono LCD bridge serial clock divider */ | ||
395 | #elif defined(IPOD_NANO) | ||
396 | IDE0_CFG &= ~0x10000000; /* clear ">65MHz" bit */ | ||
397 | #endif | ||
398 | scale_suspend_core(false); | ||
399 | PLL_CONTROL &= ~0x80000000; /* disable PLL */ | ||
400 | DEV_INIT2 &= ~INIT_PLL; /* disable PLL power */ | ||
401 | break; | ||
402 | } | ||
403 | |||
404 | #if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1) | ||
405 | corelock_unlock(&cpufreq_cl); | ||
406 | #endif | ||
407 | } | ||
408 | #endif /* !BOOTLOADER || (SANSA_E200 || SANSA_C200 || PHILIPS_SA9200) */ | ||
409 | |||
410 | #ifndef BOOTLOADER | ||
411 | void system_init(void) | ||
412 | { | ||
413 | if (CURRENT_CORE == CPU) | ||
414 | { | ||
415 | #if defined (IRIVER_H10) || defined(IRIVER_H10_5GB) || defined(IPOD_COLOR) | ||
416 | /* set minimum startup configuration */ | ||
417 | DEV_EN = 0xc2000124; | ||
418 | DEV_EN2 = 0x00002000; | ||
419 | CACHE_PRIORITY = 0x0000003f; | ||
420 | GPO32_VAL = 0x20000000; | ||
421 | DEV_INIT1 = 0xdc000000; | ||
422 | DEV_INIT2 = 0x40000000; | ||
423 | |||
424 | /* reset all allowed devices */ | ||
425 | DEV_RS = 0x3dfffef8; | ||
426 | DEV_RS2 = 0xffffdfff; | ||
427 | DEV_RS = 0x00000000; | ||
428 | DEV_RS2 = 0x00000000; | ||
429 | #elif defined (IPOD_VIDEO) | ||
430 | /* set minimum startup configuration */ | ||
431 | DEV_EN = 0xc2000124; | ||
432 | DEV_EN2 = 0x00000000; | ||
433 | CACHE_PRIORITY = 0x0000003f; | ||
434 | GPO32_VAL &= 0x00004000; | ||
435 | DEV_INIT1 = 0x00000000; | ||
436 | DEV_INIT2 = 0x40000000; | ||
437 | |||
438 | /* reset all allowed devices */ | ||
439 | DEV_RS = 0x3dfffef8; | ||
440 | DEV_RS2 = 0xffffffff; | ||
441 | DEV_RS = 0x00000000; | ||
442 | DEV_RS2 = 0x00000000; | ||
443 | #elif defined (IPOD_NANO) | ||
444 | /* set minimum startup configuration */ | ||
445 | DEV_EN = 0xc2000124; | ||
446 | DEV_EN2 = 0x00002000; | ||
447 | CACHE_PRIORITY = 0x0000003f; | ||
448 | GPO32_VAL = 0x50000000; | ||
449 | DEV_INIT1 = 0xa8000000; | ||
450 | DEV_INIT2 = 0x40000000; | ||
451 | |||
452 | /* reset all allowed devices */ | ||
453 | DEV_RS = 0x3ffffef8; | ||
454 | DEV_RS2 = 0xffffdfff; | ||
455 | DEV_RS = 0x00000000; | ||
456 | DEV_RS2 = 0x00000000; | ||
457 | #elif defined(SANSA_C200) || defined (SANSA_E200) | ||
458 | /* set minimum startup configuration */ | ||
459 | DEV_EN = 0xc4000124; | ||
460 | DEV_EN2 = 0x00000000; | ||
461 | CACHE_PRIORITY = 0x0000003f; | ||
462 | GPO32_VAL = 0x10000000; | ||
463 | DEV_INIT1 = 0x54000000; | ||
464 | DEV_INIT2 = 0x40000000; | ||
465 | |||
466 | /* reset all allowed devices */ | ||
467 | DEV_RS = 0x3bfffef8; | ||
468 | DEV_RS2 = 0xffffffff; | ||
469 | DEV_RS = 0x00000000; | ||
470 | DEV_RS2 = 0x00000000; | ||
471 | #elif defined(PHILIPS_SA9200) | ||
472 | /* reset all allowed devices */ | ||
473 | DEV_RS = 0x3ffffef8; | ||
474 | DEV_RS2 = 0xffffffff; | ||
475 | DEV_RS = 0x00000000; | ||
476 | DEV_RS2 = 0x00000000; | ||
477 | #elif defined(IPOD_4G) | ||
478 | /* set minimum startup configuration */ | ||
479 | DEV_EN = 0xc2020124; | ||
480 | DEV_EN2 = 0x00000000; | ||
481 | CACHE_PRIORITY = 0x0000003f; | ||
482 | GPO32_VAL = 0x02000000; | ||
483 | DEV_INIT1 = 0x00000000; | ||
484 | DEV_INIT2 = 0x40000000; | ||
485 | |||
486 | /* reset all allowed devices */ | ||
487 | DEV_RS = 0x3dfdfef8; | ||
488 | DEV_RS2 = 0xffffffff; | ||
489 | DEV_RS = 0x00000000; | ||
490 | DEV_RS2 = 0x00000000; | ||
491 | #elif defined (IPOD_MINI) | ||
492 | /* to be done */ | ||
493 | #elif defined (IPOD_MINI2G) | ||
494 | /* to be done */ | ||
495 | #elif defined (MROBE_100) | ||
496 | /* to be done */ | ||
497 | #elif defined (TATUNG_TPJ1022) | ||
498 | /* to be done */ | ||
499 | #elif defined(PBELL_VIBE500) | ||
500 | /* reset all allowed devices */ | ||
501 | DEV_RS = 0x3ffffef8; | ||
502 | DEV_RS2 = 0xffffffff; | ||
503 | DEV_RS = 0x00000000; | ||
504 | DEV_RS2 = 0x00000000; | ||
505 | #endif | ||
506 | |||
507 | #if !defined(SANSA_E200) && !defined(SANSA_C200) && !defined(PHILIPS_SA9200) | ||
508 | /* Remap the flash ROM on CPU, keep hidden from COP: | ||
509 | * 0x00000000-0x3fffffff = 0x20000000-0x23ffffff */ | ||
510 | MMAP1_LOGICAL = 0x20003c00; | ||
511 | MMAP1_PHYSICAL = 0x00003084 | | ||
512 | MMAP_PHYS_READ_MASK | MMAP_PHYS_WRITE_MASK | | ||
513 | MMAP_PHYS_DATA_MASK | MMAP_PHYS_CODE_MASK; | ||
514 | #endif | ||
515 | |||
516 | disable_all_interrupts(); | ||
517 | |||
518 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
519 | #if NUM_CORES > 1 | ||
520 | corelock_init(&cpufreq_cl); | ||
521 | cpu_boost_init(); | ||
522 | #endif | ||
523 | #else | ||
524 | pp_set_cpu_frequency(CPUFREQ_MAX); | ||
525 | #endif | ||
526 | |||
527 | #if defined(IPOD_VIDEO) | ||
528 | /* crt0-pp.S wrote the ram size to the last byte of the first 32MB | ||
529 | ram bank. See the comment there for how we determine it. */ | ||
530 | volatile unsigned char *end32 = (volatile unsigned char *)0x01ffffff; | ||
531 | probed_ramsize = *end32; | ||
532 | #endif | ||
533 | } | ||
534 | |||
535 | init_cache(); | ||
536 | } | ||
537 | |||
538 | #else /* BOOTLOADER */ | ||
539 | |||
540 | void system_init(void) | ||
541 | { | ||
542 | /* Only the CPU gets here in the bootloader */ | ||
543 | #ifdef HAVE_BOOTLOADER_USB_MODE | ||
544 | disable_all_interrupts(); | ||
545 | init_cache(); | ||
546 | /* Use the local vector map */ | ||
547 | CACHE_CTL |= CACHE_CTL_VECT_REMAP; | ||
548 | #endif /* HAVE_BOOTLOADER_USB_MODE */ | ||
549 | |||
550 | #if defined(SANSA_C200) || defined(SANSA_E200) || defined(PHILIPS_SA9200) | ||
551 | pp_set_cpu_frequency(CPUFREQ_MAX); | ||
552 | #endif | ||
553 | /* Else the frequency should get changed upon USB connect - | ||
554 | * decide per-target */ | ||
555 | } | ||
556 | |||
557 | #ifdef HAVE_BOOTLOADER_USB_MODE | ||
558 | void system_prepare_fw_start(void) | ||
559 | { | ||
560 | disable_interrupt(IRQ_FIQ_STATUS); | ||
561 | tick_stop(); | ||
562 | disable_all_interrupts(); | ||
563 | /* Some OF's disable this themselves, others do not and will hang. */ | ||
564 | CACHE_CTL &= ~CACHE_CTL_VECT_REMAP; | ||
565 | } | ||
566 | #endif /* HAVE_BOOTLOADER_USB_MODE */ | ||
567 | #endif /* !BOOTLOADER */ | ||
568 | |||
569 | void ICODE_ATTR system_reboot(void) | ||
570 | { | ||
571 | disable_interrupt(IRQ_FIQ_STATUS); | ||
572 | CPU_INT_DIS = -1; | ||
573 | COP_INT_DIS = -1; | ||
574 | |||
575 | /* Reboot */ | ||
576 | #if defined(SANSA_E200) || defined(SANSA_C200) || defined(PHILIPS_SA9200) | ||
577 | CACHE_CTL &= ~CACHE_CTL_VECT_REMAP; | ||
578 | |||
579 | /* Magic used by the c200 OF: 0x23066000 | ||
580 | Magic used by the c200 BL: 0x23066b7b | ||
581 | In both cases, the OF executes these 2 commands from iram. */ | ||
582 | STRAP_OPT_A = 0x23066b7b; | ||
583 | DEV_RS = DEV_SYSTEM; | ||
584 | #else | ||
585 | DEV_RS |= DEV_SYSTEM; | ||
586 | #endif | ||
587 | /* wait until reboot kicks in */ | ||
588 | while (1); | ||
589 | } | ||
590 | |||
591 | void system_exception_wait(void) | ||
592 | { | ||
593 | /* FIXME: we just need the right buttons */ | ||
594 | CPU_INT_DIS = -1; | ||
595 | COP_INT_DIS = -1; | ||
596 | |||
597 | /* Halt */ | ||
598 | PROC_CTL(CURRENT_CORE) = 0x40000000; | ||
599 | while (1); | ||
600 | } | ||
601 | |||
602 | int system_memory_guard(int newmode) | ||
603 | { | ||
604 | (void)newmode; | ||
605 | return 0; | ||
606 | } | ||
607 | |||
diff --git a/firmware/target/arm/pp/thread-pp.c b/firmware/target/arm/pp/thread-pp.c new file mode 100644 index 0000000000..0836b27204 --- /dev/null +++ b/firmware/target/arm/pp/thread-pp.c | |||
@@ -0,0 +1,554 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by Daniel Ankers | ||
11 | * | ||
12 | * PP5002 and PP502x SoC threading support | ||
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 | |||
24 | #if defined(MAX_PHYS_SECTOR_SIZE) && MEMORYSIZE == 64 | ||
25 | /* Support a special workaround object for large-sector disks */ | ||
26 | #define IF_NO_SKIP_YIELD(...) __VA_ARGS__ | ||
27 | #endif | ||
28 | |||
29 | #if NUM_CORES == 1 | ||
30 | /* Single-core variants for FORCE_SINGLE_CORE */ | ||
31 | static inline void core_sleep(void) | ||
32 | { | ||
33 | sleep_core(CURRENT_CORE); | ||
34 | enable_irq(); | ||
35 | } | ||
36 | |||
37 | /* Shared single-core build debugging version */ | ||
38 | void core_wake(void) | ||
39 | { | ||
40 | /* No wakey - core already wakey (because this is it) */ | ||
41 | } | ||
42 | #else /* NUM_CORES > 1 */ | ||
43 | /** Model-generic PP dual-core code **/ | ||
44 | extern uintptr_t cpu_idlestackbegin[]; | ||
45 | extern uintptr_t cpu_idlestackend[]; | ||
46 | extern uintptr_t cop_idlestackbegin[]; | ||
47 | extern uintptr_t cop_idlestackend[]; | ||
48 | static uintptr_t * const idle_stacks[NUM_CORES] = | ||
49 | { | ||
50 | [CPU] = cpu_idlestackbegin, | ||
51 | [COP] = cop_idlestackbegin | ||
52 | }; | ||
53 | |||
54 | /* Core locks using Peterson's mutual exclusion algorithm */ | ||
55 | |||
56 | /*--------------------------------------------------------------------------- | ||
57 | * Initialize the corelock structure. | ||
58 | *--------------------------------------------------------------------------- | ||
59 | */ | ||
60 | void corelock_init(struct corelock *cl) | ||
61 | { | ||
62 | memset(cl, 0, sizeof (*cl)); | ||
63 | } | ||
64 | |||
65 | #if 1 /* Assembly locks to minimize overhead */ | ||
66 | /*--------------------------------------------------------------------------- | ||
67 | * Wait for the corelock to become free and acquire it when it does. | ||
68 | *--------------------------------------------------------------------------- | ||
69 | */ | ||
70 | void __attribute__((naked)) corelock_lock(struct corelock *cl) | ||
71 | { | ||
72 | /* Relies on the fact that core IDs are complementary bitmasks (0x55,0xaa) */ | ||
73 | asm volatile ( | ||
74 | "mov r1, %0 \n" /* r1 = PROCESSOR_ID */ | ||
75 | "ldrb r1, [r1] \n" | ||
76 | "strb r1, [r0, r1, lsr #7] \n" /* cl->myl[core] = core */ | ||
77 | "eor r2, r1, #0xff \n" /* r2 = othercore */ | ||
78 | "strb r2, [r0, #2] \n" /* cl->turn = othercore */ | ||
79 | "1: \n" | ||
80 | "ldrb r3, [r0, r2, lsr #7] \n" /* cl->myl[othercore] == 0 ? */ | ||
81 | "cmp r3, #0 \n" /* yes? lock acquired */ | ||
82 | "bxeq lr \n" | ||
83 | "ldrb r3, [r0, #2] \n" /* || cl->turn == core ? */ | ||
84 | "cmp r3, r1 \n" | ||
85 | "bxeq lr \n" /* yes? lock acquired */ | ||
86 | "b 1b \n" /* keep trying */ | ||
87 | : : "i"(&PROCESSOR_ID) | ||
88 | ); | ||
89 | (void)cl; | ||
90 | } | ||
91 | |||
92 | /*--------------------------------------------------------------------------- | ||
93 | * Try to aquire the corelock. If free, caller gets it, otherwise return 0. | ||
94 | *--------------------------------------------------------------------------- | ||
95 | */ | ||
96 | int __attribute__((naked)) corelock_try_lock(struct corelock *cl) | ||
97 | { | ||
98 | /* Relies on the fact that core IDs are complementary bitmasks (0x55,0xaa) */ | ||
99 | asm volatile ( | ||
100 | "mov r1, %0 \n" /* r1 = PROCESSOR_ID */ | ||
101 | "ldrb r1, [r1] \n" | ||
102 | "mov r3, r0 \n" | ||
103 | "strb r1, [r0, r1, lsr #7] \n" /* cl->myl[core] = core */ | ||
104 | "eor r2, r1, #0xff \n" /* r2 = othercore */ | ||
105 | "strb r2, [r0, #2] \n" /* cl->turn = othercore */ | ||
106 | "ldrb r0, [r3, r2, lsr #7] \n" /* cl->myl[othercore] == 0 ? */ | ||
107 | "eors r0, r0, r2 \n" /* yes? lock acquired */ | ||
108 | "bxne lr \n" | ||
109 | "ldrb r0, [r3, #2] \n" /* || cl->turn == core? */ | ||
110 | "ands r0, r0, r1 \n" | ||
111 | "streqb r0, [r3, r1, lsr #7] \n" /* if not, cl->myl[core] = 0 */ | ||
112 | "bx lr \n" /* return result */ | ||
113 | : : "i"(&PROCESSOR_ID) | ||
114 | ); | ||
115 | |||
116 | return 0; | ||
117 | (void)cl; | ||
118 | } | ||
119 | |||
120 | /*--------------------------------------------------------------------------- | ||
121 | * Release ownership of the corelock | ||
122 | *--------------------------------------------------------------------------- | ||
123 | */ | ||
124 | void __attribute__((naked)) corelock_unlock(struct corelock *cl) | ||
125 | { | ||
126 | asm volatile ( | ||
127 | "mov r1, %0 \n" /* r1 = PROCESSOR_ID */ | ||
128 | "ldrb r1, [r1] \n" | ||
129 | "mov r2, #0 \n" /* cl->myl[core] = 0 */ | ||
130 | "strb r2, [r0, r1, lsr #7] \n" | ||
131 | "bx lr \n" | ||
132 | : : "i"(&PROCESSOR_ID) | ||
133 | ); | ||
134 | (void)cl; | ||
135 | } | ||
136 | |||
137 | #else /* C versions for reference */ | ||
138 | |||
139 | void corelock_lock(struct corelock *cl) | ||
140 | { | ||
141 | const unsigned int core = CURRENT_CORE; | ||
142 | const unsigned int othercore = 1 - core; | ||
143 | |||
144 | cl->myl[core] = core; | ||
145 | cl->turn = othercore; | ||
146 | |||
147 | for (;;) | ||
148 | { | ||
149 | if (cl->myl[othercore] == 0 || cl->turn == core) | ||
150 | break; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | int corelock_try_lock(struct corelock *cl) | ||
155 | { | ||
156 | const unsigned int core = CURRENT_CORE; | ||
157 | const unsigned int othercore = 1 - core; | ||
158 | |||
159 | cl->myl[core] = core; | ||
160 | cl->turn = othercore; | ||
161 | |||
162 | if (cl->myl[othercore] == 0 || cl->turn == core) | ||
163 | { | ||
164 | return 1; | ||
165 | } | ||
166 | |||
167 | cl->myl[core] = 0; | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | void corelock_unlock(struct corelock *cl) | ||
172 | { | ||
173 | cl->myl[CURRENT_CORE] = 0; | ||
174 | } | ||
175 | #endif /* ASM / C selection */ | ||
176 | |||
177 | /*--------------------------------------------------------------------------- | ||
178 | * Do any device-specific inits for the threads and synchronize the kernel | ||
179 | * initializations. | ||
180 | *--------------------------------------------------------------------------- | ||
181 | */ | ||
182 | static void INIT_ATTR core_thread_init(unsigned int core) | ||
183 | { | ||
184 | if (core == CPU) | ||
185 | { | ||
186 | /* Wake up coprocessor and let it initialize kernel and threads */ | ||
187 | #ifdef CPU_PP502x | ||
188 | MBX_MSG_CLR = 0x3f; | ||
189 | #endif | ||
190 | wake_core(COP); | ||
191 | /* Sleep until COP has finished */ | ||
192 | sleep_core(CPU); | ||
193 | } | ||
194 | else | ||
195 | { | ||
196 | /* Wake the CPU and return */ | ||
197 | wake_core(CPU); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | /*--------------------------------------------------------------------------- | ||
202 | * Switches to a stack that always resides in the Rockbox core then calls | ||
203 | * the final exit routine to actually finish removing the thread from the | ||
204 | * scheduler. | ||
205 | * | ||
206 | * Needed when a thread suicides on a core other than the main CPU since the | ||
207 | * stack used when idling is the stack of the last thread to run. This stack | ||
208 | * may not reside in the core firmware in which case the core will continue | ||
209 | * to use a stack from an unloaded module until another thread runs on it. | ||
210 | *--------------------------------------------------------------------------- | ||
211 | */ | ||
212 | static inline void NORETURN_ATTR __attribute__((always_inline)) | ||
213 | thread_final_exit(struct thread_entry *current) | ||
214 | { | ||
215 | asm volatile ( | ||
216 | "cmp %1, #0 \n" /* CPU? */ | ||
217 | "ldrne r0, =commit_dcache \n" /* No? write back data */ | ||
218 | "movne lr, pc \n" | ||
219 | "bxne r0 \n" | ||
220 | "mov r0, %0 \n" /* copy thread parameter */ | ||
221 | "mov sp, %2 \n" /* switch to idle stack */ | ||
222 | "bl thread_final_exit_do \n" /* finish removal */ | ||
223 | : : "r"(current), | ||
224 | "r"(current->core), | ||
225 | "r"(&idle_stacks[current->core][IDLE_STACK_WORDS]) | ||
226 | : "r0", "r1", "r2", "r3", "ip", "lr"); /* Because of flush call, | ||
227 | force inputs out | ||
228 | of scratch regs */ | ||
229 | while (1); | ||
230 | } | ||
231 | |||
232 | /*--------------------------------------------------------------------------- | ||
233 | * Perform core switch steps that need to take place inside switch_thread. | ||
234 | * | ||
235 | * These steps must take place while before changing the processor and after | ||
236 | * having entered switch_thread since switch_thread may not do a normal return | ||
237 | * because the stack being used for anything the compiler saved will not belong | ||
238 | * to the thread's destination core and it may have been recycled for other | ||
239 | * purposes by the time a normal context load has taken place. switch_thread | ||
240 | * will also clobber anything stashed in the thread's context or stored in the | ||
241 | * nonvolatile registers if it is saved there before the call since the | ||
242 | * compiler's order of operations cannot be known for certain. | ||
243 | */ | ||
244 | static void core_switch_blk_op(unsigned int core, struct thread_entry *thread) | ||
245 | { | ||
246 | /* Flush our data to ram */ | ||
247 | commit_dcache(); | ||
248 | /* Stash thread in r4 slot */ | ||
249 | thread->context.r[0] = (uint32_t)thread; | ||
250 | /* Stash restart address in r5 slot */ | ||
251 | thread->context.r[1] = thread->context.start; | ||
252 | /* Save sp in context.sp while still running on old core */ | ||
253 | thread->context.sp = idle_stacks[core][IDLE_STACK_WORDS-1]; | ||
254 | } | ||
255 | |||
256 | /*--------------------------------------------------------------------------- | ||
257 | * Machine-specific helper function for switching the processor a thread is | ||
258 | * running on. Basically, the thread suicides on the departing core and is | ||
259 | * reborn on the destination. Were it not for gcc's ill-behavior regarding | ||
260 | * naked functions written in C where it actually clobbers non-volatile | ||
261 | * registers before the intended prologue code, this would all be much | ||
262 | * simpler. Generic setup is done in switch_core itself. | ||
263 | */ | ||
264 | |||
265 | /*--------------------------------------------------------------------------- | ||
266 | * This actually performs the core switch. | ||
267 | */ | ||
268 | static void __attribute__((naked)) | ||
269 | switch_thread_core(unsigned int core, struct thread_entry *thread) | ||
270 | { | ||
271 | /* Pure asm for this because compiler behavior isn't sufficiently predictable. | ||
272 | * Stack access also isn't permitted until restoring the original stack and | ||
273 | * context. */ | ||
274 | asm volatile ( | ||
275 | "stmfd sp!, { r4-r11, lr } \n" /* Stack all non-volatile context on current core */ | ||
276 | "ldr r2, =idle_stacks \n" /* r2 = &idle_stacks[core][IDLE_STACK_WORDS] */ | ||
277 | "ldr r2, [r2, r0, lsl #2] \n" | ||
278 | "add r2, r2, %0*4 \n" | ||
279 | "stmfd r2!, { sp } \n" /* save original stack pointer on idle stack */ | ||
280 | "mov sp, r2 \n" /* switch stacks */ | ||
281 | "adr r2, 1f \n" /* r2 = new core restart address */ | ||
282 | "str r2, [r1, #40] \n" /* thread->context.start = r2 */ | ||
283 | "ldr pc, =switch_thread \n" /* r0 = thread after call - see load_context */ | ||
284 | "1: \n" | ||
285 | "ldr sp, [r0, #32] \n" /* Reload original sp from context structure */ | ||
286 | "mov r1, #0 \n" /* Clear start address */ | ||
287 | "str r1, [r0, #40] \n" | ||
288 | "ldr r0, =commit_discard_idcache \n" /* Invalidate new core's cache */ | ||
289 | "mov lr, pc \n" | ||
290 | "bx r0 \n" | ||
291 | "ldmfd sp!, { r4-r11, pc } \n" /* Restore non-volatile context to new core and return */ | ||
292 | : : "i"(IDLE_STACK_WORDS) | ||
293 | ); | ||
294 | (void)core; (void)thread; | ||
295 | } | ||
296 | |||
297 | /** PP-model-specific dual-core code **/ | ||
298 | |||
299 | #if CONFIG_CPU == PP5002 | ||
300 | /* PP5002 has no mailboxes - Bytes to emulate the PP502x mailbox bits */ | ||
301 | struct core_semaphores | ||
302 | { | ||
303 | volatile uint8_t intend_wake; /* 00h */ | ||
304 | volatile uint8_t stay_awake; /* 01h */ | ||
305 | volatile uint8_t intend_sleep; /* 02h */ | ||
306 | volatile uint8_t unused; /* 03h */ | ||
307 | }; | ||
308 | |||
309 | static struct core_semaphores core_semaphores[NUM_CORES] IBSS_ATTR; | ||
310 | |||
311 | #if 1 /* Select ASM */ | ||
312 | /*--------------------------------------------------------------------------- | ||
313 | * Put core in a power-saving state if waking list wasn't repopulated and if | ||
314 | * no other core requested a wakeup for it to perform a task. | ||
315 | *--------------------------------------------------------------------------- | ||
316 | */ | ||
317 | static inline void core_sleep(unsigned int core) | ||
318 | { | ||
319 | asm volatile ( | ||
320 | "mov r0, #1 \n" /* Signal intent to sleep */ | ||
321 | "strb r0, [%[sem], #2] \n" | ||
322 | "ldrb r0, [%[sem], #1] \n" /* && stay_awake == 0? */ | ||
323 | "cmp r0, #0 \n" | ||
324 | "bne 2f \n" | ||
325 | /* Sleep: PP5002 crashes if the instruction that puts it to sleep is | ||
326 | * located at 0xNNNNNNN0. 4/8/C works. This sequence makes sure | ||
327 | * that the correct alternative is executed. Don't change the order | ||
328 | * of the next 4 instructions! */ | ||
329 | "tst pc, #0x0c \n" | ||
330 | "mov r0, #0xca \n" | ||
331 | "strne r0, [%[ctl], %[c], lsl #2] \n" | ||
332 | "streq r0, [%[ctl], %[c], lsl #2] \n" | ||
333 | "nop \n" /* nop's needed because of pipeline */ | ||
334 | "nop \n" | ||
335 | "nop \n" | ||
336 | "2: \n" | ||
337 | "mov r0, #0 \n" /* Clear stay_awake and sleep intent */ | ||
338 | "strb r0, [%[sem], #1] \n" | ||
339 | "strb r0, [%[sem], #2] \n" | ||
340 | "1: \n" /* Wait for wake procedure to finish */ | ||
341 | "ldrb r0, [%[sem], #0] \n" | ||
342 | "cmp r0, #0 \n" | ||
343 | "bne 1b \n" | ||
344 | : | ||
345 | : [sem]"r"(&core_semaphores[core]), [c]"r"(core), | ||
346 | [ctl]"r"(&CPU_CTL) | ||
347 | : "r0" | ||
348 | ); | ||
349 | enable_irq(); | ||
350 | } | ||
351 | |||
352 | /*--------------------------------------------------------------------------- | ||
353 | * Wake another processor core that is sleeping or prevent it from doing so | ||
354 | * if it was already destined. FIQ, IRQ should be disabled before calling. | ||
355 | *--------------------------------------------------------------------------- | ||
356 | */ | ||
357 | void core_wake(unsigned int othercore) | ||
358 | { | ||
359 | /* avoid r0 since that contains othercore */ | ||
360 | asm volatile ( | ||
361 | "mrs r3, cpsr \n" /* Disable IRQ */ | ||
362 | "orr r1, r3, #0x80 \n" | ||
363 | "msr cpsr_c, r1 \n" | ||
364 | "mov r1, #1 \n" /* Signal intent to wake other core */ | ||
365 | "orr r1, r1, r1, lsl #8 \n" /* and set stay_awake */ | ||
366 | "strh r1, [%[sem], #0] \n" | ||
367 | "mov r2, #0x8000 \n" | ||
368 | "1: \n" /* If it intends to sleep, let it first */ | ||
369 | "ldrb r1, [%[sem], #2] \n" /* intend_sleep != 0 ? */ | ||
370 | "cmp r1, #1 \n" | ||
371 | "ldr r1, [%[st]] \n" /* && not sleeping ? */ | ||
372 | "tsteq r1, r2, lsr %[oc] \n" | ||
373 | "beq 1b \n" /* Wait for sleep or wake */ | ||
374 | "tst r1, r2, lsr %[oc] \n" | ||
375 | "ldrne r2, =0xcf004054 \n" /* If sleeping, wake it */ | ||
376 | "movne r1, #0xce \n" | ||
377 | "strne r1, [r2, %[oc], lsl #2] \n" | ||
378 | "mov r1, #0 \n" /* Done with wake procedure */ | ||
379 | "strb r1, [%[sem], #0] \n" | ||
380 | "msr cpsr_c, r3 \n" /* Restore IRQ */ | ||
381 | : | ||
382 | : [sem]"r"(&core_semaphores[othercore]), | ||
383 | [st]"r"(&PROC_STAT), | ||
384 | [oc]"r"(othercore) | ||
385 | : "r1", "r2", "r3" | ||
386 | ); | ||
387 | } | ||
388 | |||
389 | #else /* C version for reference */ | ||
390 | |||
391 | static inline void core_sleep(unsigned int core) | ||
392 | { | ||
393 | /* Signal intent to sleep */ | ||
394 | core_semaphores[core].intend_sleep = 1; | ||
395 | |||
396 | /* Something waking or other processor intends to wake us? */ | ||
397 | if (core_semaphores[core].stay_awake == 0) | ||
398 | { | ||
399 | sleep_core(core); | ||
400 | } | ||
401 | |||
402 | /* Signal wake - clear wake flag */ | ||
403 | core_semaphores[core].stay_awake = 0; | ||
404 | core_semaphores[core].intend_sleep = 0; | ||
405 | |||
406 | /* Wait for other processor to finish wake procedure */ | ||
407 | while (core_semaphores[core].intend_wake != 0); | ||
408 | |||
409 | /* Enable IRQ */ | ||
410 | enable_irq(); | ||
411 | } | ||
412 | |||
413 | void core_wake(unsigned int othercore) | ||
414 | { | ||
415 | /* Disable interrupts - avoid reentrancy from the tick */ | ||
416 | int oldlevel = disable_irq_save(); | ||
417 | |||
418 | /* Signal intent to wake other processor - set stay awake */ | ||
419 | core_semaphores[othercore].intend_wake = 1; | ||
420 | core_semaphores[othercore].stay_awake = 1; | ||
421 | |||
422 | /* If it intends to sleep, wait until it does or aborts */ | ||
423 | while (core_semaphores[othercore].intend_sleep != 0 && | ||
424 | (PROC_STAT & PROC_SLEEPING(othercore)) == 0); | ||
425 | |||
426 | /* If sleeping, wake it up */ | ||
427 | if (PROC_STAT & PROC_SLEEPING(othercore)) | ||
428 | wake_core(othercore); | ||
429 | |||
430 | /* Done with wake procedure */ | ||
431 | core_semaphores[othercore].intend_wake = 0; | ||
432 | restore_irq(oldlevel); | ||
433 | } | ||
434 | #endif /* ASM/C selection */ | ||
435 | |||
436 | #elif defined (CPU_PP502x) | ||
437 | |||
438 | #if 1 /* Select ASM */ | ||
439 | /*--------------------------------------------------------------------------- | ||
440 | * Put core in a power-saving state if waking list wasn't repopulated and if | ||
441 | * no other core requested a wakeup for it to perform a task. | ||
442 | *--------------------------------------------------------------------------- | ||
443 | */ | ||
444 | static inline void core_sleep(unsigned int core) | ||
445 | { | ||
446 | asm volatile ( | ||
447 | "mov r0, #4 \n" /* r0 = 0x4 << core */ | ||
448 | "mov r0, r0, lsl %[c] \n" | ||
449 | "str r0, [%[mbx], #4] \n" /* signal intent to sleep */ | ||
450 | "ldr r1, [%[mbx], #0] \n" /* && !(MBX_MSG_STAT & (0x10<<core)) ? */ | ||
451 | "tst r1, r0, lsl #2 \n" | ||
452 | "moveq r1, #0x80000000 \n" /* Then sleep */ | ||
453 | "streq r1, [%[ctl], %[c], lsl #2] \n" | ||
454 | "moveq r1, #0 \n" /* Clear control reg */ | ||
455 | "streq r1, [%[ctl], %[c], lsl #2] \n" | ||
456 | "orr r1, r0, r0, lsl #2 \n" /* Signal intent to wake - clear wake flag */ | ||
457 | "str r1, [%[mbx], #8] \n" | ||
458 | "1: \n" /* Wait for wake procedure to finish */ | ||
459 | "ldr r1, [%[mbx], #0] \n" | ||
460 | "tst r1, r0, lsr #2 \n" | ||
461 | "bne 1b \n" | ||
462 | : | ||
463 | : [ctl]"r"(&CPU_CTL), [mbx]"r"(MBX_BASE), [c]"r"(core) | ||
464 | : "r0", "r1"); | ||
465 | enable_irq(); | ||
466 | } | ||
467 | |||
468 | /*--------------------------------------------------------------------------- | ||
469 | * Wake another processor core that is sleeping or prevent it from doing so | ||
470 | * if it was already destined. FIQ, IRQ should be disabled before calling. | ||
471 | *--------------------------------------------------------------------------- | ||
472 | */ | ||
473 | void core_wake(unsigned int othercore) | ||
474 | { | ||
475 | /* avoid r0 since that contains othercore */ | ||
476 | asm volatile ( | ||
477 | "mrs r3, cpsr \n" /* Disable IRQ */ | ||
478 | "orr r1, r3, #0x80 \n" | ||
479 | "msr cpsr_c, r1 \n" | ||
480 | "mov r2, #0x11 \n" /* r2 = (0x11 << othercore) */ | ||
481 | "mov r2, r2, lsl %[oc] \n" /* Signal intent to wake othercore */ | ||
482 | "str r2, [%[mbx], #4] \n" | ||
483 | "1: \n" /* If it intends to sleep, let it first */ | ||
484 | "ldr r1, [%[mbx], #0] \n" /* (MSG_MSG_STAT & (0x4 << othercore)) != 0 ? */ | ||
485 | "eor r1, r1, #0xc \n" | ||
486 | "tst r1, r2, lsr #2 \n" | ||
487 | "ldr r1, [%[ctl], %[oc], lsl #2] \n" /* && (PROC_CTL(othercore) & PROC_SLEEP) == 0 ? */ | ||
488 | "tsteq r1, #0x80000000 \n" | ||
489 | "beq 1b \n" /* Wait for sleep or wake */ | ||
490 | "tst r1, #0x80000000 \n" /* If sleeping, wake it */ | ||
491 | "movne r1, #0x0 \n" | ||
492 | "strne r1, [%[ctl], %[oc], lsl #2] \n" | ||
493 | "mov r1, r2, lsr #4 \n" | ||
494 | "str r1, [%[mbx], #8] \n" /* Done with wake procedure */ | ||
495 | "msr cpsr_c, r3 \n" /* Restore IRQ */ | ||
496 | : | ||
497 | : [ctl]"r"(&PROC_CTL(CPU)), [mbx]"r"(MBX_BASE), | ||
498 | [oc]"r"(othercore) | ||
499 | : "r1", "r2", "r3"); | ||
500 | } | ||
501 | |||
502 | #else /* C version for reference */ | ||
503 | |||
504 | static inline void core_sleep(unsigned int core) | ||
505 | { | ||
506 | /* Signal intent to sleep */ | ||
507 | MBX_MSG_SET = 0x4 << core; | ||
508 | |||
509 | /* Something waking or other processor intends to wake us? */ | ||
510 | if ((MBX_MSG_STAT & (0x10 << core)) == 0) | ||
511 | { | ||
512 | sleep_core(core); | ||
513 | wake_core(core); | ||
514 | } | ||
515 | |||
516 | /* Signal wake - clear wake flag */ | ||
517 | MBX_MSG_CLR = 0x14 << core; | ||
518 | |||
519 | /* Wait for other processor to finish wake procedure */ | ||
520 | while (MBX_MSG_STAT & (0x1 << core)); | ||
521 | enable_irq(); | ||
522 | } | ||
523 | |||
524 | void core_wake(unsigned int othercore) | ||
525 | { | ||
526 | /* Disable interrupts - avoid reentrancy from the tick */ | ||
527 | int oldlevel = disable_irq_save(); | ||
528 | |||
529 | /* Signal intent to wake other processor - set stay awake */ | ||
530 | MBX_MSG_SET = 0x11 << othercore; | ||
531 | |||
532 | /* If it intends to sleep, wait until it does or aborts */ | ||
533 | while ((MBX_MSG_STAT & (0x4 << othercore)) != 0 && | ||
534 | (PROC_CTL(othercore) & PROC_SLEEP) == 0); | ||
535 | |||
536 | /* If sleeping, wake it up */ | ||
537 | if (PROC_CTL(othercore) & PROC_SLEEP) | ||
538 | PROC_CTL(othercore) = 0; | ||
539 | |||
540 | /* Done with wake procedure */ | ||
541 | MBX_MSG_CLR = 0x1 << othercore; | ||
542 | restore_irq(oldlevel); | ||
543 | } | ||
544 | #endif /* ASM/C selection */ | ||
545 | |||
546 | #endif /* CPU_PPxxxx */ | ||
547 | |||
548 | /* Keep constant pool in range of inline ASM */ | ||
549 | static void __attribute__((naked)) USED_ATTR dump_ltorg(void) | ||
550 | { | ||
551 | asm volatile (".ltorg"); | ||
552 | } | ||
553 | |||
554 | #endif /* NUM_CORES */ | ||
diff --git a/firmware/target/arm/pp/timer-pp.c b/firmware/target/arm/pp/timer-pp.c new file mode 100644 index 0000000000..db859f6b88 --- /dev/null +++ b/firmware/target/arm/pp/timer-pp.c | |||
@@ -0,0 +1,86 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Thom Johansen | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "cpu.h" | ||
23 | #include "system.h" | ||
24 | #include "timer.h" | ||
25 | |||
26 | static long SHAREDBSS_ATTR cycles_new = 0; | ||
27 | |||
28 | void TIMER2(void) | ||
29 | { | ||
30 | TIMER2_VAL; /* ACK interrupt */ | ||
31 | if (cycles_new > 0) | ||
32 | { | ||
33 | TIMER2_CFG = 0xc0000000 | (cycles_new - 1); | ||
34 | cycles_new = 0; | ||
35 | } | ||
36 | if (pfn_timer != NULL) | ||
37 | { | ||
38 | cycles_new = -1; | ||
39 | /* "lock" the variable, in case timer_set_period() | ||
40 | * is called within pfn_timer() */ | ||
41 | pfn_timer(); | ||
42 | cycles_new = 0; | ||
43 | } | ||
44 | } | ||
45 | |||
46 | bool timer_set(long cycles, bool start) | ||
47 | { | ||
48 | if (cycles > 0x20000000 || cycles < 2) | ||
49 | return false; | ||
50 | |||
51 | if (start) | ||
52 | { | ||
53 | if (pfn_unregister != NULL) | ||
54 | { | ||
55 | pfn_unregister(); | ||
56 | pfn_unregister = NULL; | ||
57 | } | ||
58 | CPU_INT_DIS = TIMER2_MASK; | ||
59 | COP_INT_DIS = TIMER2_MASK; | ||
60 | } | ||
61 | if (start || (cycles_new == -1)) /* within isr, cycles_new is "locked" */ | ||
62 | TIMER2_CFG = 0xc0000000 | (cycles - 1); /* enable timer */ | ||
63 | else | ||
64 | cycles_new = cycles; | ||
65 | |||
66 | return true; | ||
67 | } | ||
68 | |||
69 | bool timer_start(IF_COP_VOID(int core)) | ||
70 | { | ||
71 | /* unmask interrupt source */ | ||
72 | #if NUM_CORES > 1 | ||
73 | if (core == COP) | ||
74 | COP_INT_EN = TIMER2_MASK; | ||
75 | else | ||
76 | #endif | ||
77 | CPU_INT_EN = TIMER2_MASK; | ||
78 | return true; | ||
79 | } | ||
80 | |||
81 | void timer_stop(void) | ||
82 | { | ||
83 | TIMER2_CFG = 0; /* stop timer 2 */ | ||
84 | CPU_INT_DIS = TIMER2_MASK; | ||
85 | COP_INT_DIS = TIMER2_MASK; | ||
86 | } | ||
diff --git a/firmware/target/arm/pp/usb-fw-pp5002.c b/firmware/target/arm/pp/usb-fw-pp5002.c new file mode 100644 index 0000000000..d296b05b2f --- /dev/null +++ b/firmware/target/arm/pp/usb-fw-pp5002.c | |||
@@ -0,0 +1,75 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by Jens Arnold | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "config.h" | ||
23 | #include "ata.h" | ||
24 | #include "cpu.h" | ||
25 | #include "string.h" | ||
26 | #include "system.h" | ||
27 | #include "usb.h" | ||
28 | |||
29 | void usb_pin_init(void) | ||
30 | { | ||
31 | /* TODO: add USB init for iPod 3rd gen */ | ||
32 | |||
33 | #if defined(IPOD_1G2G) || defined(IPOD_3G) | ||
34 | /* GPIO C bit 7 is firewire detect */ | ||
35 | GPIOC_ENABLE |= 0x80; | ||
36 | GPIOC_OUTPUT_EN &= ~0x80; | ||
37 | #endif | ||
38 | } | ||
39 | |||
40 | /* No different for now */ | ||
41 | void usb_init_device(void) __attribute__((alias("usb_pin_init"))); | ||
42 | |||
43 | void usb_enable(bool on) | ||
44 | { | ||
45 | /* This device specific code will eventually give way to proper USB | ||
46 | handling, which should be the same for all PP5002 targets. */ | ||
47 | if (on) | ||
48 | { | ||
49 | #ifdef IPOD_ARCH | ||
50 | /* For iPod, we can only do one thing with USB mode atm - reboot | ||
51 | into the flash-based disk-mode. This does not return. */ | ||
52 | |||
53 | ata_sleepnow(); /* Immediately spindown the disk. */ | ||
54 | sleep(HZ*2); | ||
55 | |||
56 | memcpy((void *)0x40017f00, "diskmodehotstuff\1", 17); | ||
57 | |||
58 | system_reboot(); /* Reboot */ | ||
59 | #endif | ||
60 | } | ||
61 | } | ||
62 | |||
63 | int usb_detect(void) | ||
64 | { | ||
65 | #if defined(IPOD_1G2G) || defined(IPOD_3G) | ||
66 | /* GPIO C bit 7 is firewire detect */ | ||
67 | if (!(GPIOC_INPUT_VAL & 0x80)) | ||
68 | return USB_INSERTED; | ||
69 | #endif | ||
70 | |||
71 | /* TODO: add USB detection for iPod 3rd gen */ | ||
72 | |||
73 | return USB_EXTRACTED; | ||
74 | } | ||
75 | bool usb_plugged(void) __attribute__((alias("usb_detect"))); | ||
diff --git a/firmware/target/arm/pp/usb-fw-pp502x.c b/firmware/target/arm/pp/usb-fw-pp502x.c new file mode 100644 index 0000000000..5272102fad --- /dev/null +++ b/firmware/target/arm/pp/usb-fw-pp502x.c | |||
@@ -0,0 +1,308 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Linus Nielsen Feltzing | ||
11 | * | ||
12 | * iPod driver based on code from the ipodlinux project - http://ipodlinux.org | ||
13 | * Adapted for Rockbox in January 2006 | ||
14 | * Original file: podzilla/usb.c | ||
15 | * Copyright (C) 2005 Adam Johnston | ||
16 | * | ||
17 | * This program is free software; you can redistribute it and/or | ||
18 | * modify it under the terms of the GNU General Public License | ||
19 | * as published by the Free Software Foundation; either version 2 | ||
20 | * of the License, or (at your option) any later version. | ||
21 | * | ||
22 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
23 | * KIND, either express or implied. | ||
24 | * | ||
25 | ****************************************************************************/ | ||
26 | #include "config.h" | ||
27 | #include "system.h" | ||
28 | #include "usb.h" | ||
29 | #include "button.h" | ||
30 | #include "ata.h" | ||
31 | #include "string.h" | ||
32 | #include "usb_core.h" | ||
33 | #include "usb_drv.h" | ||
34 | |||
35 | #if defined(IPOD_4G) || defined(IPOD_COLOR) \ | ||
36 | || defined(IPOD_MINI) || defined(IPOD_MINI2G) | ||
37 | /* GPIO D bit 3 is usb detect */ | ||
38 | #define USB_GPIO GPIOD | ||
39 | #define USB_GPIO_MASK 0x08 | ||
40 | #define USB_GPIO_VAL 0x08 | ||
41 | |||
42 | #elif defined(IPOD_NANO) || defined(IPOD_VIDEO) | ||
43 | /* GPIO L bit 4 is usb detect */ | ||
44 | #define USB_GPIO GPIOL | ||
45 | #define USB_GPIO_MASK 0x10 | ||
46 | #define USB_GPIO_VAL 0x10 | ||
47 | |||
48 | #elif defined(SANSA_C200) | ||
49 | /* GPIO H bit 1 is usb/charger detect */ | ||
50 | #define USB_GPIO GPIOH | ||
51 | #define USB_GPIO_MASK 0x02 | ||
52 | #define USB_GPIO_VAL 0x02 | ||
53 | |||
54 | #elif defined(SANSA_E200) | ||
55 | /* GPIO B bit 4 is usb/charger detect */ | ||
56 | #define USB_GPIO GPIOB | ||
57 | #define USB_GPIO_MASK 0x10 | ||
58 | #define USB_GPIO_VAL 0x10 | ||
59 | |||
60 | #elif defined(IRIVER_H10) || defined(IRIVER_H10_5GB) || defined(MROBE_100) | ||
61 | /* GPIO L bit 2 is usb detect */ | ||
62 | #define USB_GPIO GPIOL | ||
63 | #define USB_GPIO_MASK 0x04 | ||
64 | #define USB_GPIO_VAL 0x04 | ||
65 | |||
66 | #elif defined(PHILIPS_SA9200) | ||
67 | /* GPIO B bit 6 (high) is usb bus power detect */ | ||
68 | #define USB_GPIO GPIOB | ||
69 | #define USB_GPIO_MASK 0x40 | ||
70 | #define USB_GPIO_VAL 0x40 | ||
71 | |||
72 | #elif defined(PHILIPS_HDD1630) || defined(PHILIPS_HDD6330) | ||
73 | /* GPIO E bit 2 is usb detect */ | ||
74 | #define USB_GPIO GPIOE | ||
75 | #define USB_GPIO_MASK 0x04 | ||
76 | #define USB_GPIO_VAL 0x04 | ||
77 | |||
78 | #elif defined(SAMSUNG_YH820) || defined(SAMSUNG_YH920) || defined(SAMSUNG_YH925) | ||
79 | /* GPIO D bit 4 is usb detect */ | ||
80 | #define USB_GPIO GPIOD | ||
81 | #define USB_GPIO_MASK 0x10 | ||
82 | #define USB_GPIO_VAL 0x10 | ||
83 | |||
84 | #elif defined(TATUNG_TPJ1022) | ||
85 | /* GPIO ? bit ? is usb detect (dummy value)*/ | ||
86 | #define USB_GPIO GPIOD | ||
87 | #define USB_GPIO_MASK 0x10 | ||
88 | #define USB_GPIO_VAL 0x10 | ||
89 | |||
90 | #elif defined(PBELL_VIBE500) | ||
91 | /* GPIO L bit 3 is usb detect */ | ||
92 | #define USB_GPIO GPIOL | ||
93 | #define USB_GPIO_MASK 0x04 | ||
94 | #define USB_GPIO_VAL 0x04 | ||
95 | |||
96 | #else | ||
97 | #error No USB GPIO config specified | ||
98 | #endif | ||
99 | |||
100 | #define USB_GPIO_ENABLE GPIO_ENABLE(USB_GPIO) | ||
101 | #define USB_GPIO_OUTPUT_EN GPIO_OUTPUT_EN(USB_GPIO) | ||
102 | #define USB_GPIO_INPUT_VAL GPIO_INPUT_VAL(USB_GPIO) | ||
103 | #define USB_GPIO_INT_EN GPIO_INT_EN(USB_GPIO) | ||
104 | #define USB_GPIO_INT_LEV GPIO_INT_LEV(USB_GPIO) | ||
105 | #define USB_GPIO_INT_CLR GPIO_INT_CLR(USB_GPIO) | ||
106 | #define USB_GPIO_HI_INT_MASK GPIO_HI_INT_MASK(USB_GPIO) | ||
107 | |||
108 | static void usb_reset_controller(void) | ||
109 | { | ||
110 | /* enable usb module */ | ||
111 | outl(inl(0x7000002C) | 0x3000000, 0x7000002C); | ||
112 | |||
113 | DEV_EN |= DEV_USB0; | ||
114 | DEV_EN |= DEV_USB1; | ||
115 | |||
116 | /* reset both USBs */ | ||
117 | DEV_RS |= DEV_USB0; | ||
118 | DEV_RS &=~DEV_USB0; | ||
119 | DEV_RS |= DEV_USB1; | ||
120 | DEV_RS &=~DEV_USB1; | ||
121 | |||
122 | DEV_INIT2 |= INIT_USB; | ||
123 | |||
124 | while ((inl(0x70000028) & 0x80) == 0); | ||
125 | outl(inl(0x70000028) | 0x2, 0x70000028); | ||
126 | udelay(100000); | ||
127 | XMB_RAM_CFG |= 0x47A; | ||
128 | |||
129 | /* disable USB-devices until USB is detected via GPIO */ | ||
130 | #ifndef BOOTLOADER | ||
131 | /* Disabling USB0 in the bootloader makes the OF not load, | ||
132 | Also something here breaks usb pin detect in bootloader. | ||
133 | leave it all enabled untill rockbox main loads */ | ||
134 | DEV_EN &= ~DEV_USB0; | ||
135 | DEV_EN &= ~DEV_USB1; | ||
136 | DEV_INIT2 &= ~INIT_USB; | ||
137 | #endif | ||
138 | } | ||
139 | |||
140 | /* Enable raw status pin read only - not interrupt */ | ||
141 | void usb_pin_init(void) | ||
142 | { | ||
143 | GPIO_CLEAR_BITWISE(USB_GPIO_OUTPUT_EN, USB_GPIO_MASK); | ||
144 | GPIO_SET_BITWISE(USB_GPIO_ENABLE, USB_GPIO_MASK); | ||
145 | #ifdef USB_FIREWIRE_HANDLING | ||
146 | /* GPIO C bit 1 is firewire detect */ | ||
147 | GPIO_CLEAR_BITWISE(GPIOC_OUTPUT_EN, 0x02); | ||
148 | GPIO_SET_BITWISE(GPIOC_ENABLE, 0x02); | ||
149 | #endif | ||
150 | } | ||
151 | |||
152 | void usb_init_device(void) | ||
153 | { | ||
154 | usb_reset_controller(); | ||
155 | |||
156 | /* Do one-time inits (no dependency on controller) */ | ||
157 | usb_drv_startup(); | ||
158 | |||
159 | usb_pin_init(); | ||
160 | |||
161 | /* These set INT_LEV to the inserted level so it will fire if already | ||
162 | * inserted at the time they are enabled. */ | ||
163 | #ifdef USB_STATUS_BY_EVENT | ||
164 | GPIO_CLEAR_BITWISE(USB_GPIO_INT_EN, USB_GPIO_MASK); | ||
165 | GPIO_WRITE_BITWISE(USB_GPIO_INT_LEV, USB_GPIO_VAL, USB_GPIO_MASK); | ||
166 | USB_GPIO_INT_CLR = USB_GPIO_MASK; | ||
167 | GPIO_SET_BITWISE(USB_GPIO_INT_EN, USB_GPIO_MASK); | ||
168 | CPU_HI_INT_EN = USB_GPIO_HI_INT_MASK; | ||
169 | |||
170 | #ifdef USB_FIREWIRE_HANDLING | ||
171 | /* GPIO C bit 1 is firewire detect */ | ||
172 | GPIO_CLEAR_BITWISE(GPIOC_INT_EN, 0x02); | ||
173 | GPIO_WRITE_BITWISE(GPIOC_INT_LEV, 0x00, 0x02); | ||
174 | GPIOC_INT_CLR = 0x02; | ||
175 | GPIO_SET_BITWISE(GPIOC_INT_EN, 0x02); | ||
176 | CPU_HI_INT_EN = GPIO0_MASK; | ||
177 | #endif | ||
178 | CPU_INT_EN = HI_MASK; | ||
179 | #endif /* USB_STATUS_BY_EVENT */ | ||
180 | } | ||
181 | |||
182 | void usb_enable(bool on) | ||
183 | { | ||
184 | if (on) { | ||
185 | /* if USB is detected, re-enable the USB-devices, otherwise make sure it's disabled */ | ||
186 | DEV_EN |= DEV_USB0; | ||
187 | DEV_EN |= DEV_USB1; | ||
188 | DEV_INIT2 |= INIT_USB; | ||
189 | usb_core_init(); | ||
190 | } | ||
191 | else { | ||
192 | usb_core_exit(); | ||
193 | /* Disable USB devices */ | ||
194 | usb_reset_controller(); | ||
195 | } | ||
196 | } | ||
197 | |||
198 | void usb_attach(void) | ||
199 | { | ||
200 | usb_drv_attach(); | ||
201 | } | ||
202 | |||
203 | bool usb_plugged(void) | ||
204 | { | ||
205 | return (USB_GPIO_INPUT_VAL & USB_GPIO_MASK) == USB_GPIO_VAL; | ||
206 | } | ||
207 | |||
208 | #ifdef USB_STATUS_BY_EVENT | ||
209 | /* Cannot always tell power pin from USB pin */ | ||
210 | static int usb_status = USB_EXTRACTED; | ||
211 | |||
212 | static int usb_timeout_event(struct timeout *tmo) | ||
213 | { | ||
214 | usb_status_event(tmo->data == USB_GPIO_VAL ? USB_INSERTED : USB_EXTRACTED); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | void usb_insert_int(void) | ||
219 | { | ||
220 | static struct timeout usb_oneshot; | ||
221 | unsigned long val = USB_GPIO_INPUT_VAL & USB_GPIO_MASK; | ||
222 | usb_status = (val == USB_GPIO_VAL) ? USB_INSERTED : USB_EXTRACTED; | ||
223 | GPIO_WRITE_BITWISE(USB_GPIO_INT_LEV, val ^ USB_GPIO_MASK, USB_GPIO_MASK); | ||
224 | USB_GPIO_INT_CLR = USB_GPIO_MASK; | ||
225 | timeout_register(&usb_oneshot, usb_timeout_event, HZ/5, val); | ||
226 | } | ||
227 | |||
228 | /* USB_DETECT_BY_CORE: Called when device descriptor is requested */ | ||
229 | void usb_drv_usb_detect_event(void) | ||
230 | { | ||
231 | /* Filter for invalid bus reset when unplugging by checking the pin state. */ | ||
232 | if(usb_plugged()) { | ||
233 | usb_status_event(USB_HOSTED); | ||
234 | } | ||
235 | } | ||
236 | #endif /* USB_STATUS_BY_EVENT */ | ||
237 | |||
238 | #ifdef HAVE_BOOTLOADER_USB_MODE | ||
239 | /* Replacement function that returns all unused memory after the bootloader | ||
240 | * because the storage driver uses the audio buffer */ | ||
241 | extern unsigned char freebuffer[]; | ||
242 | extern unsigned char freebufferend[]; | ||
243 | unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size) | ||
244 | { | ||
245 | if (buffer_size) | ||
246 | *buffer_size = freebufferend - freebuffer + 1; | ||
247 | |||
248 | return freebuffer; | ||
249 | (void)talk_buf; | ||
250 | } | ||
251 | #endif /* HAVE_BOOTLOADER_USB_MODE */ | ||
252 | |||
253 | void usb_drv_int_enable(bool enable) | ||
254 | { | ||
255 | /* enable/disable USB IRQ in CPU */ | ||
256 | if(enable) { | ||
257 | CPU_INT_EN = USB_MASK; | ||
258 | } | ||
259 | else { | ||
260 | CPU_INT_DIS = USB_MASK; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | /* detect host or charger (INSERTED or EXTRACTED) */ | ||
265 | int usb_detect(void) | ||
266 | { | ||
267 | #ifdef USB_STATUS_BY_EVENT | ||
268 | return usb_status; | ||
269 | #else | ||
270 | return usb_plugged() ? USB_INSERTED : USB_EXTRACTED; | ||
271 | #endif | ||
272 | } | ||
273 | |||
274 | #ifdef USB_FIREWIRE_HANDLING | ||
275 | #ifdef USB_STATUS_BY_EVENT | ||
276 | static bool firewire_status = false; | ||
277 | #endif | ||
278 | |||
279 | bool firewire_detect(void) | ||
280 | { | ||
281 | #ifdef USB_STATUS_BY_EVENT | ||
282 | return firewire_status; | ||
283 | #else | ||
284 | /* GPIO C bit 1 is firewire detect */ | ||
285 | /* no charger detection needed for firewire */ | ||
286 | return (GPIOC_INPUT_VAL & 0x02) == 0x00; | ||
287 | #endif | ||
288 | } | ||
289 | |||
290 | #ifdef USB_STATUS_BY_EVENT | ||
291 | static int firewire_timeout_event(struct timeout *tmo) | ||
292 | { | ||
293 | if (tmo->data == 0x00) | ||
294 | usb_firewire_connect_event(); | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | void firewire_insert_int(void) | ||
299 | { | ||
300 | static struct timeout firewire_oneshot; | ||
301 | unsigned long val = GPIOC_INPUT_VAL & 0x02; | ||
302 | firewire_status = val == 0x00; | ||
303 | GPIO_WRITE_BITWISE(GPIOC_INT_LEV, val ^ 0x02, 0x02); | ||
304 | GPIOC_INT_CLR = 0x02; | ||
305 | timeout_register(&firewire_oneshot, firewire_timeout_event, HZ/5, val); | ||
306 | } | ||
307 | #endif /* USB_STATUS_BY_EVENT */ | ||
308 | #endif /* USB_FIREWIRE_HANDLING */ | ||
diff --git a/firmware/target/arm/pp/wmcodec-pp.c b/firmware/target/arm/pp/wmcodec-pp.c new file mode 100644 index 0000000000..839fd90676 --- /dev/null +++ b/firmware/target/arm/pp/wmcodec-pp.c | |||
@@ -0,0 +1,112 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Portalplayer specific code for Wolfson audio codecs | ||
11 | * | ||
12 | * Based on code from the ipodlinux project - http://ipodlinux.org/ | ||
13 | * Adapted for Rockbox in December 2005 | ||
14 | * | ||
15 | * Original file: linux/arch/armnommu/mach-ipod/audio.c | ||
16 | * | ||
17 | * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org) | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or | ||
20 | * modify it under the terms of the GNU General Public License | ||
21 | * as published by the Free Software Foundation; either version 2 | ||
22 | * of the License, or (at your option) any later version. | ||
23 | * | ||
24 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
25 | * KIND, either express or implied. | ||
26 | * | ||
27 | ****************************************************************************/ | ||
28 | |||
29 | #include "system.h" | ||
30 | #include "audiohw.h" | ||
31 | #include "i2c-pp.h" | ||
32 | #include "i2s.h" | ||
33 | #include "wmcodec.h" | ||
34 | |||
35 | #if defined(IRIVER_H10) || defined(IRIVER_H10_5GB) || \ | ||
36 | defined(MROBE_100) || defined(PHILIPS_HDD1630) || defined(PHILIPS_HDD6330) || \ | ||
37 | defined(PBELL_VIBE500) | ||
38 | /* The H10's audio codec uses an I2C address of 0x1b */ | ||
39 | #define I2C_AUDIO_ADDRESS 0x1b | ||
40 | #else | ||
41 | /* The iPod's audio codecs use an I2C address of 0x1a */ | ||
42 | #define I2C_AUDIO_ADDRESS 0x1a | ||
43 | #endif | ||
44 | |||
45 | |||
46 | /* | ||
47 | * Initialise the PP I2C and I2S. | ||
48 | */ | ||
49 | void audiohw_init(void) | ||
50 | { | ||
51 | #ifdef CPU_PP502x | ||
52 | /* normal outputs for CDI and I2S pin groups */ | ||
53 | DEV_INIT2 &= ~0x300; | ||
54 | |||
55 | /*mini2?*/ | ||
56 | DEV_INIT1 &=~0x3000000; | ||
57 | /*mini2?*/ | ||
58 | |||
59 | /* I2S device reset */ | ||
60 | DEV_RS |= DEV_I2S; | ||
61 | DEV_RS &=~DEV_I2S; | ||
62 | |||
63 | /* I2S device enable */ | ||
64 | DEV_EN |= DEV_I2S; | ||
65 | |||
66 | /* enable external dev clock clocks */ | ||
67 | DEV_EN |= DEV_EXTCLOCKS; | ||
68 | |||
69 | /* external dev clock to 24MHz */ | ||
70 | outl(inl(0x70000018) & ~0xc, 0x70000018); | ||
71 | #else | ||
72 | /* device reset */ | ||
73 | outl(inl(0xcf005030) | 0x80, 0xcf005030); | ||
74 | outl(inl(0xcf005030) & ~0x80, 0xcf005030); | ||
75 | |||
76 | /* device enable */ | ||
77 | outl(inl(0xcf005000) | 0x80, 0xcf005000); | ||
78 | |||
79 | /* GPIO D06 enable for output */ | ||
80 | outl(inl(0xcf00000c) | 0x40, 0xcf00000c); | ||
81 | outl(inl(0xcf00001c) & ~0x40, 0xcf00001c); | ||
82 | #ifdef IPOD_1G2G | ||
83 | /* bits 11,10 == 10 */ | ||
84 | outl(inl(0xcf004040) & ~0x400, 0xcf004040); | ||
85 | outl(inl(0xcf004040) | 0x800, 0xcf004040); | ||
86 | #else /* IPOD_3G */ | ||
87 | /* bits 11,10 == 01 */ | ||
88 | outl(inl(0xcf004040) | 0x400, 0xcf004040); | ||
89 | outl(inl(0xcf004040) & ~0x800, 0xcf004040); | ||
90 | |||
91 | outl(inl(0xcf004048) & ~0x1, 0xcf004048); | ||
92 | |||
93 | outl(inl(0xcf000004) & ~0xf, 0xcf000004); | ||
94 | outl(inl(0xcf004044) & ~0xf, 0xcf004044); | ||
95 | |||
96 | /* C03 = 0 */ | ||
97 | outl(inl(0xcf000008) | 0x8, 0xcf000008); | ||
98 | outl(inl(0xcf000018) | 0x8, 0xcf000018); | ||
99 | outl(inl(0xcf000028) & ~0x8, 0xcf000028); | ||
100 | #endif /* IPOD_1G2G/3G */ | ||
101 | #endif | ||
102 | |||
103 | /* reset the I2S controller into known state */ | ||
104 | i2s_reset(); | ||
105 | |||
106 | audiohw_preinit(); | ||
107 | } | ||
108 | |||
109 | void wmcodec_write(int reg, int data) | ||
110 | { | ||
111 | pp_i2c_send(I2C_AUDIO_ADDRESS, (reg<<1) | ((data&0x100)>>8),data&0xff); | ||
112 | } | ||