Index: linux/arch/arm/mach-sa1100/ucb1x00.c =================================================================== --- /dev/null +++ linux/arch/arm/mach-sa1100/ucb1x00.c @@ -0,0 +1,242 @@ +/* + * linux/arch/arm/mach-sa1100/ucb1x00.c + * + * Copyright (C) 2005 John Lenz + * Based on code written by Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * SA1100 UCB Register access driver. + * + * MCP read/write timeouts from Jordi Colomer, rehacked by rmk. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct ucb_sa1100_reg_access { + struct ucb1x00_reg_access reg_access; + + unsigned int rw_timeout; + +#ifdef CONFIG_PM + /* used for suspend and resume */ + u32 mccr0; + u32 mccr1; +#endif +}; +#define UCB_SA1100_REG_ACCESS(access) container_of(access, struct ucb_sa1100_reg_access, reg_access) + +#define UCB_SA1100_SCLK_RATE 11981000 + +static void +ucb_sa1100_set_telecom_divisor(struct ucb1x00_reg_access *access, unsigned int divisor) +{ + unsigned int mccr0; + + divisor /= 32; + + mccr0 = Ser4MCCR0 & ~0x00007f00; + mccr0 |= divisor << 8; + Ser4MCCR0 = mccr0; +} + +static void +ucb_sa1100_set_audio_divisor(struct ucb1x00_reg_access *access, unsigned int divisor) +{ + unsigned int mccr0; + + divisor /= 32; + + mccr0 = Ser4MCCR0 & ~0x0000007f; + mccr0 |= divisor; + Ser4MCCR0 = mccr0; +} + +/* + * Write data to the device. The bit should be set after 3 subframe + * times (each frame is 64 clocks). We wait a maximum of 6 subframes. + * We really should try doing something more productive while we + * wait. + */ +static void +ucb_sa1100_write(struct ucb1x00_reg_access *access, unsigned int reg, unsigned int val) +{ + int ret = -ETIME; + int i; + struct ucb_sa1100_reg_access *ucb = UCB_SA1100_REG_ACCESS(access); + + Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); + + for (i = 0; i < 2; i++) { + udelay(ucb->rw_timeout); + if (Ser4MCSR & MCSR_CWC) { + ret = 0; + break; + } + } + + if (ret < 0) + printk(KERN_WARNING "ucb sa1100: write timed out\n"); +} + +/* + * Read data from the device. The bit should be set after 3 subframe + * times (each frame is 64 clocks). We wait a maximum of 6 subframes. + * We really should try doing something more productive while we + * wait. + */ +static unsigned int +ucb_sa1100_read(struct ucb1x00_reg_access *access, unsigned int reg) +{ + int ret = -ETIME; + int i; + struct ucb_sa1100_reg_access *ucb = UCB_SA1100_REG_ACCESS(access); + + Ser4MCDR2 = reg << 17 | MCDR2_Rd; + + for (i = 0; i < 2; i++) { + udelay(ucb->rw_timeout); + if (Ser4MCSR & MCSR_CRC) { + ret = Ser4MCDR2 & 0xffff; + break; + } + } + + if (ret < 0) + printk(KERN_WARNING "mcp: read timed out\n"); + + return ret; +} + +static void ucb_sa1100_enable(struct ucb1x00_reg_access *access) +{ + Ser4MCSR = -1; + Ser4MCCR0 |= MCCR0_MCE; +} + +static void ucb_sa1100_disable(struct ucb1x00_reg_access *access) +{ + Ser4MCCR0 &= ~MCCR0_MCE; +} + +#ifdef CONFIG_PM +static int ucb_sa1100_suspend(struct ucb1x00_reg_access *access, pm_message_t state, u32 level) +{ + struct ucb_sa1100_reg_access *ucb = UCB_SA1100_REG_ACCESS(access); + + if (ucb) { + ucb->mccr0 = Ser4MCCR0; + ucb->mccr1 = Ser4MCCR1; + } + + if (level == SUSPEND_DISABLE) + Ser4MCCR0 &= ~MCCR0_MCE; + return 0; +} + +static int ucb_sa1100_resume(struct ucb1x00_reg_access *access, u32 level) +{ + struct ucb_sa1100_reg_access *ucb = UCB_SA1100_REG_ACCESS(access); + + if (ucb && level == RESUME_RESTORE_STATE) { + Ser4MCCR1 = ucb->mccr1; + Ser4MCCR0 = ucb->mccr0; + } + return 0; +} +#else +#define ucb_sa1100_suspend NULL +#define ucb_sa1100_resume NULL +#endif + +static struct ucb_sa1100_reg_access ucb_sa1100_reg_access = { + .reg_access = { + .enable = ucb_sa1100_enable, + .disable = ucb_sa1100_disable, + .read = ucb_sa1100_read, + .write = ucb_sa1100_write, + + .audio_divisor = ucb_sa1100_set_audio_divisor, + .telecom_divisor = ucb_sa1100_set_telecom_divisor, + + .clkrate = UCB_SA1100_SCLK_RATE, + + .suspend = ucb_sa1100_suspend, + .resume = ucb_sa1100_resume, + }, +}; + +static struct platform_device ucb_sa1100_dev = { + .name = "ucb1x00", + .id = 0, + .dev = { + .platform_data = &ucb_sa1100_reg_access, + }, +}; + +int sa1100_ucb_init(struct resource *resources, int nr, int ext_clock) +{ + int ret; + + ucb_sa1100_dev.resource = resources; + ucb_sa1100_dev.num_resources = nr; + + if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) + return -EBUSY; + + /*if (machine_is_assabet()) { + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + }*/ + + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + + Ser4MCSR = -1; + Ser4MCCR1 = 0; + if (ext_clock) + Ser4MCCR0 = 0x00007f7f | MCCR0_ADM | MCCR0_ExtClk; + else + Ser4MCCR0 = 0x00007f7f | MCCR0_ADM | MCCR0_IntClk; + + /* + * Calculate the read/write timeout (us) from the bit clock + * rate. This is the period for 3 64-bit frames. Always + * round this time up. + */ + ucb_sa1100_reg_access.rw_timeout = (64 * 3 * 1000000 + ucb_sa1100_reg_access.reg_access.clkrate - 1) / + ucb_sa1100_reg_access.reg_access.clkrate; + + ret = platform_device_register(&ucb_sa1100_dev); + if (ret) + printk(KERN_ERR "ucb sa1100: unable to register platform device\n"); + + return ret; +} + +/*void ucb_sa1100_exit(void) +{ + platform_device_unregister(&ucb_sa1100_dev); + release_mem_region(0x80060000, 0x60); +}*/ + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("SA11x0 UCB register access driver"); +MODULE_LICENSE("GPL"); Index: linux/arch/arm/mach-sa1100/generic.h =================================================================== --- linux.orig/arch/arm/mach-sa1100/generic.h +++ linux/arch/arm/mach-sa1100/generic.h @@ -36,3 +36,5 @@ struct irda_platform_data; void sa11x0_set_irda_data(struct irda_platform_data *irda); + +int sa1100_ucb_init(struct resource *resources, int nr, int ext_clock);