summaryrefslogtreecommitdiff
path: root/firmware/target/arm/pp
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/pp')
-rw-r--r--firmware/target/arm/pp/adc-pp5020.c179
-rw-r--r--firmware/target/arm/pp/ascodec-pp.c82
-rw-r--r--firmware/target/arm/pp/ata-pp5002.c53
-rw-r--r--firmware/target/arm/pp/ata-pp5020.c248
-rw-r--r--firmware/target/arm/pp/ata-sd-pp.c1387
-rw-r--r--firmware/target/arm/pp/audio-pp.c135
-rw-r--r--firmware/target/arm/pp/boot-pp502x-bl-usb.lds133
-rw-r--r--firmware/target/arm/pp/crt0-pp-bl.S217
-rw-r--r--firmware/target/arm/pp/crt0-pp.S430
-rw-r--r--firmware/target/arm/pp/crt0-pp502x-bl-usb.S367
-rw-r--r--firmware/target/arm/pp/debug-pp.c232
-rw-r--r--firmware/target/arm/pp/i2c-pp.c314
-rw-r--r--firmware/target/arm/pp/i2s-pp.c95
-rw-r--r--firmware/target/arm/pp/kernel-pp.c64
-rw-r--r--firmware/target/arm/pp/pcm-pp.c734
-rw-r--r--firmware/target/arm/pp/system-pp5002.c227
-rw-r--r--firmware/target/arm/pp/system-pp502x.c607
-rw-r--r--firmware/target/arm/pp/thread-pp.c554
-rw-r--r--firmware/target/arm/pp/timer-pp.c86
-rw-r--r--firmware/target/arm/pp/usb-fw-pp5002.c75
-rw-r--r--firmware/target/arm/pp/usb-fw-pp502x.c308
-rw-r--r--firmware/target/arm/pp/wmcodec-pp.c112
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
38static unsigned short adcdata[NUM_ADC_CHANNELS];
39
40/* Scan ADC so that adcdata[channel] gets updated. */
41unsigned 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 */
79unsigned short adc_read(int channel)
80{
81 return adcdata[channel];
82}
83
84static int adc_counter;
85
86static 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 */
99void 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 */
34void 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
68void 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
28void ata_reset()
29{
30
31}
32
33void ata_enable(bool on)
34{
35 /* TODO: Implement ata_enable() */
36 (void)on;
37}
38
39bool ata_is_coldstart()
40{
41 return false;
42 /* TODO: Implement coldstart variable */
43}
44
45void 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
28void ata_reset()
29{
30
31}
32
33void ata_enable(bool on)
34{
35 /* TODO: Implement ata_enable() */
36 (void)on;
37}
38
39bool ata_is_coldstart()
40{
41 return false;
42 /* TODO: Implement coldstart variable */
43}
44
45void 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.
76static 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 */
85static const unsigned long tm_mwdma[] = {
86 0xF9F92, 0x56562, 0x45451
87};
88
89static 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
97static bool dma_boosted = false;
98static 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 */
104void 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 */
136STATICIRAM 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 */
159bool 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 */
223bool 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 */
153static long last_disk_activity = -1;
154
155/** static, private data **/
156static bool initialized = false;
157static unsigned int sd_thread_id = 0;
158
159#define Q_CLOSE 1
160
161static long next_yield = 0;
162#define MIN_YIELD_PERIOD 1000
163
164static tCardInfo card_info[2];
165static tCardInfo *currcard = NULL; /* current active card */
166
167struct sd_card_status
168{
169 int retry;
170 int retry_max;
171};
172
173static 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 */
182static long sd_stack [(DEFAULT_STACK_SIZE*2 + 0x1c0)/sizeof(long)];
183static const char sd_thread_name[] = "ata/sd";
184static struct mutex sd_mtx SHAREDBSS_ATTR;
185static struct event_queue sd_queue SHAREDBSS_ATTR;
186
187#ifdef HAVE_HOTSWAP
188static 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 */
194enum sd_thread_actions
195{
196 SDA_NONE = 0x0,
197 SDA_UNMOUNTED = 0x1,
198 SDA_MOUNTED = 0x2
199};
200
201/* Private Functions */
202
203static unsigned int check_time[NUM_EC];
204
205static inline bool sd_check_timeout(long timeout, int id)
206{
207 return !TIME_AFTER(USEC_TIMER, check_time[id] + timeout);
208}
209
210static 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
233static 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
313static 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
348static 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
362static 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
485static 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 */
506static 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
551static 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
599static 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
672static 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 */
836card_init_error:
837 currcard->initialized = ret;
838}
839
840/* lock must already be aquired */
841static 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
867int 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
883sd_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
973sd_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
984int 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
1001sd_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
1101sd_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
1113static void sd_thread(void) NORETURN_ATTR;
1114#endif
1115static 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
1189void 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
1203void 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
1216int 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
1301tCardInfo *card_get_info_target(int card_no)
1302{
1303 return &card_info[card_no];
1304}
1305#ifdef HAVE_HOTSWAP
1306static 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 */
1321void 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
1341long sd_last_disk_activity(void)
1342{
1343 return last_disk_activity;
1344}
1345
1346#ifdef HAVE_HOTSWAP
1347bool 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
1355bool 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
1372int 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
27void 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
33void 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 */
2ENTRY(start)
3OUTPUT_FORMAT(elf32-littlearm)
4OUTPUT_ARCH(arm)
5STARTUP(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
20MEMORY
21{
22 DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
23 IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
24}
25
26SECTIONS
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
27start:
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
731:
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
87pad_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
95start_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
102cop:
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
117cpu:
118 /* Wait for COP to be sleeping */
119 ldr r0, =COP_STATUS
1201:
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
1291:
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
1381:
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
178startup_loc:
179 .word 0x0
180
181#ifdef IPOD_ARCH
182.align 8 /* starts at 0x100 */
183.global boot_table
184boot_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
190cache_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]
2021:
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
2111:
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
27start:
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
90pad_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
1291:
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 */
1671:
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
180remap_start:
181 str r2, [r1]
182 str r4, [r3]
183 ldr r1, L_post_remap
184 bx r1
185L_post_remap:
186 .word remap_end
187remap_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
203cpu_init:
204 /* Wait for COP to go to sleep before proceeding */
205 ldr r4, =COP_STATUS
2061:
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
2181:
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
2281:
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
2401:
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
2511:
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
2601:
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
2721:
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
2991:
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
316cop_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
3201:
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
3482:
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
369vectors:
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 */
386undef_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 */
394software_int_handler:
395reserved_handler:
396 sub r0, lr, #4
397 mov r1, #4
398 b UIE
399
400prefetch_abort_handler:
401 sub r0, lr, #4
402 mov r1, #1
403 b UIE
404
405data_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
415irq_stack:
416
417/* 256 words of COP IRQ stack */
418 .space 256*4
419cop_irq_stack:
420
421/* 256 words of FIQ stack */
422 .space 256*4
423fiq_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
430cop_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
62start:
63 b newstart
64
65#ifdef IPOD_ARCH
66.align 8 /* starts at 0x100 */
67.global boot_table
68boot_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
77newstart:
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
861:
87 ldr r3, [r0], #4
88 str r3, [r1], #4
89 cmp r0, r2
90 blo 1b
91 mov pc, #0x40000000
92
93start_stub_begin:
94 ldr r0, =PROC_ID
95 ldrb r0, [r0]
96 cmp r0, #0x55
97 beq cpu
98
99cop:
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
1161:
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
127cpu:
128 /* wait for COP to sleep */
129 ldr r1, =COP_STATUS
1301:
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 */
1451: /* lower to higher move - copy up */
146 cmphi r2, r1
147 ldrhi r3, [r0], #4
148 strhi r3, [r1], #4
149 bhi 1b
1501: /* 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
1761:
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
184cache_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]
1951:
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 */
201start_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
2081:
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
2181:
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
2291:
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
2371:
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
2601:
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
313startup_loc:
314 .word 0x00000000
315
316/* exception handlers: will be copied to local vector table */
317vectorsstart:
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
326vectorsend:
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 */
335undef_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 */
343software_int_handler:
344reserved_handler:
345 sub r0, lr, #4
346 mov r1, #4
347 b UIE
348
349prefetch_abort_handler:
350 sub r0, lr, #4
351 mov r1, #1
352 b UIE
353
354data_abort_handler:
355 sub r0, lr, #8
356 mov r1, #2
357 b UIE
358
359/* should never happen in the bootloader */
360fiq_handler:
361 subs pc, lr, #4
362
363/* 256 words of IRQ stack */
364 .section .bss
365 .balign 16
366 .space 256*4
367irq_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
34static 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
68bool 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
191bool 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 */
51static struct mutex i2c_mtx SHAREDBSS_ATTR;
52
53#define POLL_TIMEOUT (HZ)
54
55static 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
69static 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
116static 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
153static 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 */
163void i2c_lock(void)
164{
165 mutex_lock(&i2c_mtx);
166}
167
168void i2c_unlock(void)
169{
170 mutex_unlock(&i2c_mtx);
171}
172
173int 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
199int 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
211int 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
234int 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
249void 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
37void 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 */
54void 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)
26void 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 */
43void 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
59void 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
53struct 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
71extern void *fiq_function;
72
73/* Dispatch to the proper handler and leave the main vector table alone */
74void fiq_handler(void) ICODE_ATTR __attribute__((naked));
75void 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
85void * 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 **/
98static 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
110void 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 */
117void 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
180void fiq_playback(void) ICODE_ATTR __attribute__((naked));
181void 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 */
280void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR;
281/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
282void 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 */
327void 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
342void 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
357static 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
393static 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
434void 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 */
464void 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
474void pcm_play_dma_pause(bool pause)
475{
476 if (pause) {
477 play_stop_pcm();
478 } else {
479 play_start_pcm();
480 }
481}
482
483size_t pcm_get_bytes_waiting(void)
484{
485 return dma_play_data.size & ~3;
486}
487
488void 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
530void pcm_play_dma_postinit(void)
531{
532 audiohw_postinit();
533}
534
535const 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 */
553static 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 */
568void 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
578void 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 */
589void fiq_record(void) ICODE_ATTR __attribute__((interrupt ("FIQ")));
590
591#if defined(SANSA_C200) || defined(SANSA_E200)
592void 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
657void 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
679void 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
695void 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
719void pcm_rec_dma_close(void)
720{
721 pcm_rec_dma_stop();
722} /* pcm_close_recording */
723
724void pcm_rec_dma_init(void)
725{
726 pcm_rec_dma_stop();
727} /* pcm_init */
728
729const 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
27extern void TIMER1(void);
28extern void TIMER2(void);
29
30void __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
58void 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
73void 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
90void commit_discard_dcache(void) __attribute__((alias("commit_discard_idcache")));
91
92static 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
125void set_cpu_frequency(long frequency)
126#else
127static 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
170void 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
206void system_reboot(void)
207{
208 DEV_RS |= 4;
209 while (1);
210}
211
212void 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
223int 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)
36extern void TIMER1(void);
37extern void TIMER2(void);
38extern void SERIAL0(void);
39
40#if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1)
41static struct corelock cpufreq_cl SHAREDBSS_ATTR;
42#endif
43
44#if defined(IPOD_VIDEO) && !defined(BOOTLOADER)
45unsigned char probed_ramsize;
46#endif
47
48void __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)
187static 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
211void 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
221void 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
231void commit_discard_dcache(void) __attribute__((alias("commit_discard_idcache")));
232
233static 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))
265void scale_suspend_core(bool suspend) ICODE_ATTR;
266void 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
287void set_cpu_frequency(long frequency) ICODE_ATTR;
288void set_cpu_frequency(long frequency)
289#else
290static 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
411void 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
540void 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
558void 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
569void 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
591void 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
602int 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 */
31static inline void core_sleep(void)
32{
33 sleep_core(CURRENT_CORE);
34 enable_irq();
35}
36
37/* Shared single-core build debugging version */
38void 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 **/
44extern uintptr_t cpu_idlestackbegin[];
45extern uintptr_t cpu_idlestackend[];
46extern uintptr_t cop_idlestackbegin[];
47extern uintptr_t cop_idlestackend[];
48static 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 */
60void 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 */
70void __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 */
96int __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 */
124void __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
139void 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
154int 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
171void 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 */
182static 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 */
212static 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 */
244static 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 */
268static 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 */
301struct 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
309static 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 */
317static 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 */
357void 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
391static 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
413void 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 */
444static 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 */
473void 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
504static 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
524void 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 */
549static 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
26static long SHAREDBSS_ATTR cycles_new = 0;
27
28void 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
46bool 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
69bool 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
81void 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
29void 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 */
41void usb_init_device(void) __attribute__((alias("usb_pin_init")));
42
43void 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
63int 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}
75bool 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
108static 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 */
141void 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
152void 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
182void 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
198void usb_attach(void)
199{
200 usb_drv_attach();
201}
202
203bool 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 */
210static int usb_status = USB_EXTRACTED;
211
212static 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
218void 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 */
229void 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 */
241extern unsigned char freebuffer[];
242extern unsigned char freebufferend[];
243unsigned 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
253void 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) */
265int 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
276static bool firewire_status = false;
277#endif
278
279bool 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
291static int firewire_timeout_event(struct timeout *tmo)
292{
293 if (tmo->data == 0x00)
294 usb_firewire_connect_event();
295 return 0;
296}
297
298void 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 */
49void 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
109void wmcodec_write(int reg, int data)
110{
111 pp_i2c_send(I2C_AUDIO_ADDRESS, (reg<<1) | ((data&0x100)>>8),data&0xff);
112}