cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

aica.c (16896B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3*
      4* Copyright Adrian McMenamin 2005, 2006, 2007
      5* <adrian@mcmen.demon.co.uk>
      6* Requires firmware (BSD licenced) available from:
      7* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/
      8* or the maintainer
      9*/
     10
     11#include <linux/init.h>
     12#include <linux/jiffies.h>
     13#include <linux/slab.h>
     14#include <linux/time.h>
     15#include <linux/wait.h>
     16#include <linux/module.h>
     17#include <linux/platform_device.h>
     18#include <linux/firmware.h>
     19#include <linux/timer.h>
     20#include <linux/delay.h>
     21#include <linux/workqueue.h>
     22#include <linux/io.h>
     23#include <sound/core.h>
     24#include <sound/control.h>
     25#include <sound/pcm.h>
     26#include <sound/initval.h>
     27#include <sound/info.h>
     28#include <asm/dma.h>
     29#include <mach/sysasic.h>
     30#include "aica.h"
     31
     32MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
     33MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");
     34MODULE_LICENSE("GPL");
     35MODULE_FIRMWARE("aica_firmware.bin");
     36
     37/* module parameters */
     38#define CARD_NAME "AICA"
     39static int index = -1;
     40static char *id;
     41static bool enable = 1;
     42module_param(index, int, 0444);
     43MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
     44module_param(id, charp, 0444);
     45MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
     46module_param(enable, bool, 0644);
     47MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
     48
     49/* Simple platform device */
     50static struct platform_device *pd;
     51static struct resource aica_memory_space[2] = {
     52	{
     53	 .name = "AICA ARM CONTROL",
     54	 .start = ARM_RESET_REGISTER,
     55	 .flags = IORESOURCE_MEM,
     56	 .end = ARM_RESET_REGISTER + 3,
     57	 },
     58	{
     59	 .name = "AICA Sound RAM",
     60	 .start = SPU_MEMORY_BASE,
     61	 .flags = IORESOURCE_MEM,
     62	 .end = SPU_MEMORY_BASE + 0x200000 - 1,
     63	 },
     64};
     65
     66/* SPU specific functions */
     67/* spu_write_wait - wait for G2-SH FIFO to clear */
     68static void spu_write_wait(void)
     69{
     70	int time_count;
     71	time_count = 0;
     72	while (1) {
     73		if (!(readl(G2_FIFO) & 0x11))
     74			break;
     75		/* To ensure hardware failure doesn't wedge kernel */
     76		time_count++;
     77		if (time_count > 0x10000) {
     78			snd_printk
     79			    ("WARNING: G2 FIFO appears to be blocked.\n");
     80			break;
     81		}
     82	}
     83}
     84
     85/* spu_memset - write to memory in SPU address space */
     86static void spu_memset(u32 toi, u32 what, int length)
     87{
     88	int i;
     89	unsigned long flags;
     90	if (snd_BUG_ON(length % 4))
     91		return;
     92	for (i = 0; i < length; i++) {
     93		if (!(i % 8))
     94			spu_write_wait();
     95		local_irq_save(flags);
     96		writel(what, toi + SPU_MEMORY_BASE);
     97		local_irq_restore(flags);
     98		toi++;
     99	}
    100}
    101
    102/* spu_memload - write to SPU address space */
    103static void spu_memload(u32 toi, const void *from, int length)
    104{
    105	unsigned long flags;
    106	const u32 *froml = from;
    107	u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
    108	int i;
    109	u32 val;
    110	length = DIV_ROUND_UP(length, 4);
    111	spu_write_wait();
    112	for (i = 0; i < length; i++) {
    113		if (!(i % 8))
    114			spu_write_wait();
    115		val = *froml;
    116		local_irq_save(flags);
    117		writel(val, to);
    118		local_irq_restore(flags);
    119		froml++;
    120		to++;
    121	}
    122}
    123
    124/* spu_disable - set spu registers to stop sound output */
    125static void spu_disable(void)
    126{
    127	int i;
    128	unsigned long flags;
    129	u32 regval;
    130	spu_write_wait();
    131	regval = readl(ARM_RESET_REGISTER);
    132	regval |= 1;
    133	spu_write_wait();
    134	local_irq_save(flags);
    135	writel(regval, ARM_RESET_REGISTER);
    136	local_irq_restore(flags);
    137	for (i = 0; i < 64; i++) {
    138		spu_write_wait();
    139		regval = readl(SPU_REGISTER_BASE + (i * 0x80));
    140		regval = (regval & ~0x4000) | 0x8000;
    141		spu_write_wait();
    142		local_irq_save(flags);
    143		writel(regval, SPU_REGISTER_BASE + (i * 0x80));
    144		local_irq_restore(flags);
    145	}
    146}
    147
    148/* spu_enable - set spu registers to enable sound output */
    149static void spu_enable(void)
    150{
    151	unsigned long flags;
    152	u32 regval = readl(ARM_RESET_REGISTER);
    153	regval &= ~1;
    154	spu_write_wait();
    155	local_irq_save(flags);
    156	writel(regval, ARM_RESET_REGISTER);
    157	local_irq_restore(flags);
    158}
    159
    160/* 
    161 * Halt the sound processor, clear the memory,
    162 * load some default ARM7 code, and then restart ARM7
    163*/
    164static void spu_reset(void)
    165{
    166	unsigned long flags;
    167	spu_disable();
    168	spu_memset(0, 0, 0x200000 / 4);
    169	/* Put ARM7 in endless loop */
    170	local_irq_save(flags);
    171	__raw_writel(0xea000002, SPU_MEMORY_BASE);
    172	local_irq_restore(flags);
    173	spu_enable();
    174}
    175
    176/* aica_chn_start - write to spu to start playback */
    177static void aica_chn_start(void)
    178{
    179	unsigned long flags;
    180	spu_write_wait();
    181	local_irq_save(flags);
    182	writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);
    183	local_irq_restore(flags);
    184}
    185
    186/* aica_chn_halt - write to spu to halt playback */
    187static void aica_chn_halt(void)
    188{
    189	unsigned long flags;
    190	spu_write_wait();
    191	local_irq_save(flags);
    192	writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);
    193	local_irq_restore(flags);
    194}
    195
    196/* ALSA code below */
    197static const struct snd_pcm_hardware snd_pcm_aica_playback_hw = {
    198	.info = (SNDRV_PCM_INFO_NONINTERLEAVED),
    199	.formats =
    200	    (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
    201	     SNDRV_PCM_FMTBIT_IMA_ADPCM),
    202	.rates = SNDRV_PCM_RATE_8000_48000,
    203	.rate_min = 8000,
    204	.rate_max = 48000,
    205	.channels_min = 1,
    206	.channels_max = 2,
    207	.buffer_bytes_max = AICA_BUFFER_SIZE,
    208	.period_bytes_min = AICA_PERIOD_SIZE,
    209	.period_bytes_max = AICA_PERIOD_SIZE,
    210	.periods_min = AICA_PERIOD_NUMBER,
    211	.periods_max = AICA_PERIOD_NUMBER,
    212};
    213
    214static int aica_dma_transfer(int channels, int buffer_size,
    215			     struct snd_pcm_substream *substream)
    216{
    217	int q, err, period_offset;
    218	struct snd_card_aica *dreamcastcard;
    219	struct snd_pcm_runtime *runtime;
    220	unsigned long flags;
    221	err = 0;
    222	dreamcastcard = substream->pcm->private_data;
    223	period_offset = dreamcastcard->clicks;
    224	period_offset %= (AICA_PERIOD_NUMBER / channels);
    225	runtime = substream->runtime;
    226	for (q = 0; q < channels; q++) {
    227		local_irq_save(flags);
    228		err = dma_xfer(AICA_DMA_CHANNEL,
    229			       (unsigned long) (runtime->dma_area +
    230						(AICA_BUFFER_SIZE * q) /
    231						channels +
    232						AICA_PERIOD_SIZE *
    233						period_offset),
    234			       AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
    235			       AICA_PERIOD_SIZE * period_offset,
    236			       buffer_size / channels, AICA_DMA_MODE);
    237		if (unlikely(err < 0)) {
    238			local_irq_restore(flags);
    239			break;
    240		}
    241		dma_wait_for_completion(AICA_DMA_CHANNEL);
    242		local_irq_restore(flags);
    243	}
    244	return err;
    245}
    246
    247static void startup_aica(struct snd_card_aica *dreamcastcard)
    248{
    249	spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
    250		    dreamcastcard->channel, sizeof(struct aica_channel));
    251	aica_chn_start();
    252}
    253
    254static void run_spu_dma(struct work_struct *work)
    255{
    256	int buffer_size;
    257	struct snd_pcm_runtime *runtime;
    258	struct snd_card_aica *dreamcastcard;
    259	dreamcastcard =
    260	    container_of(work, struct snd_card_aica, spu_dma_work);
    261	runtime = dreamcastcard->substream->runtime;
    262	if (unlikely(dreamcastcard->dma_check == 0)) {
    263		buffer_size =
    264		    frames_to_bytes(runtime, runtime->buffer_size);
    265		if (runtime->channels > 1)
    266			dreamcastcard->channel->flags |= 0x01;
    267		aica_dma_transfer(runtime->channels, buffer_size,
    268				  dreamcastcard->substream);
    269		startup_aica(dreamcastcard);
    270		dreamcastcard->clicks =
    271		    buffer_size / (AICA_PERIOD_SIZE * runtime->channels);
    272		return;
    273	} else {
    274		aica_dma_transfer(runtime->channels,
    275				  AICA_PERIOD_SIZE * runtime->channels,
    276				  dreamcastcard->substream);
    277		snd_pcm_period_elapsed(dreamcastcard->substream);
    278		dreamcastcard->clicks++;
    279		if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
    280			dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
    281		mod_timer(&dreamcastcard->timer, jiffies + 1);
    282	}
    283}
    284
    285static void aica_period_elapsed(struct timer_list *t)
    286{
    287	struct snd_card_aica *dreamcastcard = from_timer(dreamcastcard,
    288							      t, timer);
    289	struct snd_pcm_substream *substream = dreamcastcard->substream;
    290	/*timer function - so cannot sleep */
    291	int play_period;
    292	struct snd_pcm_runtime *runtime;
    293	runtime = substream->runtime;
    294	dreamcastcard = substream->pcm->private_data;
    295	/* Have we played out an additional period? */
    296	play_period =
    297	    frames_to_bytes(runtime,
    298			    readl
    299			    (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) /
    300	    AICA_PERIOD_SIZE;
    301	if (play_period == dreamcastcard->current_period) {
    302		/* reschedule the timer */
    303		mod_timer(&(dreamcastcard->timer), jiffies + 1);
    304		return;
    305	}
    306	if (runtime->channels > 1)
    307		dreamcastcard->current_period = play_period;
    308	if (unlikely(dreamcastcard->dma_check == 0))
    309		dreamcastcard->dma_check = 1;
    310	schedule_work(&(dreamcastcard->spu_dma_work));
    311}
    312
    313static void spu_begin_dma(struct snd_pcm_substream *substream)
    314{
    315	struct snd_card_aica *dreamcastcard;
    316	struct snd_pcm_runtime *runtime;
    317	runtime = substream->runtime;
    318	dreamcastcard = substream->pcm->private_data;
    319	/*get the queue to do the work */
    320	schedule_work(&(dreamcastcard->spu_dma_work));
    321	mod_timer(&dreamcastcard->timer, jiffies + 4);
    322}
    323
    324static int snd_aicapcm_pcm_open(struct snd_pcm_substream
    325				*substream)
    326{
    327	struct snd_pcm_runtime *runtime;
    328	struct aica_channel *channel;
    329	struct snd_card_aica *dreamcastcard;
    330	if (!enable)
    331		return -ENOENT;
    332	dreamcastcard = substream->pcm->private_data;
    333	channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL);
    334	if (!channel)
    335		return -ENOMEM;
    336	/* set defaults for channel */
    337	channel->sfmt = SM_8BIT;
    338	channel->cmd = AICA_CMD_START;
    339	channel->vol = dreamcastcard->master_volume;
    340	channel->pan = 0x80;
    341	channel->pos = 0;
    342	channel->flags = 0;	/* default to mono */
    343	dreamcastcard->channel = channel;
    344	runtime = substream->runtime;
    345	runtime->hw = snd_pcm_aica_playback_hw;
    346	spu_enable();
    347	dreamcastcard->clicks = 0;
    348	dreamcastcard->current_period = 0;
    349	dreamcastcard->dma_check = 0;
    350	return 0;
    351}
    352
    353static int snd_aicapcm_pcm_close(struct snd_pcm_substream
    354				 *substream)
    355{
    356	struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
    357	flush_work(&(dreamcastcard->spu_dma_work));
    358	del_timer(&dreamcastcard->timer);
    359	dreamcastcard->substream = NULL;
    360	kfree(dreamcastcard->channel);
    361	spu_disable();
    362	return 0;
    363}
    364
    365static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
    366				   *substream)
    367{
    368	struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
    369	if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE)
    370		dreamcastcard->channel->sfmt = SM_16BIT;
    371	dreamcastcard->channel->freq = substream->runtime->rate;
    372	dreamcastcard->substream = substream;
    373	return 0;
    374}
    375
    376static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream
    377				   *substream, int cmd)
    378{
    379	switch (cmd) {
    380	case SNDRV_PCM_TRIGGER_START:
    381		spu_begin_dma(substream);
    382		break;
    383	case SNDRV_PCM_TRIGGER_STOP:
    384		aica_chn_halt();
    385		break;
    386	default:
    387		return -EINVAL;
    388	}
    389	return 0;
    390}
    391
    392static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
    393					     *substream)
    394{
    395	return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);
    396}
    397
    398static const struct snd_pcm_ops snd_aicapcm_playback_ops = {
    399	.open = snd_aicapcm_pcm_open,
    400	.close = snd_aicapcm_pcm_close,
    401	.prepare = snd_aicapcm_pcm_prepare,
    402	.trigger = snd_aicapcm_pcm_trigger,
    403	.pointer = snd_aicapcm_pcm_pointer,
    404};
    405
    406/* TO DO: set up to handle more than one pcm instance */
    407static int __init snd_aicapcmchip(struct snd_card_aica
    408				  *dreamcastcard, int pcm_index)
    409{
    410	struct snd_pcm *pcm;
    411	int err;
    412	/* AICA has no capture ability */
    413	err =
    414	    snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0,
    415			&pcm);
    416	if (unlikely(err < 0))
    417		return err;
    418	pcm->private_data = dreamcastcard;
    419	strcpy(pcm->name, "AICA PCM");
    420	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
    421			&snd_aicapcm_playback_ops);
    422	/* Allocate the DMA buffers */
    423	snd_pcm_set_managed_buffer_all(pcm,
    424				       SNDRV_DMA_TYPE_CONTINUOUS,
    425				       NULL,
    426				       AICA_BUFFER_SIZE,
    427				       AICA_BUFFER_SIZE);
    428	return 0;
    429}
    430
    431/* Mixer controls */
    432#define aica_pcmswitch_info		snd_ctl_boolean_mono_info
    433
    434static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol,
    435			      struct snd_ctl_elem_value *ucontrol)
    436{
    437	ucontrol->value.integer.value[0] = 1;	/* TO DO: Fix me */
    438	return 0;
    439}
    440
    441static int aica_pcmswitch_put(struct snd_kcontrol *kcontrol,
    442			      struct snd_ctl_elem_value *ucontrol)
    443{
    444	if (ucontrol->value.integer.value[0] == 1)
    445		return 0;	/* TO DO: Fix me */
    446	else
    447		aica_chn_halt();
    448	return 0;
    449}
    450
    451static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol,
    452			       struct snd_ctl_elem_info *uinfo)
    453{
    454	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
    455	uinfo->count = 1;
    456	uinfo->value.integer.min = 0;
    457	uinfo->value.integer.max = 0xFF;
    458	return 0;
    459}
    460
    461static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,
    462			      struct snd_ctl_elem_value *ucontrol)
    463{
    464	struct snd_card_aica *dreamcastcard;
    465	dreamcastcard = kcontrol->private_data;
    466	if (unlikely(!dreamcastcard->channel))
    467		return -ETXTBSY;	/* we've not yet been set up */
    468	ucontrol->value.integer.value[0] = dreamcastcard->channel->vol;
    469	return 0;
    470}
    471
    472static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
    473			      struct snd_ctl_elem_value *ucontrol)
    474{
    475	struct snd_card_aica *dreamcastcard;
    476	unsigned int vol;
    477	dreamcastcard = kcontrol->private_data;
    478	if (unlikely(!dreamcastcard->channel))
    479		return -ETXTBSY;
    480	vol = ucontrol->value.integer.value[0];
    481	if (vol > 0xff)
    482		return -EINVAL;
    483	if (unlikely(dreamcastcard->channel->vol == vol))
    484		return 0;
    485	dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
    486	dreamcastcard->master_volume = ucontrol->value.integer.value[0];
    487	spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
    488		    dreamcastcard->channel, sizeof(struct aica_channel));
    489	return 1;
    490}
    491
    492static const struct snd_kcontrol_new snd_aica_pcmswitch_control = {
    493	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
    494	.name = "PCM Playback Switch",
    495	.index = 0,
    496	.info = aica_pcmswitch_info,
    497	.get = aica_pcmswitch_get,
    498	.put = aica_pcmswitch_put
    499};
    500
    501static const struct snd_kcontrol_new snd_aica_pcmvolume_control = {
    502	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
    503	.name = "PCM Playback Volume",
    504	.index = 0,
    505	.info = aica_pcmvolume_info,
    506	.get = aica_pcmvolume_get,
    507	.put = aica_pcmvolume_put
    508};
    509
    510static int load_aica_firmware(void)
    511{
    512	int err;
    513	const struct firmware *fw_entry;
    514	spu_reset();
    515	err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev);
    516	if (unlikely(err))
    517		return err;
    518	/* write firmware into memory */
    519	spu_disable();
    520	spu_memload(0, fw_entry->data, fw_entry->size);
    521	spu_enable();
    522	release_firmware(fw_entry);
    523	return err;
    524}
    525
    526static int add_aicamixer_controls(struct snd_card_aica *dreamcastcard)
    527{
    528	int err;
    529	err = snd_ctl_add
    530	    (dreamcastcard->card,
    531	     snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard));
    532	if (unlikely(err < 0))
    533		return err;
    534	err = snd_ctl_add
    535	    (dreamcastcard->card,
    536	     snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard));
    537	if (unlikely(err < 0))
    538		return err;
    539	return 0;
    540}
    541
    542static int snd_aica_remove(struct platform_device *devptr)
    543{
    544	struct snd_card_aica *dreamcastcard;
    545	dreamcastcard = platform_get_drvdata(devptr);
    546	if (unlikely(!dreamcastcard))
    547		return -ENODEV;
    548	snd_card_free(dreamcastcard->card);
    549	kfree(dreamcastcard);
    550	return 0;
    551}
    552
    553static int snd_aica_probe(struct platform_device *devptr)
    554{
    555	int err;
    556	struct snd_card_aica *dreamcastcard;
    557	dreamcastcard = kzalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
    558	if (unlikely(!dreamcastcard))
    559		return -ENOMEM;
    560	err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER,
    561			   THIS_MODULE, 0, &dreamcastcard->card);
    562	if (unlikely(err < 0)) {
    563		kfree(dreamcastcard);
    564		return err;
    565	}
    566	strcpy(dreamcastcard->card->driver, "snd_aica");
    567	strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
    568	strcpy(dreamcastcard->card->longname,
    569	       "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
    570	/* Prepare to use the queue */
    571	INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
    572	timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0);
    573	/* Load the PCM 'chip' */
    574	err = snd_aicapcmchip(dreamcastcard, 0);
    575	if (unlikely(err < 0))
    576		goto freedreamcast;
    577	/* Add basic controls */
    578	err = add_aicamixer_controls(dreamcastcard);
    579	if (unlikely(err < 0))
    580		goto freedreamcast;
    581	/* Register the card with ALSA subsystem */
    582	err = snd_card_register(dreamcastcard->card);
    583	if (unlikely(err < 0))
    584		goto freedreamcast;
    585	platform_set_drvdata(devptr, dreamcastcard);
    586	snd_printk
    587	    ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
    588	return 0;
    589      freedreamcast:
    590	snd_card_free(dreamcastcard->card);
    591	kfree(dreamcastcard);
    592	return err;
    593}
    594
    595static struct platform_driver snd_aica_driver = {
    596	.probe = snd_aica_probe,
    597	.remove = snd_aica_remove,
    598	.driver = {
    599		.name = SND_AICA_DRIVER,
    600	},
    601};
    602
    603static int __init aica_init(void)
    604{
    605	int err;
    606	err = platform_driver_register(&snd_aica_driver);
    607	if (unlikely(err < 0))
    608		return err;
    609	pd = platform_device_register_simple(SND_AICA_DRIVER, -1,
    610					     aica_memory_space, 2);
    611	if (IS_ERR(pd)) {
    612		platform_driver_unregister(&snd_aica_driver);
    613		return PTR_ERR(pd);
    614	}
    615	/* Load the firmware */
    616	return load_aica_firmware();
    617}
    618
    619static void __exit aica_exit(void)
    620{
    621	platform_device_unregister(pd);
    622	platform_driver_unregister(&snd_aica_driver);
    623	/* Kill any sound still playing and reset ARM7 to safe state */
    624	spu_reset();
    625}
    626
    627module_init(aica_init);
    628module_exit(aica_exit);