diff options
author | Michael Lauer <mickey@vanille-media.de> | 2006-05-11 17:23:44 +0000 |
---|---|---|
committer | OpenEmbedded Project <openembedded-devel@lists.openembedded.org> | 2006-05-11 17:23:44 +0000 |
commit | 844bf825c75c21864c1ba911642d58021e08e9ed (patch) | |
tree | a4fc24b7330cd955c23787749f4cf751bf35dc57 /packages/linux/linux-ezx/pxa-spi.patch | |
parent | 2222e629a6bf3ebaafce55cfd533486ec9926887 (diff) |
add first stab at support for the Motorola A780 GSM phone
NOTE: for now, we use a dedicated linux-ezx kernel, but once this proves working,
we will probably unify with linux-openzaurus
Diffstat (limited to 'packages/linux/linux-ezx/pxa-spi.patch')
-rw-r--r-- | packages/linux/linux-ezx/pxa-spi.patch | 1041 |
1 files changed, 1041 insertions, 0 deletions
diff --git a/packages/linux/linux-ezx/pxa-spi.patch b/packages/linux/linux-ezx/pxa-spi.patch new file mode 100644 index 0000000000..c0f66a8246 --- /dev/null +++ b/packages/linux/linux-ezx/pxa-spi.patch @@ -0,0 +1,1041 @@ +Index: linux-2.6.16.5-ezx/arch/arm/mach-pxa/ezx-pcap.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16.5-ezx/arch/arm/mach-pxa/ezx-pcap.c 2006-05-04 00:38:11.000000000 +0200 +@@ -0,0 +1,344 @@ ++/* 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 <laforge@openezx.org> ++ */ ++ ++#include <linux/config.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/spi/spi.h> ++ ++#include <asm/arch/ezx-pcap.h> ++ ++/* 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; ++ ++ ++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<<SSP_PCAP_REGISTER_ADDRESS_SHIFT); ++ ++ /* we need to disable IRQ_GPIO1 because it's hardirq handler will ++ * in turn want to use SPI to get the real interrupt source */ ++ irq_disable(IRQ_GPIO1); ++ ret = spi_sync_nosleep_x(pcap_spi_dev, &pcap_spi.transfer); ++ irq_enable(IRQ_GPIO1); ++ ++ spin_unlock(&pcap_spi.lock); ++ ++ return ret; ++ ++} ++EXPORT_SYMBOL_GPL(ezx_pcap_write); ++ ++int ezx_pcap_read(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 = SSP_PCAP_REGISTER_READ_OP_BIT ++ | (reg_num<<SSP_PCAP_REGISTER_ADDRESS_SHIFT); ++ ++ /* we need to disable IRQ_GPIO1 because it's hardirq handler will ++ * in turn want to use SPI to get the real interrupt source */ ++ irq_disable(IRQ_GPIO1); ++ ret = spi_sync_nosleep_x(pcap_spi_dev, &pcap_spi.transfer); ++ irq_enable(IRQ_GPIO1) ++ ++ if (ret >= 0) ++ *value = pcap_spi.transfer.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) ++{ ++ u_int32_t sspPcapRegisterBitValue; ++ int ret; ++ u_int8_t reg_num = (sspPcapBit & SSP_PCAP_REGISTER_ADDRESS_MASK) ++ >> SSP_PCAP_REGISTER_ADDRESS_SHIFT; ++ ++ if (reg_num == SSP_PCAP_ADJ_ISR_REGISTER) ++ ssp_pcap_registerValue[reg_num] = 0; ++ ++ sspPcapRegisterBitValue = (sspPcapBit & SSP_PCAP_REGISTER_VALUE_MASK); ++ ++ if (to == 0) ++ ssp_pcap_registerValue[sspPcapRegister] |= sspPcapRegisterBitValue; ++ else ++ ssp_pcap_registerValue[sspPcapRegister] &= ~sspPcapRegisterBitValue; ++ ++ /* should care the return value */ ++ return ezx_pcap_write(num_reg, ssp_pcap_registerValue[num_reg]); ++} ++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); ++ ++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; ++ ++ /* 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); ++#ifdef FIXME ++ SSP_PCAP_V_VIB_level_set(PCAP_VIBRATOR_VOLTAGE_LEVEL3); ++ SSP_PCAP_V_VIB_level_set(PCAP_VIBRATOR_VOLTAGE_LEVEL3); ++#endif ++ ++ /* 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; ++} ++ ++int ezx_pcap_vibrator_level() ++{ ++ /* FIXME */ ++} ++EXPORT_SYMBOL_GPL(ezx_pcap_vibrator_level); ++ ++ ++/* MMC/SD specific functions */ ++ ++int ezx_pcap_mmcsd_power(int on) ++{ ++ if (on) ++ ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_AUX_VREG_VAUX2_EN, 1); ++ else ++ ezx_pcap_bit_set(SSP_PCAP_ADJ_BIT_AUX_VREG_VAUX2_EN, 0); ++} ++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, ++ [7] = EZX_IRQ_USB4V, ++ [10] = EZX_IRQ_USB1V, ++ [12] = EZX_IRQ_MIC, ++ [13] = EZX_IRQ_HEADJACK, ++}; ++ ++/* Array indexed by IRQ NUMBER, returns PCAP absolute value */ ++static unsigned int irq2pcap[] = { ++ [EZX_IRQ_ADCDONE] = SSP_PCAP_ADJ_BIT_ISR_ADCDONE2I, ++ [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, ++}; ++ ++static void pcap_ack_irq(unsigned int irq) ++{ ++ ezx_pcap_write(SSP_PCAP_ADJ_ISR_REGISTER, irq2pcap[irq]); ++} ++ ++static void pcap_mask_irq(unsigned int irq) ++{ ++ ezx_pcap_write(SSP_PCAP_ADJ_MSR_REGISTER, irq2pcap[irq]); ++} ++ ++static void pcap_unmask_irq(unsigned int irq) ++{ ++ u_int32_t tmp; ++ ++ /* 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; ++ u_int32_t reg; ++ ++ ezx_pcap_read(SSP_PCAP_ADJ_ISR_REGISTER, ®); ++ ++ for (i = 0; i < ARRAY_SIZE(pcap2irq); i++) { ++ unsigned int irq = pcap2irq[i]; ++ if (irq == 0) ++ continue; ++ if (reg & (1 << i)) ++ desc_handle_irq(irq, desc, regs); ++ } ++} ++ ++ ++/* SPI protocol driver initialization */ ++ ++static int __devinit ezx_pcap_probe(struct spi_device *spi) ++{ ++ unsigned int ret, irq; ++ struct ezx_pcap *chip; ++ ++ chip = kzalloc(sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return ENOMEM; ++ ++ dev_set_drvdata(&spi->dev, chip); ++ ++ ret = ezx_pcap_init(); ++ if (ret < 0) ++ return ret; ++ ++ /* set up interrupt demultiplexing code for PCAP2 irqs */ ++ for (irq = EZX_IRQ(0); irq <= EZX_IRQ(5); 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); ++ ++ return 0; ++} ++ ++static void __devexit ezx_pcap_remove(struct spi_device *spi) ++{ ++ /* remove interrupt demultiplexing code for PCAP2 irqs */ ++ set_irq_chained_handler(IRQ_GPIO1, NULL); ++ ++ for (irq = EZX_IRQ(0); irq <= EZX_IRQ(5); irq++) { ++ set_irq_chip(irq, NULL); ++ set_irq_handler(irq, NULL); ++ set_irq_flags(irq, 0); ++ } ++ // kfree ++} ++ ++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 <laforge@openezx.org>"); ++MODULE_DESCRIPTION("SPI Driver for Motorola PCAP2"); ++ +Index: linux-2.6.16.5-ezx/arch/arm/mach-pxa/ezx.c +=================================================================== +--- linux-2.6.16.5-ezx.orig/arch/arm/mach-pxa/ezx.c 2006-05-03 14:41:58.000000000 +0200 ++++ linux-2.6.16.5-ezx/arch/arm/mach-pxa/ezx.c 2006-05-03 14:49:25.000000000 +0200 +@@ -492,17 +492,61 @@ + + /* 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 { ++ .init = &ezx_spi_init, + }; + ++/* SPI/SSP controller devices */ ++ ++static struct spi_board_info spi_board_info[] __initdata = { ++ { ++ .modalias = "ezx-pcap", ++ .mode = SPI_MODE_0, ++ .max_speed_hz = /* FIXME */, ++ .bus_num = 1, ++ .irq = IRQ_GPIO1, ++ }, { ++ .modalias = "ezx-snd", ++ .mode = SPI_MODE_0, ++ .max_speed_hz = /* FIXME */, ++ .bus_num = 2, ++ }, { ++ .modalias = "ezx-snd", ++ .mode = SPI_MODE_0, ++ .max_speed_hz = /* FIXME */, ++ .bus_num = 3, ++ }, ++} + + static int step = FIRST_STEP; + void handshake(void) +@@ -704,7 +748,7 @@ + + static struct platform_device *devices[] __initdata = { + &ezx_bp_device, +- &ezx_ssp_pcap_device, ++ //&ezx_ssp_pcap_device, + }; + + static void __init +@@ -806,6 +850,14 @@ + #endif + + platform_add_devices(devices, ARRAY_SIZE(devices)); ++ ++ /* register all three SPI busses */ ++ pxa_register_spi(1, &pxa_spi_data); ++ //pxa_register_spi(2, &pxa_spi_data); ++ //pxa_register_spi(3, &pxa_spi_data); ++ ++ /* register information about SPI slaves attached to SPI */ ++ spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); + } + + MACHINE_START(EZX, "Motorola Ezx Platform") +Index: linux-2.6.16.5-ezx/arch/arm/mach-pxa/spi.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.16.5-ezx/arch/arm/mach-pxa/spi.c 2006-05-04 00:40:25.000000000 +0200 +@@ -0,0 +1,486 @@ ++#include <linux/config.h> ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/workqueue.h> ++#include <linux/platform_device.h> ++ ++#include <linux/spi/spi.h> ++ ++#include <asm/arch/pxa-regs.h> ++ ++/* board-specific data for one SPI controller */ ++struct pxa_spi_data { ++ int (init)(unsigned int n); ++}; ++ ++/* */ ++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) ++{ ++ SSCR0_P(port) = (dev->speed | dev->mode); ++ SSCR1_P(port) = dev->flags; ++ SSPSP_P(port) = dev->psp_flags; ++ ++ return 0; ++} ++ ++static int ssp_write_word(struct ssp_dev *dev, u32 data) ++{ ++ while (!(SSSR_P(dev->port) & SSSR_TNF)) ++ cpu_relax(); ++ ++ SSDR_P(dev->port) = data; ++ ++ return 0; ++} ++ ++static u_int32_t ssp_read_word(struct ssp_dev *dev) ++{ ++ while (!(SSSR_P(dev->port) & SSSR_RNE)) ++ cpu_relax(); ++ ++ return SSDR_P(dev->port); ++} ++ ++static irqreturn_t pxa_spi_interrupt(int irq, void *dev_id, ++ struct pt_regs *regs) ++{ ++ struct spi_master *master = (struct spi_master *) dev_id; ++ struct pxa_master *pm = master->private; ++ unsigned int status = SSSR_P(pm->port); ++ ++ SSSR_P(dev->port) = status; /* clear status bits */ ++ ++ if (status & SSSR_ROR) ++ pritnk(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 < 4 || ++ spi->bits_per_word > 32) ++ return -EINVAL; ++ ++ if (!spi->bits_per_word) ++ spi->bits_per_word = 8; ++ ++ pm = spi_master_get_devdata(spi->master); ++ ++ mode = SSCR0_Motorola; /* SPI, FIXME: other modes such as SSP! */ ++ flags = 0; ++ ++ /* clock phase and polarity */ ++ if (spi->mode & SPI_CPHA) ++ flags |= SSCR1_SPH; ++ if (spi->mode & SPI_CPOL) ++ flags |= SSCR1_SPO; ++ ++ /* word size */ ++ mode |= (spi->bits_per_word & 0xf); ++ if (spi->bits_per_word > 16) ++ mode |= SSCR0_EDSS; ++ ++#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)); ++} ++ ++/* 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(&m->queue, &pm->queue); ++ queue_work(pm->workqueue, &pm->work); ++ ++ spin_unlock_irqrestore(&pm->lock, flags); ++} ++ ++/* the actualy workqueue dequeueing workhorse */ ++static void pxa_spi_work(void *_pxa_master) ++{ ++ struct pxa_master *pm = _pxa_master; ++ unsigned long flags; ++ ++ spn_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; ++ ++ 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(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; ++ if (n == 1) { ++ tx = t->tx_buf[i]; ++ ssp_write_word(pm->port, tx); ++ rx = ssp_read_word(pm->port); ++ t->rx_buf[i] = rx & 0xff ++ t->rx_buf[i+1] = rx >> 8; ++ } else if (n == 3) { ++ tx = t->tx_buf[i] | t->tx_buf[i+1] << 8 | t->tx_buf[i+2] << 16; ++ ssp_write_word(pm->port, tx); ++ rx = ssp_read_word(pm->port); ++ t->rx_buf[i] = rx & 0xff; ++ t->rx_buf[i+1] = rx >> 8; ++ t->rx_buf[i+2] = rx >> 16; ++ } else { ++ tx = t->tx_buf[i] | t->tx_buf[i+1] << 8; ++ tx |= t->tx_buf[i+2] << 16 | t->tx_buf[i+3] << 24; ++ ssp_write_word(pm->port, tx); ++ rx = ssp_read_word(pm->port); ++ t->rx_buf[i] = rx & 0xff; ++ t->rx_buf[i+1] = rx >> 8; ++ t->rx_buf[i+2] = rx >> 16; ++ t->rx_buf[i+3] = rx >> 24; ++ } ++ } ++ ssp_disable(pm->port); ++ ++ return 0; ++} ++ ++static int pxa_spi_cleanup(struct spi_device *spi) ++{ ++ //FIXME; ++ return 0; ++} ++ ++/* non-standard really-synchronous non-sleeping SPI handling */ ++int spi_sync_xfer_nosleep(struct spi_device *spi, struct spi_transfer *x) ++{ ++ return pxa_spi_trasnfer_x(spi, x); ++} ++EXPORT_SYMBOL_GPL(spi_sync_xfer_nosleep); ++ ++void 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 err; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*pp)); ++ 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; ++ } ++ ++ if (!request_irq(platform_get_irq(pdev, 0), pxa_spi_interrupt, ++ 0, "pxa-spi", master)) { ++ ret = -EBUSY; ++ goto out_mem; ++ } ++ ++ pm->master = master; ++ /* FIXME: this is still PXA27x specific */ ++ switch (mem_res->start) { ++ case SSCR0_1: ++ pm->port = 1; ++ break; ++ case SSCR0_2: ++ pm->port = 2; ++ break; ++ case SSCR0_3: ++ 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) { ++ 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->private = pm; ++ ++ /* FIXME: this is still PXA27x specific */ ++ if (pm->port == 1) ++ pxa_set_cken(CKEN23_SSP1); ++ else if (pm->port == 2) ++ pxa_set_cken(CKEN3_SSP2); ++ else ++ pxa_set_cken(CKEN4_SSP3); ++ ++ ret = spi_register_master(master); ++ if (ret < 0) ++ 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)); ++out_mem: ++ release_mem_region(mem_res->start, mem_res->end-mem_res->start); ++out_master: ++ spi_free_master(master); ++out_pm: ++ kfree(pm); ++ ++ return ret; ++} ++ ++void pxa_spi_remove() ++{ ++} ++ ++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 = SSCR0_1, ++ .end = SSACD_1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = IRQ_SSP, ++ .end = IRQ_SSP, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++static struct resource pxa_spi2_resources[] = { ++ { ++ .start = SSCR0_2, ++ .end = SSACD_2, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = IRQ_SSP2, ++ .end = IRQ_SSP2, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++static struct resource pxa_spi3_resources[] = { ++ { ++ .start = SSCR0_3, ++ .end = SSACD_3, ++ .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, ++ .resources = pxa_spi1_resources, ++ .num_resourcs = ARRAY_SIZE(pxa_spi1_resources), ++ },{ ++ .name = "pxa-spi", ++ .dev = { ++ }, ++ .id = -1, ++ .resources = pxa_spi2_resources, ++ .num_resources = ARRAY_SIZE(pxa_spi2_resources), ++ },{ ++ .name = "pxa-spi", ++ .dev = { ++ }, ++ .id = -1; ++ .resources = 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_WARN, "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; ++ register_platform_device(&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 <laforge@openezx.org>"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.16.5-ezx/include/asm-arm/arch-pxa/irqs.h +=================================================================== +--- linux-2.6.16.5-ezx.orig/include/asm-arm/arch-pxa/irqs.h 2006-05-03 14:41:58.000000000 +0200 ++++ linux-2.6.16.5-ezx/include/asm-arm/arch-pxa/irqs.h 2006-05-03 14:42:00.000000000 +0200 +@@ -217,3 +217,12 @@ + #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_TSM EZX_IRQ(1) /* PCAP */ ++#define EZX_IRQ_USB4V EZX_IRQ(2) /* PCAP */ ++#define EZZ_IRQ_USB1V EZX_IRQ(3) /* PCAP */ ++#define EZX_IRQ_HEADJACK EZX_IRQ(4) /* PCAP */ ++#define EZX_IRQ_MIC EZX_IRQ(5) /* PCAP */ +Index: linux-2.6.16.5-ezx/include/asm/arch/pxa-regs.h +=================================================================== +--- linux-2.6.16.5-ezx.orig/include/asm/arch/pxa-regs.h 2006-05-03 14:41:58.000000000 +0200 ++++ linux-2.6.16.5-ezx/include/asm/arch/pxa-regs.h 2006-05-03 14:42:01.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 GPIO90_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.5-ezx/include/linux/spi/spi.h +=================================================================== +--- linux-2.6.16.5-ezx.orig/include/linux/spi/spi.h 2006-05-03 15:11:08.000000000 +0200 ++++ linux-2.6.16.5-ezx/include/linux/spi/spi.h 2006-05-04 00:38:42.000000000 +0200 +@@ -205,6 +205,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 +488,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 = master; ++ return spi->master->transfer_nosleep(spi, message); ++} ++ + /*---------------------------------------------------------------------------*/ + + /* All these synchronous SPI transfer routines are utilities layered |