Index: linux-2.6.16.13-ezx5/arch/arm/mach-pxa/ezx-pcap.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.16.13-ezx5/arch/arm/mach-pxa/ezx-pcap.c 2006-05-19 02:35:53.000000000 +0200 @@ -0,0 +1,483 @@ +/* Driver for Motorola PCAP2 as present in EZX phones + * + * This is both a SPI device driver for PCAP itself, as well as + * an IRQ demultiplexer for handling PCAP generated events such as + * headphone jack sense by downstream drivers. + * + * (C) 2006 by Harald Welte + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if 0 +#define DEBUGP(x, args...) printk(x, ## args) +#else +#define DEBUGP(x, args...) +#endif + +/* lowlevel functions for register access */ + +static struct { + struct spi_transfer transfer; + u_int32_t rx_buf; + u_int32_t tx_buf; + spinlock_t lock; /* lock protecting transfer + buffers */ +} pcap_spi; + +/* this is a static pointer to the PCAP spi device. We can keep this around + * since it wouldn't make sense to have two PCAP devices in a single phone + * anyway */ +static struct spi_device *pcap_spi_dev; + +int ezx_pcap_write(u_int8_t reg_num, u_int32_t value) +{ + int ret; + u_int32_t frame; + + value &= SSP_PCAP_REGISTER_VALUE_MASK; + + spin_lock(&pcap_spi.lock); + + pcap_spi.tx_buf = value | SSP_PCAP_REGISTER_WRITE_OP_BIT + |(reg_num<= 0) + *value = pcap_spi.rx_buf; + + spin_unlock(&pcap_spi.lock); + + return ret; + +} +EXPORT_SYMBOL_GPL(ezx_pcap_read); + +int ezx_pcap_bit_set(u_int32_t sspPcapBit, u_int8_t to) +{ + int ret; + u_int32_t tmp; + u_int32_t bit = (sspPcapBit & SSP_PCAP_REGISTER_VALUE_MASK); + u_int8_t reg_num = (sspPcapBit & SSP_PCAP_REGISTER_ADDRESS_MASK) + >> SSP_PCAP_REGISTER_ADDRESS_SHIFT; + + ret = ezx_pcap_read(reg_num, &tmp); + if (ret < 0) + return ret; + + if (to == 0) + tmp &= ~bit; + else + tmp |= bit; + + return ezx_pcap_write(reg_num, tmp); +} +EXPORT_SYMBOL_GPL(ezx_pcap_bit_set); + +int ezx_pcap_read_bit(u_int32_t bit) +{ + int ret; + u_int32_t tmp; + u_int8_t reg_num = (bit & SSP_PCAP_REGISTER_ADDRESS_MASK) + >> SSP_PCAP_REGISTER_ADDRESS_SHIFT; + + ret = ezx_pcap_read(reg_num, &tmp); + if (ret < 0) + return ret; + + return tmp & (bit & SSP_PCAP_REGISTER_VALUE_MASK); +} +EXPORT_SYMBOL_GPL(ezx_pcap_read_bit); + +int ezx_pcap_vibrator_level() +{ + /* FIXME */ +} +EXPORT_SYMBOL_GPL(ezx_pcap_vibrator_level); + + +static int ezx_pcap_init(void) +{ + /* initialize our transfer structure */ + memset(&pcap_spi, 0, sizeof(pcap_spi)); + pcap_spi.transfer.tx_buf = &pcap_spi.tx_buf; + pcap_spi.transfer.rx_buf = &pcap_spi.rx_buf; + pcap_spi.transfer.len = 4; + + return 0; + + /* FIXME: resolve the spi_master and configure it apropriately */ + + /* initialize registers */ + /* FIXME: this should be board-level, not chip-level */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_ISR_USB4VI, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_USB4VM, 0); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_ISR_USB1VI, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_MSR_USB1VM, 0); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_AUD_RX_AMPS_A1CTRL, 1); + ezx_pcap_vibrator_level(PCAP_VIBRATOR_VOLTAGE_LEVEL3); + ezx_pcap_vibrator_level(PCAP_VIBRATOR_VOLTAGE_LEVEL3); + + /* set SW1 sleep to keep SW1 1.3v in sync mode */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_SW1_MODE10, 0); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_SW1_MODE11, 0); + /* SW1 active in sync mode */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_SW1_MODE00, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_SW1_MODE01, 0); + /* at SW1 -core voltage to 1.30V */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_SW10_DVS, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_SW11_DVS, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_SW12_DVS, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_SW13_DVS, 0); + + /* when STANDY2 PIN ACTIVE (high) set V3-- sram V8 -- pll off */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_VREG2_V3_STBY, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_VREG2_V3_LOWPWR, 0); + + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_VREG2_V8_STBY, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_VREG2_V8_LOWPWR, 0); + + /* when STANDY2 PIN ACTIVE (high) set V4-- lcd only for e680 V6 --- + * camera for e680 */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_VREG2_V4_STBY, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_VREG2_V4_LOWPWR, 1); + + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_VREG2_V6_STBY, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_VREG2_V6_LOWPWR, 0); + + /* set Vc to low power mode when AP sleep */ + //SSP_PCAP_bit_set( SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_VC_STBY); + + /* set VAUX2 to voltage 2.775V and low power mode when AP sleep */ + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_AUX_VREG_VAUX2_1, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_AUX_VREG_VAUX2_0, 0); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_VAUX2_STBY, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_LOWPWR_CTRL_VAUX2_LOWPWR, 1); + ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_AUX_VREG_VAUX2_EN, 1); + +#ifdef FIXME + PGSR(GPIO34_TXENB) |= GPIO_bit(GPIO34_TXENB); + if(SSP_PCAP_BIT_ONE == SSP_PCAP_get_bit_from_PCAP(SSP_PCAP_ADJ_BIT_PSTAT_USBDET_4V )) + { + accessory_bus_detect_handler(ACCESSORY_DEVICE_USB_PORT,ACCESSORY_DEVICE_STATUS_ATTACHED,NULL); + } + else + { + accessory_bus_detect_handler(ACCESSORY_DEVICE_USB_PORT,ACCESSORY_DEVICE_STATUS_DETACHED,NULL); + } +#endif + return 0; +} + + +/* MMC/SD specific functions */ + +int ezx_pcap_mmcsd_power(int on) +{ + if (on) { +#if defined(CONFIG_PXA_EZX_E680) + return ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_AUX_VREG_VAUX2_EN, 1); +#else + return ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_AUX_VREG_VAUX3_EN, 1); +#endif + } else { +#if defined(CONFIG_PXA_EZX_E680) + return ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_AUX_VREG_VAUX2_EN, 0); +#else + return ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_AUX_VREG_VAUX3_EN, 0); +#endif + } +} +EXPORT_SYMBOL_GPL(ezx_pcap_mmcsd_power); + +/* IRQ Handling */ + +/* Array indexed by BIT POSITION of PCAP register, returns IRQ number */ +static unsigned int pcap2irq[] = { + [0] = EZX_IRQ_ADCDONE, + [1] = EZX_IRQ_TS, + [2] = 0, /* 1HZ */ + [3] = 0, /* WI */ + [4] = 0, /* WI */ + [5] = 0, /* TODA */ + [6] = EZX_IRQ_USB4V, + [7] = 0, /* ONOFF */ + [8] = 0, /* ONOFF2 */ + [9] = EZX_IRQ_USB1V, + [10] = 0, /* MOBPORT */ + [11] = EZX_IRQ_MIC, + [12] = EZX_IRQ_HEADJACK, + [13] = 0, /* ST */ + [14] = 0, /* PC */ + [15] = 0, /* WARM */ + [16] = 0, /* EOL */ + [17] = 0, /* CLK */ + [18] = 0, /* SYS_RST */ + [19] = 0, + [20] = EZX_IRQ_ADCDONE2, + [21] = 0, /* SOFT_RESET */ + [22] = 0, /* MNEXB */ +}; + +/* Array indexed by IRQ NUMBER, returns PCAP absolute value */ +static unsigned int irq2pcap[] = { + [EZX_IRQ_ADCDONE] = SSP_PCAP_ADJ_BIT_ISR_ADCDONEI, + [EZX_IRQ_TS] = SSP_PCAP_ADJ_BIT_ISR_TSI, + [EZX_IRQ_USB4V] = SSP_PCAP_ADJ_BIT_ISR_USB4VI, + [EZX_IRQ_USB1V] = SSP_PCAP_ADJ_BIT_ISR_USB1VI, + [EZX_IRQ_HEADJACK] = SSP_PCAP_ADJ_BIT_ISR_A1I, + [EZX_IRQ_MIC] = SSP_PCAP_ADJ_BIT_ISR_MB2I, + [EZX_IRQ_ADCDONE2] = SSP_PCAP_ADJ_BIT_ISR_ADCDONE2I, +}; + +static void pcap_ack_irq(unsigned int irq) +{ + DEBUGP("%s: %u\n", __FUNCTION__, irq); + ezx_pcap_write(SSP_PCAP_ADJ_ISR_REGISTER, irq2pcap[irq]); +} + +static void pcap_mask_irq(unsigned int irq) +{ + u_int32_t reg; + + DEBUGP("%s: %u\n", __FUNCTION__, irq); + + /* this needs to be atomic... but we're not on SMP so it is */ + ezx_pcap_read(SSP_PCAP_ADJ_MSR_REGISTER, ®); + reg |= irq2pcap[irq]; + ezx_pcap_write(SSP_PCAP_ADJ_MSR_REGISTER, reg); +} + +static void pcap_unmask_irq(unsigned int irq) +{ + u_int32_t tmp; + DEBUGP("%s: %u\n", __FUNCTION__, irq); + + /* this needs to be atomic... but we're not on SMP so it is */ + ezx_pcap_read(SSP_PCAP_ADJ_MSR_REGISTER, &tmp); + tmp &= ~irq2pcap[irq]; + ezx_pcap_write(SSP_PCAP_ADJ_MSR_REGISTER, tmp); +} + +static struct irqchip pcap_chip = { + .ack = pcap_ack_irq, + .mask = pcap_mask_irq, + .unmask = pcap_unmask_irq, +}; + +/* handler for interrupt received from PCAP via GPIO */ +static void pcap_irq_demux_handler(unsigned int irq, struct irqdesc *desc, + struct pt_regs *regs) +{ + int i; + const unsigned int cpu = smp_processor_id(); + u_int32_t reg; + + DEBUGP("%s(%u,,) entered\n", __FUNCTION__, irq); + + desc->triggered = 1; + + if (unlikely(desc->running || desc->disable_depth)) + goto running; + + //desc->chip->ack(irq); + desc->running = 1; + kstat_cpu(cpu).irqs[irq]++; + + do { + if (desc->pending && !desc->disable_depth) { + DEBUGP("dealing with pending IRQ, unmasking\n"); + desc->pending = 0; + desc->chip->unmask(irq); + } + + ezx_pcap_read(SSP_PCAP_ADJ_ISR_REGISTER, ®); + DEBUGP("%s: ISR=0x%08x\n", __FUNCTION__, reg); + desc->chip->ack(irq); + + for (i = ARRAY_SIZE(pcap2irq)-1; i >= 0; i--) { + unsigned int irq = pcap2irq[i]; + if (irq == 0) + continue; + + if (reg & (1 << i)) { + struct irqdesc *subdesc; + DEBUGP("found irq %u\n", irq); + subdesc = irq_desc + irq; + desc_handle_irq(irq, subdesc, regs); + } + } + + } while (desc->pending && !desc->disable_depth); + + desc->running = 0; + return; + +running: + DEBUGP("irq busy (running=%u, disable_depth=%d), masking it off\n", + desc->running, desc->disable_depth); + desc->pending = 1; + desc->chip->mask(irq); + desc->chip->ack(irq); +} + + +/* SPI protocol driver initialization */ + +static struct resource pcap_ts_resources[] = { + { + .start = EZX_IRQ_ADCDONE2, + .end = EZX_IRQ_ADCDONE2, + .flags = IORESOURCE_IRQ, + }, + { + .start = EZX_IRQ_TS, + .end = EZX_IRQ_TS, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device pcap_ts_device = { + .name = "pcap-ts", + .id = -1, + .resource = &pcap_ts_resources, + .num_resources = ARRAY_SIZE(pcap_ts_resources), +}; + +struct ezx_pcap { + int foo; +}; + +static int __devinit ezx_pcap_probe(struct spi_device *spi) +{ + unsigned int ret, irq; + struct ezx_pcap *chip; + + printk("%s entered\n", __FUNCTION__); + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return ENOMEM; + + dev_set_drvdata(&spi->dev, chip); + + ret = ezx_pcap_init(); + if (ret < 0) { + printk("error %d during ezx_pcap_init()\n", ret); + return ret; + } + + pcap_spi_dev = spi; + + set_irq_type(IRQ_GPIO1, IRQT_RISING); + /* set up interrupt demultiplexing code for PCAP2 irqs */ + for (irq = EZX_IRQ(0); irq <= EZX_IRQ(6); irq++) { + set_irq_chip(irq, &pcap_chip); + set_irq_handler(irq, do_edge_IRQ); + set_irq_flags(irq, IRQF_VALID); + } + set_irq_chained_handler(IRQ_GPIO1, pcap_irq_demux_handler); + + platform_device_register(&pcap_ts_device); + + return 0; +} + +static int __devexit ezx_pcap_remove(struct spi_device *spi) +{ + int irq; + struct ezx_pcap *chip = dev_get_drvdata(&spi->dev); + + printk("%s entered\n", __FUNCTION__); + + pcap_spi_dev = NULL; + + /* remove interrupt demultiplexing code for PCAP2 irqs */ + set_irq_chained_handler(IRQ_GPIO1, NULL); + + for (irq = EZX_IRQ(0); irq <= EZX_IRQ(6); irq++) { + set_irq_chip(irq, NULL); + set_irq_handler(irq, NULL); + set_irq_flags(irq, 0); + } + + dev_set_drvdata(&spi->dev, NULL); + + kfree(chip); + + return 0; +} + +static struct spi_driver ezx_pcap_driver = { + .driver = { + .name = "ezx-pcap", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + + .probe = ezx_pcap_probe, + .remove = __devexit_p(ezx_pcap_remove), +}; + + +static int __init pcap_init(void) +{ + return spi_register_driver(&ezx_pcap_driver); +} + +static void __exit pcap_fini(void) +{ + spi_unregister_driver(&ezx_pcap_driver); +} + +module_init(pcap_init); +module_exit(pcap_fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("SPI Driver for Motorola PCAP2"); + Index: linux-2.6.16.13-ezx5/arch/arm/mach-pxa/ezx.c =================================================================== --- linux-2.6.16.13-ezx5.orig/arch/arm/mach-pxa/ezx.c 2006-05-18 19:14:35.000000000 +0200 +++ linux-2.6.16.13-ezx5/arch/arm/mach-pxa/ezx.c 2006-05-19 02:35:45.000000000 +0200 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include "generic.h" #include @@ -131,7 +133,7 @@ pxa_gpio_mode(GPIO110_MMCDAT2_MD); pxa_gpio_mode(GPIO111_MMCDAT3_MD); - SSP_PCAP_MMCSD_poweron(); + ezx_pcap_mmcsd_power(1); ezx_mci_platform_data.detect_delay = msecs_to_jiffies(250); @@ -217,13 +219,13 @@ if (vdd <= MMC_VDD_360) SSP_PCAP_MMCSD_voltage(mmc_voltage[vdd]); - SSP_PCAP_MMCSD_poweron(); + ezx_pcap_mmcsd_power(1); } static void ezx_mci_exit(struct device *dev, void *data) { printk("%s entered\n", __FUNCTION__); - SSP_PCAP_MMCSD_poweroff(); + ezx_pcap_mmcsd_power(0); free_irq(0x49, data); } @@ -515,17 +517,58 @@ /* touch screen */ +/* SPI master devices */ -/* SSP device */ +static int ezx_spi_init(unsigned int num) +{ + switch (num) { + case 1: + pxa_gpio_mode(GPIO24_SFRM_MD); + pxa_gpio_mode(GPIO25_STXD_MD); + pxa_gpio_mode(GPIO26_SRXD_MD); + pxa_gpio_mode(GPIO29_SCLK_MD); + break; + case 2: + pxa_gpio_mode(GPIO22_SCLK2_MD); + pxa_gpio_mode(GPIO37_SFRM2_MD); + pxa_gpio_mode(GPIO38_STXD2_MD); + pxa_gpio_mode(GPIO88_SRXD2_MD); + break; + case 3: + pxa_gpio_mode(GPIO52_SCLK3_MD); + pxa_gpio_mode(GPIO83_SFRM3_MD); + pxa_gpio_mode(GPIO81_STXD3_MD); + pxa_gpio_mode(GPIO89_SRXD3_MD); + break; + default: + return -ENODEV; + } -static struct platform_device ezx_ssp_pcap_device = { - .name = "ezx-ssp-pcap", - .dev = { - //.parent = , - }, - .id = -1, + return 0; +} + +static struct pxa_spi_data ezx_spi_data = { + .init = &ezx_spi_init, }; +/* SPI/SSP controller devices */ + +static struct spi_board_info spi_board_info[] __initdata = { + { + .modalias = "ezx-pcap", + .max_speed_hz = 13000000, + .bus_num = 1, + .irq = IRQ_GPIO1, + }, { + .modalias = "ezx-snd", + .max_speed_hz = 8000000, + .bus_num = 2, + }, { + .modalias = "ezx-snd", + .max_speed_hz = 8000000, + .bus_num = 3, + }, +}; static int step = FIRST_STEP; void handshake(void) @@ -727,7 +770,6 @@ static struct platform_device *devices[] __initdata = { &ezx_bp_device, - &ezx_ssp_pcap_device, }; static void __init @@ -813,6 +855,16 @@ //SSP_PCAP_bit_set(SSP_PCAP_ADJ_BIT_BUSCTRL_VUSB_MSTR_EN); platform_add_devices(devices, ARRAY_SIZE(devices)); + +#ifdef CONFIG_PXA_SPI + /* register all three SPI busses */ + pxa_register_spi(1, &ezx_spi_data); + //pxa_register_spi(2, &ezx_spi_data); + //pxa_register_spi(3, &ezx_spi_data); + + /* register information about SPI slaves attached to SPI */ + spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); +#endif } MACHINE_START(EZX, "Motorola Ezx Platform") Index: linux-2.6.16.13-ezx5/arch/arm/mach-pxa/spi.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.16.13-ezx5/arch/arm/mach-pxa/spi.c 2006-05-18 19:20:55.000000000 +0200 @@ -0,0 +1,506 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* */ +struct pxa_master { + struct workqueue_struct *workqueue; + struct work_struct work; + + atomic_t busy; + spinlock_t lock; + struct list_head queue; + + struct spi_master *master; + + int port; +}; + +static void ssp_enable(unsigned int port) +{ + SSCR0_P(port) |= SSCR0_SSE; +} + +static void ssp_disable(unsigned int port) +{ + SSCR0_P(port) &= ~SSCR0_SSE; +} + +/* low level utility functions, mainly copied from arch/arm/mach-pxa/ssp.c */ +static int ssp_config(unsigned int port, u_int32_t mode, u_int32_t flags, + u_int32_t psp_flags, u_int32_t speed) +{ + SSCR1_P(port) = flags; + SSCR0_P(port) = 0; + SSCR0_P(port) = (speed | mode); + SSPSP_P(port) = psp_flags; + + return 0; +} + +static int ssp_write_word(unsigned int port, u32 data) +{ + while (!(SSSR_P(port) & SSSR_TNF)) + cpu_relax(); + + SSDR_P(port) = data; + return 0; +} + +static u_int32_t ssp_read_word(unsigned int port) +{ + while (!(SSSR_P(port) & SSSR_RNE)) + cpu_relax(); + + return SSDR_P(port); +} + +static irqreturn_t pxa_spi_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct pxa_master *pm = (struct pxa_master *) dev_id; + struct spi_master *master = pm->master; + unsigned int status = SSSR_P(pm->port); + + SSSR_P(pm->port) = status; /* clear status bits */ + + if (status & SSSR_ROR) + printk(KERN_WARNING "SPI(%d): receiver overrun\n", pm->port); + if (status & SSSR_TUR) + printk(KERN_WARNING "SPI(%d): transmitter underrun\n", + pm->port); + + if (status & SSSR_BCE) + printk(KERN_WARNING "SPI(%d): bit count error\n", pm->port); + + return IRQ_HANDLED; +} + +static int pxa_spi_setup(struct spi_device *spi) +{ + struct pxa_master *pm; + u_int32_t mode, flags, div; + + if (!spi->max_speed_hz) + return -EINVAL; + + if (spi->bits_per_word == 0) + spi->bits_per_word = 32; + + if (spi->bits_per_word < 4 || + spi->bits_per_word > 32) + return -EINVAL; + + pm = spi_master_get_devdata(spi->master); + + mode = SSCR0_Motorola | SSCR0_SSE; + flags = SSCR1_TxTresh(1) | SSCR1_RxTresh(1); //| SSCR1_RIE; + + /* clock phase and polarity */ + if (spi->mode & SPI_CPHA) + flags |= SSCR1_SPH; + if (spi->mode & SPI_CPOL) + flags |= SSCR1_SPO; + + /* word size */ + if (spi->bits_per_word <= 16) + mode |= SSCR0_DataSize(spi->bits_per_word); + else + mode |= SSCR0_EDSS | SSCR0_DataSize(spi->bits_per_word >> 1); + +#define PXA27x_SPI_CLOCK (13*1000*1000) + div = PXA27x_SPI_CLOCK / spi->max_speed_hz; + + return ssp_config(pm->port, mode, flags, 0, SSCR0_SerClkDiv(div)); + return 0; +} + +/* just add an SPI transfer to the workqueue */ +static int pxa_spi_transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct pxa_master *pm; + unsigned long flags; + + pm = spi_master_get_devdata(spi->master); + + spin_lock_irqsave(&pm->lock, flags); + + list_add_tail(&msg->queue, &pm->queue); + queue_work(pm->workqueue, &pm->work); + + spin_unlock_irqrestore(&pm->lock, flags); + + return 0; +} + +static int pxa_spi_transfer_x(struct spi_device *spi, struct spi_transfer *t); + +/* the actualy workqueue dequeueing workhorse */ +static void pxa_spi_work(void *_pxa_master) +{ + struct pxa_master *pm = _pxa_master; + unsigned long flags; + + spin_lock_irqsave(&pm->lock, flags); + atomic_inc(&pm->busy); + + while (!list_empty(&pm->queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + int status = 0, cs_change = 0; + + m = container_of(pm->queue.next, struct spi_message, queue); + list_del_init(&m->queue); + spin_unlock_irqrestore(&pm->lock, flags); + + spi = m->spi; + cs_change = 1; + + list_for_each_entry(t, &m->transfers, transfer_list) { + +#ifdef LATER + if (cs_change) { + pm->chipselect(spi, BITBANG_CS_ACTIVE); + } + cs_change = t->cs_change; +#endif + + if (!t->tx_buf && !t->rx_buf && t->len) { + status = -EINVAL; + break; + } + + if (t->len) { + if (!m->is_dma_mapped) + t->rx_dma = t->tx_dma = 0; + status = pxa_spi_transfer_x(spi, t); + } + if (status != t->len) { + if (status > 0) + status = -EMSGSIZE; + break; + } + m->actual_length += status; + status = 0; + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (!cs_change) + continue; + if (t->transfer_list.next == &m->transfers) + break; + +#ifdef LATER + pm->chipselect(spi, BITBANG_CS_INACTIVE); +#endif + } + + m->status = status; + m->complete(m->context); + +#ifdef LATER + if (!(status == 0 && cs_change)) + pm->chipselect(spi, BITBANG_CS_INACTIVE); +#endif + + spin_lock_irqsave(&pm->lock, flags); + } + atomic_dec(&pm->busy); + spin_unlock_irqrestore(&pm->lock, flags); +} + +/* actually transfer a given message */ +static int pxa_spi_transfer_x(struct spi_device *spi, struct spi_transfer *t) +{ + struct spi_master *master = spi->master; + struct pxa_master *pm = spi_master_get_devdata(master); + unsigned int i, n; + + ssp_enable(pm->port); + + /* calculate number of bytes per fifo-write/read */ + n = spi->bits_per_word / 8; + if (spi->bits_per_word % 8) + n++; + + /* FIXME: What about endianness? */ + for (i = 0; i < t->len; i += n) { + u_int32_t tx, rx; + const unsigned char *tx_buf = t->tx_buf; + unsigned char *rx_buf = t->rx_buf; + switch (n) { + case 1: + tx = tx_buf[i]; + ssp_write_word(pm->port, tx); + rx = ssp_read_word(pm->port); + rx_buf[i] = rx & 0xff; + rx_buf[i+1] = rx >> 8; + break; + case 3: + tx = tx_buf[i] | tx_buf[i+1] << 8 | tx_buf[i+2] << 16; + ssp_write_word(pm->port, tx); + rx = ssp_read_word(pm->port); + rx_buf[i] = rx & 0xff; + rx_buf[i+1] = rx >> 8; + rx_buf[i+2] = rx >> 16; + break; + case 4: + tx = tx_buf[i] | tx_buf[i+1] << 8; + tx |= tx_buf[i+2] << 16 | tx_buf[i+3] << 24; + ssp_write_word(pm->port, tx); + rx = ssp_read_word(pm->port); + rx_buf[i] = rx & 0xff; + rx_buf[i+1] = (rx >> 8) & 0xff; + rx_buf[i+2] = (rx >> 16) & 0xff; + rx_buf[i+3] = (rx >> 24) & 0xff; + break; + } + } + //ssp_disable(pm->port); + + return 0; +} + +int spi_sync_nosleep_x(struct spi_device *spi, struct spi_transfer *t) +{ + return pxa_spi_transfer_x(spi, t); +} +EXPORT_SYMBOL(spi_sync_nosleep_x); + +static void pxa_spi_cleanup(const struct spi_device *spi) +{ + //FIXME; +} + +/* non-standard really-synchronous non-sleeping SPI handling */ +static int spi_sync_xfer_nosleep(struct spi_device *spi, struct spi_message *m) +{ + //return pxa_spi_transfer_x(spi, x); + return -EINVAL; +} + +static int pxa_spi_probe(struct platform_device *pdev) +{ + struct resource *mem_res; + struct pxa_master *pm; + struct spi_master *master; + int ret = -ENOMEM; + + pm = kzalloc(sizeof(*pm), GFP_KERNEL); + if (!pm) + return ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*pm)); + if (!master) + goto out_pm; + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + ret = -EINVAL; + goto out_mem; + } + + /* request register memory region */ + if (!request_mem_region(mem_res->start, mem_res->end - mem_res->start, + "pxa-spi")) { + ret = -EBUSY; + goto out_master; + } + + ret = request_irq(platform_get_irq(pdev, 0), pxa_spi_interrupt, + 0, "pxa-spi", pm); + if (ret < 0) + goto out_mem; + + pm->master = master; + /* FIXME: this is still PXA27x specific */ + switch (mem_res->start) { + case 0x41000000: + pm->port = 1; + break; + case 0x47000000: + pm->port = 2; + break; + case 0x49000000: + pm->port = 3; + break; + default: + ret = -EINVAL; + goto out_irq; + } + INIT_WORK(&pm->work, pxa_spi_work, pm); + spin_lock_init(&pm->lock); + INIT_LIST_HEAD(&pm->queue); + + pm->workqueue = create_singlethread_workqueue( + master->cdev.dev->bus_id); + if (!pm->workqueue) { + printk("can't create workqueue\n"); + ret = -EBUSY; + goto out_irq; + } + + master->cleanup = &pxa_spi_cleanup; + master->setup = &pxa_spi_setup; + master->transfer = &pxa_spi_transfer; + master->transfer_nosleep = &spi_sync_xfer_nosleep; + master->bus_num = pm->port; + spi_master_set_devdata(master, pm); + + /* FIXME: this is still PXA27x specific */ + if (pm->port == 1) + pxa_set_cken(CKEN23_SSP1, 1); + else if (pm->port == 2) + pxa_set_cken(CKEN3_SSP2, 1); + else + pxa_set_cken(CKEN4_SSP3, 1); + + ret = spi_register_master(master); + if (ret < 0) { + printk("failing to register master\n"); + goto out_workqueue; + } + + platform_set_drvdata(pdev, pm); + + return 0; + +out_workqueue: + destroy_workqueue(pm->workqueue); +out_irq: + free_irq(platform_get_irq(pdev, 0), &pxa_spi_interrupt); +out_mem: + release_mem_region(mem_res->start, mem_res->end-mem_res->start); +out_master: + spi_master_put(master); +out_pm: + kfree(pm); + + return ret; +} + +static int pxa_spi_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver pxa_spi_driver = { + .probe = pxa_spi_probe, + .remove = pxa_spi_remove, + .driver = { + .name = "pxa-spi", + }, +}; + +static int __init pxa_spi_init(void) +{ + return platform_driver_register(&pxa_spi_driver); +} + +static void __exit pxa_spi_fini(void) +{ + platform_driver_unregister(&pxa_spi_driver); +} + +module_init(pxa_spi_init); +module_exit(pxa_spi_fini); + +/* SPI/SSP master (controller) platform devices, registered by board-level init + * code calling pxa_register_spi() */ + +/* FIXME: this is still PXA27x specific */ +static struct resource pxa_spi1_resources[] = { + { + .start = 0x41000000, //SSCR0_P1 + .end = 0x4100003f, //SSACD_P1 + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_SSP, + .end = IRQ_SSP, + .flags = IORESOURCE_IRQ, + }, +}; +static struct resource pxa_spi2_resources[] = { + { + .start = 0x41700000, //SSCR0_P2, + .end = 0x4170003f, //SSACD_P2, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_SSP2, + .end = IRQ_SSP2, + .flags = IORESOURCE_IRQ, + }, +}; +static struct resource pxa_spi3_resources[] = { + { + .start = 0x41900000, //SSCR0_P3, + .end = 0x4190003f, //SSACD_P3, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_SSP3, + .end = IRQ_SSP3, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device pxa_spi_devices[] = { + { + .name = "pxa-spi", + .dev = { + }, + .id = -1, + .resource = pxa_spi1_resources, + .num_resources = ARRAY_SIZE(pxa_spi1_resources), + },{ + .name = "pxa-spi", + .dev = { + }, + .id = -1, + .resource = pxa_spi2_resources, + .num_resources = ARRAY_SIZE(pxa_spi2_resources), + },{ + .name = "pxa-spi", + .dev = { + }, + .id = -1, + .resource = pxa_spi3_resources, + .num_resources = ARRAY_SIZE(pxa_spi3_resources), + }, +}; + +void pxa_register_spi(unsigned int n, struct pxa_spi_data *pdata) +{ + if (n < 1 || n > 3) { + printk(KERN_WARNING "Board init code tries to use non-" + "existing SPI Bus %u\n", n); + return; + } + + /* n = 1..3, we need 0..2 */ + n--; + + pxa_spi_devices[n].dev.platform_data = pdata; + platform_device_register(&pxa_spi_devices[n]); + + /* Setup GPIO alternate functions */ + if (pdata->init) + pdata->init(n); +} +EXPORT_SYMBOL_GPL(pxa_register_spi); + +MODULE_DESCRIPTION("New PXA SPI/SSP driver"); +MODULE_AUTHOR("Harald Welte "); +MODULE_LICENSE("GPL"); Index: linux-2.6.16.13-ezx5/include/asm-arm/arch-pxa/irqs.h =================================================================== --- linux-2.6.16.13-ezx5.orig/include/asm-arm/arch-pxa/irqs.h 2006-05-18 19:14:35.000000000 +0200 +++ linux-2.6.16.13-ezx5/include/asm-arm/arch-pxa/irqs.h 2006-05-18 19:20:55.000000000 +0200 @@ -176,7 +176,8 @@ #elif defined(CONFIG_SHARP_LOCOMO) #define NR_IRQS (IRQ_LOCOMO_SPI_TEND + 1) #elif defined(CONFIG_ARCH_LUBBOCK) || \ - defined(CONFIG_MACH_MAINSTONE) + defined(CONFIG_MACH_MAINSTONE) || \ + defined(CONFIG_PXA_EZX) #define NR_IRQS (IRQ_BOARD_END) #else #define NR_IRQS (IRQ_BOARD_START) @@ -217,3 +218,13 @@ #define IRQ_LOCOMO_GPIO_BASE (IRQ_BOARD_START + 1) #define IRQ_LOCOMO_LT_BASE (IRQ_BOARD_START + 2) #define IRQ_LOCOMO_SPI_BASE (IRQ_BOARD_START + 3) + +/* EZX Interrupts (CONFIG_EZX) */ +#define EZX_IRQ(x) (IRQ_BOARD_START + (x)) +#define EZX_IRQ_ADCDONE EZX_IRQ(0) /* PCAP */ +#define EZX_IRQ_TS EZX_IRQ(1) /* PCAP */ +#define EZX_IRQ_USB4V EZX_IRQ(2) /* PCAP */ +#define EZX_IRQ_USB1V EZX_IRQ(3) /* PCAP */ +#define EZX_IRQ_HEADJACK EZX_IRQ(4) /* PCAP */ +#define EZX_IRQ_MIC EZX_IRQ(5) /* PCAP */ +#define EZX_IRQ_ADCDONE2 EZX_IRQ(6) /* PCAP */ Index: linux-2.6.16.13-ezx5/include/asm-arm/arch-pxa/pxa-regs.h =================================================================== --- linux-2.6.16.13-ezx5.orig/include/asm-arm/arch-pxa/pxa-regs.h 2006-05-18 19:14:35.000000000 +0200 +++ linux-2.6.16.13-ezx5/include/asm-arm/arch-pxa/pxa-regs.h 2006-05-18 19:20:55.000000000 +0200 @@ -1374,6 +1374,7 @@ #define GPIO18_RDY_MD (18 | GPIO_ALT_FN_1_IN) #define GPIO19_DREQ1_MD (19 | GPIO_ALT_FN_1_IN) #define GPIO20_DREQ0_MD (20 | GPIO_ALT_FN_1_IN) +#define GPIO22_SCLK2_MD (22 | GPIO_ALT_FN_3_IN) #define GPIO23_SCLK_MD (23 | GPIO_ALT_FN_2_OUT) #define GPIO24_SFRM_MD (24 | GPIO_ALT_FN_2_OUT) #define GPIO25_STXD_MD (25 | GPIO_ALT_FN_2_OUT) @@ -1384,6 +1385,7 @@ #define GPIO28_BITCLK_OUT_I2S_MD (28 | GPIO_ALT_FN_1_OUT) #define GPIO29_SDATA_IN_AC97_MD (29 | GPIO_ALT_FN_1_IN) #define GPIO29_SDATA_IN_I2S_MD (29 | GPIO_ALT_FN_2_IN) +#define GPIO29_SCLK_MD (29 | GPIO_ALT_FN_3_IN) #define GPIO30_SDATA_OUT_AC97_MD (30 | GPIO_ALT_FN_2_OUT) #define GPIO30_USB_P3_2 (30 | GPIO_ALT_FN_3_OUT) #define GPIO30_SDATA_OUT_I2S_MD (30 | GPIO_ALT_FN_1_OUT) @@ -1402,7 +1404,9 @@ #define GPIO36_FFDCD_MD (36 | GPIO_ALT_FN_1_IN) #define GPIO36_USB_P2_4_MD (36 | GPIO_ALT_FN_1_OUT) #define GPIO37_FFDSR_MD (37 | GPIO_ALT_FN_1_IN) +#define GPIO37_SFRM2_MD (37 | GPIO_ALT_FN_2_IN) #define GPIO38_FFRI_MD (38 | GPIO_ALT_FN_1_IN) +#define GPIO38_STXD2_MD (38 | GPIO_ALT_FN_2_OUT) #define GPIO39_MMCCS1_MD (39 | GPIO_ALT_FN_1_OUT) #define GPIO39_FFTXD_MD (39 | GPIO_ALT_FN_2_OUT) #define GPIO39_USB_P2_6_MD (39 | GPIO_ALT_FN_1_OUT) @@ -1432,6 +1436,7 @@ #define GPIO51_HWRTS_MD (51 | GPIO_ALT_FN_1_OUT) #define GPIO51_nPIOW_MD (51 | GPIO_ALT_FN_2_OUT) #define GPIO52_nPCE_1_MD (52 | GPIO_ALT_FN_2_OUT) +#define GPIO52_SCLK3_MD (52 | GPIO_ALT_FN_2_OUT) #define GPIO53_MMCCLK_MD (53 | GPIO_ALT_FN_1_OUT) #define GPIO53_nPCE_2_MD (53 | GPIO_ALT_FN_2_OUT) #define GPIO53_FFRXD_MD (53 | GPIO_ALT_FN_1_IN) @@ -1477,13 +1482,17 @@ #define GPIO80_nCS_4_MD (80 | GPIO_ALT_FN_2_OUT) #define GPIO81_NSSP_CLK_OUT (81 | GPIO_ALT_FN_1_OUT) #define GPIO81_NSSP_CLK_IN (81 | GPIO_ALT_FN_1_IN) +#define GPIO81_STXD3_MD (81 | GPIO_ALT_FN_1_OUT) #define GPIO82_NSSP_FRM_OUT (82 | GPIO_ALT_FN_1_OUT) #define GPIO82_NSSP_FRM_IN (82 | GPIO_ALT_FN_1_IN) #define GPIO83_NSSP_TX (83 | GPIO_ALT_FN_1_OUT) #define GPIO83_NSSP_RX (83 | GPIO_ALT_FN_2_IN) +#define GPIO83_SFRM3_MD (83 | GPIO_ALT_FN_1_IN) #define GPIO84_NSSP_TX (84 | GPIO_ALT_FN_1_OUT) #define GPIO84_NSSP_RX (84 | GPIO_ALT_FN_2_IN) #define GPIO85_nPCE_1_MD (85 | GPIO_ALT_FN_1_OUT) +#define GPIO88_SRXD2_MD (88 | GPIO_ALT_FN_2_IN) +#define GPIO89_SRXD3_MD (89 | GPIO_ALT_FN_1_IN) #define GPIO90_USB_P3_5 (90 | GPIO_ALT_FN_2_IN) #define GPIO91_USB_P3_1 (91 | GPIO_ALT_FN_2_IN) #define GPIO92_MMCDAT0_MD (92 | GPIO_ALT_FN_1_OUT) Index: linux-2.6.16.13-ezx5/include/linux/spi/spi.h =================================================================== --- linux-2.6.16.13-ezx5.orig/include/linux/spi/spi.h 2006-05-18 19:14:35.000000000 +0200 +++ linux-2.6.16.13-ezx5/include/linux/spi/spi.h 2006-05-18 19:20:55.000000000 +0200 @@ -19,6 +19,8 @@ #ifndef __LINUX_SPI_H #define __LINUX_SPI_H +#include + /* * INTERFACES between SPI master-side drivers and SPI infrastructure. * (There's no SPI slave support for Linux yet...) @@ -205,6 +207,9 @@ int (*transfer)(struct spi_device *spi, struct spi_message *mesg); + /* bidirectional bulk transfer, synchronous, non-sleeping */ + int (*transfer_nosleep)(struct spi_device *spi, + struct spi_message *mesg); /* called on release() to free memory provided by spi_master */ void (*cleanup)(const struct spi_device *spi); }; @@ -485,6 +490,16 @@ return spi->master->transfer(spi, message); } +static inline int +spi_sync_nosleep(struct spi_device *spi, struct spi_message *msg) +{ + if (!spi->master->transfer_nosleep) + return -EIO; + + msg->spi = spi; + return spi->master->transfer_nosleep(spi, msg); +} + /*---------------------------------------------------------------------------*/ /* All these synchronous SPI transfer routines are utilities layered Index: linux-2.6.16.13-ezx5/arch/arm/mach-pxa/Kconfig =================================================================== --- linux-2.6.16.13-ezx5.orig/arch/arm/mach-pxa/Kconfig 2006-05-18 19:14:35.000000000 +0200 +++ linux-2.6.16.13-ezx5/arch/arm/mach-pxa/Kconfig 2006-05-19 02:35:49.000000000 +0200 @@ -2,7 +2,8 @@ config PXA_EZX bool - select PXA_SSP + #select PXA_SPI + #select PXA_EZX_PCAP menu "Intel PXA2xx Implementations" @@ -143,4 +144,13 @@ tristate help Enable support for PXA2xx SSP ports + +config PXA_SPI + tristate "SPI controller driver for PXA2xx" + help + Enable support for PXA2xx SPI + +config PXA_EZX_PCAP + tristate "SPI protocol driver for PCAP2" + endif Index: linux-2.6.16.13-ezx5/arch/arm/mach-pxa/Makefile =================================================================== --- linux-2.6.16.13-ezx5.orig/arch/arm/mach-pxa/Makefile 2006-05-18 19:14:35.000000000 +0200 +++ linux-2.6.16.13-ezx5/arch/arm/mach-pxa/Makefile 2006-05-19 02:35:49.000000000 +0200 @@ -29,6 +29,8 @@ # Misc features obj-$(CONFIG_PM) += pm.o sleep.o obj-$(CONFIG_PXA_SSP) += ssp.o +obj-$(CONFIG_PXA_SPI) += spi.o +obj-$(CONFIG_PXA_EZX_PCAP) += ezx-pcap.o ifeq ($(CONFIG_PXA27x),y) obj-$(CONFIG_PM) += standby.o Index: linux-2.6.16.13-ezx5/include/asm-arm/arch-pxa/spi.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.16.13-ezx5/include/asm-arm/arch-pxa/spi.h 2006-05-18 19:20:55.000000000 +0200 @@ -0,0 +1,11 @@ +#ifndef _PXA_SPI_H +#define _PXA_SPI_H + +/* board-specific data for one SPI controller */ +struct pxa_spi_data { + int (*init)(unsigned int n); +}; + +extern void pxa_register_spi(unsigned int n, struct pxa_spi_data *pdata); + +#endif Index: linux-2.6.16.13-ezx5/drivers/misc/ezx/Makefile =================================================================== --- linux-2.6.16.13-ezx5.orig/drivers/misc/ezx/Makefile 2006-05-18 19:14:35.000000000 +0200 +++ linux-2.6.16.13-ezx5/drivers/misc/ezx/Makefile 2006-05-18 19:20:55.000000000 +0200 @@ -1,5 +1,5 @@ -obj-$(CONFIG_PXA_EZX) += ssp_pcap_main.o ezx-ts.o ezx-button.o ezx-emu.o +obj-$(CONFIG_PXA_EZX) += ssp_pcap_main.o ezx-emu.o # ezx-ts.o ezx-button.o # obj-$(CONFIG_EZX) += keypad.o keypad-panasonic.o keypad-e398.o Index: linux-2.6.16.13-ezx5/drivers/misc/ezx/ssp_pcap_main.c =================================================================== --- linux-2.6.16.13-ezx5.orig/drivers/misc/ezx/ssp_pcap_main.c 2006-05-18 19:20:03.000000000 +0200 +++ linux-2.6.16.13-ezx5/drivers/misc/ezx/ssp_pcap_main.c 2006-05-18 19:20:55.000000000 +0200 @@ -1056,7 +1056,7 @@ pxa_gpio_mode(GPIO_PCAP_SEC_INT | GPIO_IN); set_GPIO_IRQ_edge(GPIO_PCAP_SEC_INT,GPIO_RISING_EDGE); - request_irq(IRQ_GPIO1,ssp_pcap_interrupt_routine,0,"PCAP request irq ",NULL); + //request_irq(IRQ_GPIO1,ssp_pcap_interrupt_routine,0,"PCAP request irq ",NULL); #ifdef PCAP_LOG_FFUART_OUT pcap_output_log_init(); #endif @@ -1272,6 +1272,10 @@ #endif return; } +EXPORT_SYMBOL(ssp_pcap_open); +EXPORT_SYMBOL(SSP_PCAP_bit_set); +EXPORT_SYMBOL(SSP_PCAP_bit_clean); + /*--------------------------------------------------------------------------- DESCRIPTION: @@ -1957,7 +1961,7 @@ /* 2 seconds protect timer */ /* better used it after linux 2.4.17 */ mod_timer(&ssp_tsi_timer,jiffies + SSP_PCAP_TS_KEEPER_TIMER); - ezx_ts_dataReadok_interrupt(0,NULL,NULL); + //ezx_ts_dataReadok_interrupt(0,NULL,NULL); } else { @@ -1986,7 +1990,7 @@ /* 2 seconds protect timer */ /* better used it after linux 2.4.17 */ mod_timer(&ssp_tsi_timer,jiffies + SSP_PCAP_TS_KEEPER_TIMER);; - ezx_ts_touch_interrupt(0,NULL,NULL); + //ezx_ts_touch_interrupt(0,NULL,NULL); } } else