diff options
Diffstat (limited to 'packages/linux/linux-rp-2.6.26/collie.patch')
-rw-r--r-- | packages/linux/linux-rp-2.6.26/collie.patch | 1883 |
1 files changed, 1883 insertions, 0 deletions
diff --git a/packages/linux/linux-rp-2.6.26/collie.patch b/packages/linux/linux-rp-2.6.26/collie.patch new file mode 100644 index 0000000000..00b7d881a5 --- /dev/null +++ b/packages/linux/linux-rp-2.6.26/collie.patch @@ -0,0 +1,1883 @@ +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index c7ad324..daa2e0a 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -998,7 +998,7 @@ config CPU_FREQ_SA1100 + + config CPU_FREQ_SA1110 + bool +- depends on CPU_FREQ && (SA1100_ASSABET || SA1100_CERF || SA1100_PT_SYSTEM3) ++ depends on CPU_FREQ && (SA1100_ASSABET || SA1100_CERF || SA1100_PT_SYSTEM3 || SA1100_COLLIE) + default y + + config CPU_FREQ_INTEGRATOR +diff --git a/arch/arm/mach-sa1100/dma.c b/arch/arm/mach-sa1100/dma.c +index e508028..36f726c 100644 +--- a/arch/arm/mach-sa1100/dma.c ++++ b/arch/arm/mach-sa1100/dma.c +@@ -39,7 +39,7 @@ typedef struct { + + static sa1100_dma_t dma_chan[SA1100_DMA_CHANNELS]; + +-static spinlock_t dma_list_lock; ++static DEFINE_SPINLOCK(dma_list_lock); + + + static irqreturn_t dma_irq_handler(int irq, void *dev_id) +diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c +index 9caed30..79e19bf 100644 +--- a/drivers/input/keyboard/locomokbd.c ++++ b/drivers/input/keyboard/locomokbd.c +@@ -265,6 +265,7 @@ static int __devinit locomokbd_probe(struct locomo_dev *dev) + for (i = 0; i < LOCOMOKBD_NUMKEYS; i++) + set_bit(locomokbd->keycode[i], input_dev->keybit); + clear_bit(0, input_dev->keybit); ++ locomo_writel(0, locomokbd->base + LOCOMO_KSC); + + /* attempt to get the interrupt */ + err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd); +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 9f93c29..33fc5d6 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -72,4 +72,10 @@ config MCP_UCB1200_TS + tristate "Touchscreen interface support" + depends on MCP_UCB1200 && INPUT + ++config MCP_COLLIE_TS ++ tristate "Touchscreen collie support" ++ depends on MCP_UCB1200 && INPUT && !MCP_UCB1200_TS ++ ---help--- ++ Driver for touchscreen on collie - sharp sl-5500. ++ + endmenu +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 33daa2f..0885ccd 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -16,7 +16,7 @@ obj-$(CONFIG_MCP) += mcp-core.o + obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o + obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o +- ++obj-$(CONFIG_MCP_COLLIE_TS) += collie-ts.o + ifeq ($(CONFIG_SA1100_ASSABET),y) + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o + endif +diff --git a/drivers/mfd/collie-ts.c b/drivers/mfd/collie-ts.c +new file mode 100644 +index 0000000..ddde5fc +--- /dev/null ++++ b/drivers/mfd/collie-ts.c +@@ -0,0 +1,449 @@ ++/* ++ * Touchscreen driver for UCB1x00-based touchscreens ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * Copyright (C) 2005 Pavel Machek ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * 21-Jan-2002 <jco@ict.es> : ++ * ++ * Added support for synchronous A/D mode. This mode is useful to ++ * avoid noise induced in the touchpanel by the LCD, provided that ++ * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. ++ * It is important to note that the signal connected to the ADCSYNC ++ * pin should provide pulses even when the LCD is blanked, otherwise ++ * a pen touch needed to unblank the LCD will never be read. ++ */ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/smp.h> ++#include <linux/smp_lock.h> ++#include <linux/sched.h> ++#include <linux/completion.h> ++#include <linux/delay.h> ++#include <linux/string.h> ++#include <linux/input.h> ++#include <linux/device.h> ++#include <linux/freezer.h> ++#include <linux/slab.h> ++#include <linux/kthread.h> ++ ++#include <asm/dma.h> ++#include <asm/semaphore.h> ++#include <asm/arch/collie.h> ++#include <asm/mach-types.h> ++ ++#include "ucb1x00.h" ++ ++struct ucb1x00_ts { ++ struct input_dev *idev; ++ struct ucb1x00 *ucb; ++ ++ wait_queue_head_t irq_wait; ++ struct task_struct *rtask; ++ u16 x_res; ++ u16 y_res; ++ ++ unsigned int adcsync:1; ++}; ++ ++static int adcsync; ++ ++/********************************** ++ ++ ................ ++ . . = 340 ++ . . ++ . ^. ++ . ^. ++ . ^. ++ . ^. ++ . . ++ . X. = 10 ++ . <<<<<<<< Y . ++ ................ ++ . Sharp =200 ++ . . ++ . - O - . ++ . . ++ ................ ++ ++**********************************/ ++ ++ ++static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) ++{ ++ struct input_dev *idev = ts->idev; ++ ++ input_report_abs(idev, ABS_X, x); ++ input_report_abs(idev, ABS_Y, y); ++ input_report_abs(idev, ABS_PRESSURE, pressure); ++ input_report_key(idev, BTN_TOUCH, 1); ++ input_sync(idev); ++} ++ ++static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) ++{ ++ struct input_dev *idev = ts->idev; ++ ++ input_report_abs(idev, ABS_PRESSURE, 0); ++ input_report_key(idev, BTN_TOUCH, 0); ++ input_sync(idev); ++} ++ ++/* ++ * Switch to interrupt mode. This set touchscreen to interrupt ++ * mode, so that chip is able to send interrupt. ++ */ ++static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | ++ UCB_TS_CR_MODE_INT); ++} ++ ++/* ++ * Switch to pressure mode, and read pressure. We don't need to wait ++ * here, since both plates are being driven. ++ * ++ * set_read_pressure() in sharp code ++ */ ++static inline void ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW | ++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); ++ ++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | ++ UCB_ADC_INP_AD2 | ++ UCB_ADC_SYNC_ENA); ++ udelay(100); ++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | ++ UCB_ADC_INP_AD2 | ++ UCB_ADC_SYNC_ENA | UCB_ADC_START); ++} ++ ++/* ++ * Switch to X position mode and measure Y plate. We switch the plate ++ * configuration in pressure mode, then switch to position mode. This ++ * gives a faster response time. Even so, we need to wait about 55us ++ * for things to stabilise. ++ */ ++static inline void ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); ++ ++ ++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | ++ UCB_ADC_INP_TSPY | UCB_ADC_SYNC_ENA); ++ udelay(100); ++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | ++ UCB_ADC_INP_TSPY | UCB_ADC_SYNC_ENA | ++ UCB_ADC_START); ++} ++ ++/* ++ * Switch to Y position mode and measure X plate. We switch the plate ++ * configuration in pressure mode, then switch to position mode. This ++ * gives a faster response time. Even so, we need to wait about 55us ++ * for things to stabilise. ++ */ ++static inline void ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); ++ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); ++ ++ ++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | ++ UCB_ADC_INP_TSPX | UCB_ADC_SYNC_ENA); ++ udelay(100); ++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr | ++ UCB_ADC_INP_TSPX | UCB_ADC_SYNC_ENA | ++ UCB_ADC_START); ++} ++ ++/* ++ * Switch to X plate resistance mode. Set MX to ground, PX to ++ * supply. Measure current. ++ */ ++static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); ++} ++ ++/* ++ * Switch to Y plate resistance mode. Set MY to ground, PY to ++ * supply. Measure current. ++ */ ++static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); ++} ++ ++/* ++ * This is a RT kernel thread that handles the ADC accesses ++ * (mainly so we can use semaphores in the UCB1200 core code ++ * to serialise accesses to the ADC). ++ */ ++static int ucb1x00_thread(void *_ts) ++{ ++ struct ucb1x00_ts *ts = _ts; ++ struct task_struct *tsk = current; ++ DECLARE_WAITQUEUE(wait, tsk); ++ int state; ++ ++ /* ++ * We could run as a real-time thread. However, thus far ++ * this doesn't seem to be necessary. ++ */ ++ ++ add_wait_queue(&ts->irq_wait, &wait); ++ ++ while (!kthread_should_stop()) { ++ unsigned int data[3]; ++ ++ for (state=0; state<3; state++) { ++ ++ ucb1x00_adc_enable(ts->ucb); ++ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_ADC, UCB_FALLING); ++ switch (state) { ++ /* Order matters here; last measurement seems to be more noisy then the ++ rest, and we care about pressure least */ ++ case 2: ucb1x00_ts_read_pressure(ts); ++ break; ++ case 0: ucb1x00_ts_read_ypos(ts); ++ break; ++ case 1: ucb1x00_ts_read_xpos(ts); ++ break; ++ } ++ /* wait for adc */ ++ try_to_freeze(); ++ schedule_timeout(1000 * HZ); ++ ucb1x00_disable_irq(ts->ucb, UCB_IRQ_ADC, UCB_FALLING); ++ data[state] = UCB_ADC_DAT(ucb1x00_reg_read(ts->ucb, UCB_ADC_DATA)); ++ ucb1x00_adc_disable(ts->ucb); ++ } ++ ++ /* If not pressed any more, try to sleep! */ ++ if (data[2] < 300) { ++ set_task_state(tsk, TASK_INTERRUPTIBLE); ++ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING); ++ ucb1x00_ts_mode_int(ts); ++ ucb1x00_disable(ts->ucb); ++ ucb1x00_ts_event_release(ts); ++ try_to_freeze(); ++ schedule_timeout(1000 * HZ); ++ ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING); ++ ucb1x00_enable(ts->ucb); ++ } else { ++ ucb1x00_ts_evt_add(ts, data[2], data[1], data[0]); ++ } ++ ucb1x00_disable(ts->ucb); ++ msleep(20); ++ ucb1x00_enable(ts->ucb); ++ } ++ ++ remove_wait_queue(&ts->irq_wait, &wait); ++ ++ ts->rtask = NULL; ++ return 0; ++} ++ ++/* ++ * We only detect touch screen _touches_ with this interrupt ++ * handler, and even then we just schedule our task. ++ */ ++static void ucb1x00_ts_irq(int idx, void *id) ++{ ++ struct ucb1x00_ts *ts = id; ++ wake_up(&ts->irq_wait); ++} ++ ++static void ucb1x00_adc_irq(int idx, void *id) ++{ ++ struct ucb1x00_ts *ts = id; ++ wake_up(&ts->irq_wait); ++} ++ ++static int ucb1x00_ts_open(struct input_dev *idev) ++{ ++ struct ucb1x00_ts *ts = input_get_drvdata(idev); ++ int ret = 0; ++ ++ BUG_ON(ts->rtask); ++ ++ init_waitqueue_head(&ts->irq_wait); ++ ++ ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); ++ if (ret < 0) ++ return ret; ++ ++ ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_ADC, ucb1x00_adc_irq, ts); ++ if (ret < 0) { ++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); ++ return ret; ++ } ++ ++ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING); ++ ++ /* ++ * If we do this at all, we should allow the user to ++ * measure and read the X and Y resistance at any time. ++ */ ++ ucb1x00_adc_enable(ts->ucb); ++ ts->x_res = ucb1x00_ts_read_xres(ts); ++ ts->y_res = ucb1x00_ts_read_yres(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ++ if (machine_is_collie()) { ++ ucb1x00_io_set_dir(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); ++ } ++ ++ ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd"); ++ if (!IS_ERR(ts->rtask)) { ++ ret = 0; ++ } else { ++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); ++ ts->rtask = NULL; ++ ret = -EFAULT; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Release touchscreen resources. Disable IRQs. ++ */ ++static void ucb1x00_ts_close(struct input_dev *idev) ++{ ++ struct ucb1x00_ts *ts = input_get_drvdata(idev); ++ ++ if (ts->rtask) ++ kthread_stop(ts->rtask); ++ ++ ucb1x00_enable(ts->ucb); ++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); ++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_ADC, ts); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); ++ ucb1x00_disable(ts->ucb); ++} ++ ++#ifdef CONFIG_PM ++static int ucb1x00_ts_resume(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_ts *ts = dev->priv; ++ ++ if (ts->rtask != NULL) { ++ /* ++ * Restart the TS thread to ensure the ++ * TS interrupt mode is set up again ++ * after sleep. ++ */ ++ wake_up(&ts->irq_wait); ++ } ++ return 0; ++} ++#else ++#define ucb1x00_ts_resume NULL ++#endif ++ ++ ++/* ++ * Initialisation. ++ */ ++static int ucb1x00_ts_add(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_ts *ts; ++ struct input_dev *idev; ++ int err; ++ ++ ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL); ++ idev = input_allocate_device(); ++ if (!ts || !idev) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ ts->ucb = dev->ucb; ++ ts->idev = idev; ++ ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; ++ ++ input_set_drvdata(idev, ts); ++ idev->name = "Touchscreen panel"; ++ idev->id.product = ts->ucb->id; ++ idev->open = ucb1x00_ts_open; ++ idev->close = ucb1x00_ts_close; ++ ++ __set_bit(EV_ABS, idev->evbit); ++ __set_bit(ABS_X, idev->absbit); ++ __set_bit(ABS_Y, idev->absbit); ++ __set_bit(ABS_PRESSURE, idev->absbit); ++ ++ input_set_abs_params(ts->idev, ABS_X, 0, 450, 0, 0); ++ input_set_abs_params(ts->idev, ABS_Y, 200, 800, 0, 0); ++ input_set_abs_params(ts->idev, ABS_PRESSURE, 400, 800, 0, 0); ++ ++ ++ err = input_register_device(idev); ++ if (err) ++ goto fail; ++ ++ dev->priv = ts; ++ ++ return 0; ++ ++ fail: ++ input_free_device(idev); ++ kfree(ts); ++ return err; ++} ++ ++static void ucb1x00_ts_remove(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_ts *ts = dev->priv; ++ ++ input_unregister_device(ts->idev); ++ kfree(ts); ++} ++ ++static struct ucb1x00_driver ucb1x00_ts_driver = { ++ .add = ucb1x00_ts_add, ++ .remove = ucb1x00_ts_remove, ++ .resume = ucb1x00_ts_resume, ++}; ++ ++static int __init ucb1x00_ts_init(void) ++{ ++ return ucb1x00_register_driver(&ucb1x00_ts_driver); ++} ++ ++static void __exit ucb1x00_ts_exit(void) ++{ ++ ucb1x00_unregister_driver(&ucb1x00_ts_driver); ++} ++ ++module_param(adcsync, int, 0444); ++module_init(ucb1x00_ts_init); ++module_exit(ucb1x00_ts_exit); ++ ++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); ++MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mfd/ucb1x00.h b/drivers/mfd/ucb1x00.h +index a8ad8a0..137b043 100644 +--- a/drivers/mfd/ucb1x00.h ++++ b/drivers/mfd/ucb1x00.h +@@ -34,7 +34,10 @@ + #define UCB_IE_TCLIP (1 << 14) + #define UCB_IE_ACLIP (1 << 15) + ++/* UCB1200 irqs */ ++#define UCB_IRQ_ADC 11 + #define UCB_IRQ_TSPX 12 ++#define UCB_IRQ_TSMX 13 + + #define UCB_TC_A 0x05 + #define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ +diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c +index 3b4e55c..0ae741d 100644 +--- a/drivers/net/wireless/hostap/hostap_cs.c ++++ b/drivers/net/wireless/hostap/hostap_cs.c +@@ -35,7 +35,7 @@ static int ignore_cis_vcc; + module_param(ignore_cis_vcc, int, 0444); + MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); + +- ++int activar=0; + /* struct local_info::hw_priv */ + struct hostap_cs_priv { + dev_node_t node; +@@ -499,11 +499,13 @@ static int hostap_cs_probe(struct pcmcia_device *p_dev) + + PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info); + p_dev->conf.IntType = INT_MEMORY_AND_IO; +- ++ ++ activar=0; + ret = prism2_config(p_dev); + if (ret) { + PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n"); + } ++ activar=1; + + return ret; + } +diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c +index 13d5882..6f24d66 100644 +--- a/drivers/net/wireless/hostap/hostap_hw.c ++++ b/drivers/net/wireless/hostap/hostap_hw.c +@@ -54,6 +54,7 @@ + #include "hostap.h" + #include "hostap_ap.h" + ++extern int activar; + + /* #define final_version */ + +@@ -1497,6 +1498,8 @@ static int prism2_hw_config(struct net_device *dev, int initial) + if (local->hw_downloading) + return 1; + ++ activar=1; ++ + if (prism2_hw_init(dev, initial)) { + return local->no_pri ? 0 : 1; + } +@@ -2628,8 +2631,15 @@ static irqreturn_t prism2_interrupt(int irq, void *dev_id) + int events = 0; + u16 ev; + +- iface = netdev_priv(dev); +- local = iface->local; ++ ++ // Todos los parametros de entrada son correctos (no son nulos). De momento esta es la unica forma que conozco de detectar el problema. ++ if (!activar) { ++ printk("hostap_hw.c: INTERRUPT BEFORE DEVICE INIT!\n"); ++ return IRQ_HANDLED; ++ } ++ ++ iface = netdev_priv(dev); ++ local = iface->local; + + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0); + +diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c +index 3a874fc..df58aa3 100644 +--- a/drivers/net/wireless/hostap/hostap_pci.c ++++ b/drivers/net/wireless/hostap/hostap_pci.c +@@ -19,6 +19,7 @@ + + #include "hostap_wlan.h" + ++int activar=1; + + static char *dev_info = "hostap_pci"; + +diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c +index cbf15d7..4475174 100644 +--- a/drivers/net/wireless/hostap/hostap_plx.c ++++ b/drivers/net/wireless/hostap/hostap_plx.c +@@ -21,7 +21,7 @@ + #include <asm/io.h> + + #include "hostap_wlan.h" +- ++int activar=1; + + static char *dev_info = "hostap_plx"; + +diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c +index c5b2a44..eecbe8c 100644 +--- a/drivers/pcmcia/sa1100_generic.c ++++ b/drivers/pcmcia/sa1100_generic.c +@@ -81,13 +81,14 @@ static int sa11x0_drv_pcmcia_probe(struct device *dev) + return ret; + } + +-static struct device_driver sa11x0_pcmcia_driver = { +- .probe = sa11x0_drv_pcmcia_probe, +- .remove = soc_common_drv_pcmcia_remove, +- .name = "sa11x0-pcmcia", +- .bus = &platform_bus_type, +- .suspend = pcmcia_socket_dev_suspend, +- .resume = pcmcia_socket_dev_resume, ++static struct platform_driver sa11x0_pcmcia_driver = { ++ .driver = { ++ .name = "sa11x0-pcmcia", ++ .probe = sa11x0_drv_pcmcia_probe, ++ .remove = soc_common_drv_pcmcia_remove, ++ .suspend= pcmcia_socket_dev_suspend, ++ .resume = pcmcia_socket_dev_resume, ++ }, + }; + + /* sa11x0_pcmcia_init() +@@ -100,7 +101,7 @@ static struct device_driver sa11x0_pcmcia_driver = { + */ + static int __init sa11x0_pcmcia_init(void) + { +- return driver_register(&sa11x0_pcmcia_driver); ++ return platform_driver_register(&sa11x0_pcmcia_driver); + } + + /* sa11x0_pcmcia_exit() +@@ -110,7 +111,7 @@ static int __init sa11x0_pcmcia_init(void) + */ + static void __exit sa11x0_pcmcia_exit(void) + { +- driver_unregister(&sa11x0_pcmcia_driver); ++ platform_driver_unregister(&sa11x0_pcmcia_driver); + } + + MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>"); +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 66ec5d8..aba38d7 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -123,6 +123,10 @@ config SPI_MPC52xx_PSC + This enables using the Freescale MPC52xx Programmable Serial + Controller in master SPI mode. + ++config SPI_LOCOMO ++ tristate "Locomo SPI master" ++ depends on SPI_MASTER && SHARP_LOCOMO && EXPERIMENTAL ++ + config SPI_MPC83xx + tristate "Freescale MPC83xx/QUICC Engine SPI controller" + depends on SPI_MASTER && (PPC_83xx || QUICC_ENGINE) && EXPERIMENTAL +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 7fca043..b89992b 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -28,6 +28,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o + obj-$(CONFIG_SPI_TXX9) += spi_txx9.o + obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o + obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o ++obj-$(CONFIG_SPI_LOCOMO) += locomo_spi.o + # ... add above this line ... + + # SPI protocol drivers (device/link on bus) +diff --git a/drivers/spi/locomo_spi.c b/drivers/spi/locomo_spi.c +new file mode 100644 +index 0000000..d3a4bd9 +--- /dev/null ++++ b/drivers/spi/locomo_spi.c +@@ -0,0 +1,1097 @@ ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/stat.h> ++#include <linux/delay.h> ++#include <linux/wait.h> ++#include <linux/interrupt.h> ++#include <asm/hardware/locomo.h> ++#include <asm/errno.h> ++#include <linux/mmc/host.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/mmc_spi.h> ++#include <linux/workqueue.h> ++#include <linux/spinlock.h> ++#include <linux/list.h> ++#include "locomo_spi.h" ++static struct locomospi_dev * spidev; ++static struct work_struct transfer_wq; ++int delay; ++ ++char* transtxbuf=(char*)NULL; ++char* transrxbuf=(char*)NULL; ++int transfercount=0, transfersize=0; ++static DECLARE_WAIT_QUEUE_HEAD(transferqueue); ++/* MMC_SPI functions *********************************************************/ ++ ++static int locomommcspi_init(struct device *dev, irqreturn_t (*isr)(int, void*), void *mmc) ++{ ++ int result; ++ result=request_irq(IRQ_LOCOMO_CARDDETECT, isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "locomo-spi", mmc); ++ return result; ++} ++ ++static void locomommcspi_exit(struct device *dev, void* mmc) ++{ ++ free_irq(IRQ_LOCOMO_CARDDETECT, mmc); ++} ++ ++static int locomommcspi_getro(struct device *dev) ++{ ++ return locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_WRITE_PROT) > 0 ? 1 : 0; ++} ++ ++static void locomommcspi_setpower(struct device *dev, unsigned int mask) ++{ ++ if(!mask && spidev->card_power) ++ locomospi_power(0); ++ else if( !spidev->card_power ) ++ locomospi_power(1); ++ ++} ++ ++ ++static struct mmc_spi_platform_data colliemmc ={ ++ .init = locomommcspi_init, ++ .exit = locomommcspi_exit, ++ .detect_delay = 200, ++ .get_ro = locomommcspi_getro, ++ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, ++ .setpower = locomommcspi_setpower, ++ .powerup_msecs = 200, ++}; ++ ++/* Utility function **********************************************************/ ++ ++static void locomospi_power(int on) ++{ ++ locomo_gpio_write(spidev->ldev->dev.parent, LOCOMO_GPIO_CARD_POWER, on); ++ spidev->card_power=on; ++ printk(KERN_DEBUG "locomospi: power %d\n",on); ++} ++ ++static void locomospi_setclock(unsigned int div, unsigned int clock) ++{ ++ u16 r = ioread16(spidev->base+LOCOMO_SPIMD); ++ div &= 0x7; ++ clock &= 0x3; ++ if(clock != spidev->clock_base || div != spidev->clock_div){ ++ r &= ~(LOCOMO_SPI_XSEL | LOCOMO_SPI_CLKSEL | LOCOMO_SPI_XEN); ++ iowrite16(r,spidev->base+LOCOMO_SPIMD); ++ r |= (div | (clock <<3) | LOCOMO_SPI_XEN); ++ iowrite16(r,spidev->base+LOCOMO_SPIMD); ++ spidev->clock_div = div; ++ spidev->clock_base = clock; ++ udelay(300); ++ } ++ ++} ++// returns 1 if card ist present, 0 otherwise ++static int locomospi_carddetect() ++{ ++ return (locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_CARD_DETECT)>0)?0:1; ++} ++ ++static void locomospi_setcs(int high) ++{ ++ u16 r; ++ printk(KERN_DEBUG "locomospi: cs %d\n",high); ++ r = ioread16(spidev->base + LOCOMO_SPICT); ++ if(high) ++ r |= LOCOMO_SPI_CS; ++ else ++ r &= ~LOCOMO_SPI_CS; ++ iowrite16(r, spidev->base + LOCOMO_SPICT); ++} ++ ++static void locomospi_reg_open() ++{ ++ u16 r; ++ spidev->clock_div = DIV_64; ++ spidev->clock_base = CLOCK_18MHZ; ++ locomospi_power(1); ++ msleep(100); ++// iowrite16( 0xec00 | (CLOCK_18MHZ <<3)|DIV_64, spidev->base+LOCOMO_SPIMD); ++ iowrite16( LOCOMO_SPI_MSB1ST | LOCOMO_SPI_DOSTAT | LOCOMO_SPI_RCPOL | LOCOMO_SPI_TCPOL ++ |(CLOCK_18MHZ <<3) | DIV_64, spidev->base+LOCOMO_SPIMD); ++// if(locomospi_carddetect()){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16( r, spidev->base+LOCOMO_SPIMD); ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XEN; ++ iowrite16( r, spidev->base+LOCOMO_SPIMD); ++// } ++ iowrite16( LOCOMO_SPI_CS, spidev->base+LOCOMO_SPICT); ++ r = ioread16(spidev->base+LOCOMO_SPICT); ++ r |= (LOCOMO_SPI_CEN | LOCOMO_SPI_RXUEN | LOCOMO_SPI_ALIGNEN); ++ iowrite16( r, spidev->base+LOCOMO_SPICT); ++ udelay(200); ++ r = ioread16(spidev->base+LOCOMO_SPICT); ++ iowrite16(r, spidev->base+LOCOMO_SPICT); ++ r = ioread16(spidev->base+LOCOMO_SPICT); ++ r &= ~LOCOMO_SPI_CS; ++ iowrite16(r, spidev->base+LOCOMO_SPICT); ++} ++ ++static void locomospi_reg_release() ++{ ++ u16 r; ++ r = ioread16(spidev->base+LOCOMO_SPICT); ++ r &= ~LOCOMO_SPI_CEN; ++ iowrite16(r, spidev->base+LOCOMO_SPICT); ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r &= ~LOCOMO_SPI_XEN; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r &= ~LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ r = ioread16(spidev->base+LOCOMO_SPICT); ++ r |= LOCOMO_SPI_XEN; ++ iowrite16(r, spidev->base+LOCOMO_SPICT); ++ locomospi_power(0); ++} ++#if 0 ++static int txrx(const char* txbuffer, char* rxbuffer, int size) ++{ ++ u16 r = ioread16(spidev->base+LOCOMO_SPICT); ++ r |= LOCOMO_SPI_ALIGNEN; ++ iowrite16(r, spidev->base+LOCOMO_SPICT); ++ printk(KERN_DEBUG "locomospi: %d bytes to prozess\n",size); ++ /* initialize global vars for isr */ ++ transfercount=0; transfersize=size; ++ transtxbuf=txbuffer; transrxbuf=rxbuffer; ++ ++ /* start transmit and go sleep isr will wake us*/ ++ enable_irq(IRQ_LOCOMO_SPI_TEND); ++ iowrite8(txbuffer[0], spidev->base+LOCOMO_SPITD); ++ wait_event(transferqueue, transfercount >= transfersize); ++ disable_irq(IRQ_LOCOMO_SPI_TEND); ++ transrxbuf=NULL; transtxbuf=NULL; ++ ++ r = ioread16(spidev->base+LOCOMO_SPICT); ++ r &= ~LOCOMO_SPI_ALIGNEN; ++ iowrite16(r, spidev->base+LOCOMO_SPICT); ++ int i; ++ for(i=0; i< size; i++) ++ printk(KERN_DEBUG "locomospi: sent: %x received: %x \n",txbuffer[i], rxbuffer[i]); ++ ++ ++ return size; ++} ++ ++ ++static int tx(const char* txbuffer, int size) ++{ ++ printk(KERN_DEBUG "locomospi: %d bytes to send\n",size); ++ /* initialize global vars for isr */ ++ transfercount=0; transfersize=size; ++ transtxbuf=txbuffer; ++ ++ /* start transmit and go sleep isr will wake us*/ ++ enable_irq(IRQ_LOCOMO_SPI_RFW); ++ iowrite8(txbuffer[0], spidev->base+LOCOMO_SPITD); ++ wait_event(transferqueue, transfercount >= transfersize); ++ disable_irq(IRQ_LOCOMO_SPI_RFW); ++ transtxbuf=NULL; ++ ++ int i; ++ for(i=0; i< size; i++) ++ printk(KERN_DEBUG "locomospi: sent: %x\n",txbuffer[i]); ++ ++ ++ return size; ++} ++ ++static int rx(char* rxbuffer, int size) ++{ ++ printk(KERN_DEBUG "locomospi: %d bytes to read\n",size); ++ /* initialize global vars for isr */ ++ transfercount=0; transfersize=size; ++ transrxbuf=rxbuffer; ++ ++ /* start transmit and go sleep isr will wake us*/ ++ enable_irq(IRQ_LOCOMO_SPI_RFR); ++ rxbuffer[0]=ioread8(spidev->base+LOCOMO_SPIRD); ++ wait_event(transferqueue, transfercount >= transfersize); ++ disable_irq(IRQ_LOCOMO_SPI_RFR); ++ transrxbuf=NULL; ++ ++ int i; ++ for(i=0; i< size; i++) ++ printk(KERN_DEBUG "locomospi: received: %x \n", rxbuffer[i]); ++ ++ ++ return size; ++} ++ ++#else ++static int txrx(const char* txbuffer, char* rxbuffer, int size) ++{ ++ int i=0,j=0; ++ int wait; ++ u16 r; ++/* char * txback = kmalloc(size * sizeof(char), GFP_KERNEL); ++ memcpy(txback, txbuffer, size); ++*/ ++ if(spidev->clock_div == 4) ++ wait = 0x10000; ++ else ++ wait = 8; ++ ++// printk(KERN_DEBUG "locomospi: txrx %d bytes to prozess\n",size); ++ ++// r = ioread16(spidev->base+LOCOMO_SPICT); ++// r |= LOCOMO_SPI_ALIGNEN; ++// iowrite16(r, spidev->base+LOCOMO_SPICT); ++ //discard first bogus byte ++ ++ ioread8(spidev->base+LOCOMO_SPIRD); ++ for(i=0; i<size; i++){ ++ for(j=0; j <= wait; j++){ ++ if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFW) ++ break; ++ } ++ iowrite8(txbuffer[i], spidev->base+LOCOMO_SPITD); ++ ndelay(delay); ++ ++ for(j=0; j <= wait; j++){ ++ if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFR) ++ break; ++ } ++ rxbuffer[i] = ioread8(spidev->base+LOCOMO_SPIRD); ++ ndelay(delay); ++ } ++// r = ioread16(spidev->base+LOCOMO_SPICT); ++// r &= ~LOCOMO_SPI_ALIGNEN; ++// iowrite16(r, spidev->base+LOCOMO_SPICT); ++ ++/* for(j=0; j< size; j++) ++ printk(KERN_DEBUG "locomospi: sent: %x received: %x \n",txback[j], rxbuffer[j]); ++ ++ kfree(txback); ++*/ return i; ++} ++ ++static int tx(const char* buffer, int size) ++{ ++ int i=0,j=0; ++ int wait; ++ u16 r; ++ if(spidev->clock_div == 4) ++ wait = 0x10000; ++ else ++ wait = 8; ++ r = ioread16(spidev->base+LOCOMO_SPICT); ++ r &= ~LOCOMO_SPI_ALIGNEN; ++ iowrite16(r, spidev->base+LOCOMO_SPICT); ++ ++// printk(KERN_DEBUG "locomospi: tx %d bytes to transmit\n",size); ++ for(i=0; i<size; i++){ ++ for(j=0; j <= wait; j++){ ++ if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFW) ++ break; ++ } ++ iowrite8(buffer[i], spidev->base+LOCOMO_SPITD); ++ ndelay(delay); ++ } ++ ++ for(j=0; j <= wait; j++){ ++ if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_TEND) ++ break; ++ } ++ ++ r = ioread16(spidev->base+LOCOMO_SPICT); ++ r |= LOCOMO_SPI_ALIGNEN; ++ iowrite16(r, spidev->base+LOCOMO_SPICT); ++ ++// for(j=0; j< size; j++) ++// printk(KERN_DEBUG "locomospi: sent: %x \n", buffer[j]); ++// printk(KERN_DEBUG "locomospi: tx %d bytes transmitted\n",i); ++ return i; ++} ++ ++static int rx(char* buffer, int size) ++{ ++ int i,j; ++ int wait; ++ u16 r; ++ printk(KERN_DEBUG "locomospi: rx %d bytes to receive\n",size); ++ if(spidev->clock_div == 4) ++ wait = 0x10000; ++ else ++ wait = 8; ++ r = ioread16(spidev->base+LOCOMO_SPICT); ++ r &= ~LOCOMO_SPI_ALIGNEN; ++ iowrite16(r, spidev->base+LOCOMO_SPICT); ++ ++ for(i=0; i<size; i++){ ++ ++ for(j=0; j <= wait; j++){ ++ if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFR) ++ break; ++ } ++ buffer[i]= ioread8(spidev->base+LOCOMO_SPIRD); ++ ndelay(delay); ++ } ++ ++ r = ioread16(spidev->base+LOCOMO_SPICT); ++ r |= LOCOMO_SPI_ALIGNEN; ++ iowrite16(r, spidev->base+LOCOMO_SPICT); ++ ++ for(j=0; j< size; j++) ++ printk(KERN_DEBUG "locomospi: received: %x \n", buffer[j]); ++ printk(KERN_DEBUG "locomospi: rx %d bytes received\n",i); ++ return i; ++} ++#endif ++/* ++static irqreturn_t locomospi_rwready(int irq, void *dev_id) ++{ ++ struct locomospi_dev* dev=(struct locomospi_dev*) dev_id; ++// dev_dbg(&spidev->sdev->dev, "IRQ: %d\n", irq); ++// printk(KERN_DEBUG "locomospi: IRQ: %d\n", irq); ++ wake_up_interruptible(&dev->waitqueue); ++ return IRQ_HANDLED; ++} ++*/ ++static irqreturn_t locomospi_testisr(int irq, void *dev_id) ++{ ++ char *buf=""; ++ switch(irq){ ++ case IRQ_LOCOMO_SPI_RFR: buf="RFR"; ++ break; ++ case IRQ_LOCOMO_SPI_RFW: buf="RFW"; ++ break; ++ case IRQ_LOCOMO_SPI_REND:buf="REND"; ++ break; ++ case IRQ_LOCOMO_SPI_TEND:buf="TEND"; ++ break; ++ case IRQ_LOCOMO_CARDDETECT: ++ buf="CARD_DETECT"; ++ break; ++ default: return IRQ_NONE; ++ } ++ printk(KERN_DEBUG "locomospi: IRQ: %s\n",buf); ++// dev_dbg(&spidev->sdev->dev, "IRQ: %s\n",buf); ++ return IRQ_HANDLED; ++} ++static irqreturn_t locomospi_txrxisr(int irq, void *dev_id) ++{ ++ if(transfercount < transfersize){ ++ transrxbuf[transfercount++] = ioread8(spidev->base+LOCOMO_SPIRD); ++ iowrite8(transtxbuf[transfercount], spidev->base+LOCOMO_SPITD); ++ } ++ else{ ++ /* transfer complete. wake up txrx */ ++ wake_up(&transferqueue); ++ } ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t locomospi_txisr(int irq, void *dev_id) ++{ ++ if(transfercount < transfersize){ ++ iowrite8(transtxbuf[transfercount++], spidev->base+LOCOMO_SPITD); ++ } ++ else{ ++ /* transfer complete. wake up txrx */ ++ wake_up(&transferqueue); ++ } ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t locomospi_rxisr(int irq, void *dev_id) ++{ ++ if(transfercount < transfersize){ ++ transrxbuf[transfercount++] = ioread8(spidev->base+LOCOMO_SPIRD); ++ } ++ else{ ++ /* transfer complete. wake up txrx */ ++ wake_up(&transferqueue); ++ } ++ return IRQ_HANDLED; ++} ++ ++static void locomospi_clock(unsigned int Hz) ++{ ++ u16 r; ++ printk(KERN_DEBUG "locomospi: changing clock to: %d\n", Hz); ++ if(Hz == 0){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r &= ~LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ } ++ else if(Hz >= 24576000){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_1, CLOCK_25MHZ); ++ delay=41; ++ } ++ else if(Hz >= 22579200){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_1, CLOCK_22MHZ); ++ delay=45; ++ } ++ else if(Hz >= 18432000){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_1, CLOCK_18MHZ); ++ delay=55; ++ } ++ else if(Hz >= 12288000){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_2, CLOCK_25MHZ); ++ delay=82; ++ } ++ else if(Hz >= 11289600){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_2, CLOCK_22MHZ); ++ delay=89; ++ } ++ else if(Hz >= 9216000){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_2, CLOCK_18MHZ); ++ delay=110; ++ } ++ else if(Hz >= 6144000){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_4, CLOCK_25MHZ); ++ delay=164; ++ } ++ else if(Hz >= 5644800){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_4, CLOCK_22MHZ); ++ delay=178; ++ } ++ else if(Hz >= 4608000){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_4, CLOCK_18MHZ); ++ delay=218; ++ } ++ else if(Hz >= 3072000){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_8, CLOCK_25MHZ); ++ delay=327; ++ } ++ else if(Hz >= 2822400){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_8, CLOCK_22MHZ); ++ delay=355; ++ } ++ else if(Hz >= 2304000){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_8, CLOCK_18MHZ); ++ delay=435; ++ } ++ else if(Hz >= 384000){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_64, CLOCK_25MHZ); ++ delay=2605; ++ } ++ else if(Hz >= 352800){ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_64, CLOCK_22MHZ); ++ delay=2834; ++ } ++ else{ /* set to 288 KHz */ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_XON; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ locomospi_setclock(DIV_64, CLOCK_18MHZ); ++ delay=3473; ++ } ++ spidev->clock = Hz; ++} ++ ++/* sysfs attributes used for debug *******************************************/ ++ ++/* SPI registers */ ++ssize_t locomospi_showspimd(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIMD)); ++} ++ ++ssize_t locomospi_storespimd(struct device_driver *drv, const char *buf, size_t count) ++{ ++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIMD); ++ return count; ++} ++static DRIVER_ATTR(spimd, S_IWUSR | S_IRUGO, locomospi_showspimd, locomospi_storespimd); ++ ++ssize_t locomospi_showspict(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPICT)); ++} ++ ++ssize_t locomospi_storespict(struct device_driver *drv, const char *buf, size_t count) ++{ ++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPICT); ++ return count; ++} ++static DRIVER_ATTR(spict, S_IWUSR | S_IRUGO, locomospi_showspict, locomospi_storespict); ++ ++ssize_t locomospi_showspist(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIST)); ++} ++ ++ssize_t locomospi_storespist(struct device_driver *drv, const char *buf, size_t count) ++{ ++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIST); ++ return count; ++} ++static DRIVER_ATTR(spist, S_IWUSR | S_IRUGO, locomospi_showspist, locomospi_storespist); ++ ++ssize_t locomospi_showspitd(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPITD)); ++} ++ ++ssize_t locomospi_storespitd(struct device_driver *drv, const char *buf, size_t count) ++{ ++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPITD); ++ return count; ++} ++static DRIVER_ATTR(spitd, S_IWUSR | S_IRUGO, locomospi_showspitd, locomospi_storespitd); ++ ++ssize_t locomospi_showspird(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIRD)); ++} ++ ++ssize_t locomospi_storespird(struct device_driver *drv, const char *buf, size_t count) ++{ ++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIRD); ++ return count; ++} ++static DRIVER_ATTR(spird, S_IWUSR | S_IRUGO, locomospi_showspird, locomospi_storespird); ++ ++ssize_t locomospi_showspits(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPITS)); ++} ++ ++ssize_t locomospi_storespits(struct device_driver *drv, const char *buf, size_t count) ++{ ++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPITS); ++ return count; ++} ++static DRIVER_ATTR(spits, S_IWUSR | S_IRUGO, locomospi_showspits, locomospi_storespits); ++ ++ssize_t locomospi_showspirs(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIRS)); ++} ++ ++ssize_t locomospi_storespirs(struct device_driver *drv, const char *buf, size_t count) ++{ ++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIRS); ++ return count; ++} ++static DRIVER_ATTR(spirs, S_IWUSR | S_IRUGO, locomospi_showspirs, locomospi_storespirs); ++ ++/* MMC Card status */ ++ ++ssize_t locomospi_showpower(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "%d\n", spidev->card_power); ++} ++ ++ssize_t locomospi_storepower(struct device_driver *drv, const char *buf, size_t count) ++{ ++ locomospi_power(simple_strtoul(buf, NULL, 10)); ++ return count; ++} ++static DRIVER_ATTR(cardpower, S_IWUSR | S_IRUGO, locomospi_showpower, locomospi_storepower); ++ ++ssize_t locomospi_detectcard(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "%d\n",(locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_CARD_DETECT)>0)?0:1); ++} ++static DRIVER_ATTR(carddetect, S_IRUGO, locomospi_detectcard, NULL); ++ ++ssize_t locomospi_writeprotect(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "%d\n",(locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_WRITE_PROT)>0)?1:0); ++} ++static DRIVER_ATTR(cardwriteprotect, S_IRUGO, locomospi_writeprotect, NULL); ++ ++ ++ssize_t locomospi_showclock(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "%d\n", spidev->clock); ++} ++ ++ssize_t locomospi_storeclock(struct device_driver *drv, const char *buf, size_t count) ++{ ++ locomospi_clock(simple_strtoul(buf, NULL, 10)); ++ return count; ++} ++static DRIVER_ATTR(clock, S_IWUSR | S_IRUGO, locomospi_showclock, locomospi_storeclock); ++ ++/* debug */ ++ssize_t locomospi_showdelay(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "%d\n", delay); ++} ++ ++ssize_t locomospi_storedelay(struct device_driver *drv, const char *buf, size_t count) ++{ ++ delay=simple_strtoul(buf,NULL,10); ++ return count; ++} ++static DRIVER_ATTR(delay, S_IWUSR | S_IRUGO, locomospi_showdelay, locomospi_storedelay); ++ ++ssize_t locomospi_reset(struct device_driver *drv, const char *buf, size_t count) ++{ ++ int choice = simple_strtoul(buf, NULL, 10); ++ char buff[100]; ++ u16 r; ++ switch(choice){ ++ case 0: locomospi_reg_release(); ++ schedule_timeout(2*HZ); ++ locomospi_reg_open(); ++ break; ++ case 1: { ++ char b1[] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; ++ char b2[] = "\xff\x40\x00\x00\x00\x00\x95\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; ++ locomospi_setcs(1); ++ txrx(b1,b1,17); ++ locomospi_setcs(0); ++ txrx(b2,b2,18); ++ ++ } ++ break; ++ case 2: locomospi_setcs(1); ++ txrx("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",buff,18); ++ locomospi_setcs(0); ++ txrx("\xff\x40\x00\x00\x00\x00\x95\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",buff,17); ++ break; ++ case 3: ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r |= LOCOMO_SPI_LOOPBACK; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ txrx("X",buff,1); ++ txrx("abcdefghijklmnopqrstuvwxyz1234567890",buff,36); ++ txrx("Y",buff,1); ++ udelay(100); ++ txrx("Z",buff,1); ++ schedule_timeout(HZ); ++ txrx("abcdefghijklmnopqrstuvwxyz1234567890",buff,36); ++ ++ r = ioread16(spidev->base+LOCOMO_SPIMD); ++ r &= ~LOCOMO_SPI_LOOPBACK; ++ iowrite16(r, spidev->base+LOCOMO_SPIMD); ++ break; ++ default: /* do nothing */; ++ } ++ return count; ++} ++static DRIVER_ATTR(reset, S_IWUSR, NULL, locomospi_reset); ++ ++typedef struct locomo_reg_entry { ++ u32 addr; ++ char* name; ++} locomo_reg_entry_t; ++#define LCM (sizeof(locomo_regs)/sizeof(locomo_reg_entry_t)) ++static locomo_reg_entry_t locomo_regs[] = ++{ ++/* { addr, name, description } */ ++ { 0x00, "VER" }, ++ { 0x04, "ST" }, ++ { 0x08, "C32K" }, ++ { 0x0C, "ICR" }, ++ { 0x10, "MCSX0" }, ++ { 0x14, "MCSX1" }, ++ { 0x18, "MCSX2" }, ++ { 0x1C, "MCSX3" }, ++ { 0x20, "ASD" }, ++ { 0x28, "HSD" }, ++ { 0x2C, "HSC" }, ++ { 0x30, "TADC" }, ++ { 0x38, "TC" }, ++ { 0x3C, "CPSD" }, ++ { 0x40, "KIB" }, ++ { 0x44, "KSC" }, ++ { 0x48, "KCMD" }, ++ { 0x4C, "KIC" }, ++ { 0x54, "ACC" }, ++ { 0x60, "SPIMD" }, ++ { 0x64, "SPICT" }, ++ { 0x68, "SPIST" }, ++ { 0x70, "SPIIS" }, ++ { 0x74, "SPIWE" }, ++ { 0x78, "SPIIE" }, ++ { 0x7C, "SPIIR" }, ++ { 0x80, "SPITD" }, ++ { 0x84, "SPIRD" }, ++ { 0x88, "SPITS" }, ++ { 0x8C, "SPIRS" }, ++ { 0x90, "GPD" }, ++ { 0x94, "GPE" }, ++ { 0x98, "GPL" }, ++ { 0x9C, "GPO" }, ++ { 0xa0, "GRIE" }, ++ { 0xa4, "GFIE" }, ++ { 0xa8, "GIS" }, ++ { 0xac, "GWE" }, ++ { 0xb0, "GIE" }, ++ { 0xb4, "GIR" }, ++ { 0xc8, "ALC" }, ++ { 0xcc, "ALR" }, ++ { 0xd0, "PAIF" }, ++ { 0xd8, "LTC" }, ++ { 0xdc, "LTINT" }, ++ { 0xe0, "DAC" }, ++ { 0xe8, "LPT0" }, ++ { 0xec, "LPT1" }, ++ { 0xfc, "TCR" }, ++}; ++ ++static ssize_t lcm_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ int base = spidev->base - LOCOMO_SPI; ++ char b[4000]=""; ++ char c[30]; ++ int i; ++ for(i=0; i<LCM; i++){ ++ sprintf(c,"%s:\t\t 0x%x\n",locomo_regs[i].name, ioread16(base + locomo_regs[i].addr)); ++ strcat(b,c); ++ } ++ return sprintf(buf,"%s",b); ++} ++ ++static DRIVER_ATTR(regs, 0444, lcm_show, NULL); ++ ++ ++/* SPI functions *************************************************************/ ++ ++static void locomospi_do_transfer(struct work_struct *wrk) ++{ ++ struct list_head *mptr, *tptr, *mptr2; ++ struct spi_transfer *entry; ++ struct spi_message *msg; ++ ++ list_for_each_safe(mptr, mptr2, &spidev->message_list){ ++ msg = list_entry(mptr, struct spi_message, queue); ++ ++ msg->status = 0; ++ msg->actual_length = 0; ++ list_for_each(tptr, &msg->transfers){ ++ entry = list_entry(tptr, struct spi_transfer, transfer_list); ++ if(entry->tx_buf && entry->rx_buf){ //duplex ++ txrx((char*) entry->tx_buf, (char*) entry->rx_buf, entry->len); ++ msg->actual_length += entry->len; ++ } else if(entry->tx_buf && !entry->rx_buf){ //write ++ tx((char*) entry->tx_buf, entry->len); ++ msg->actual_length += entry->len; ++ } else if(!entry->tx_buf && entry->rx_buf){ //read ++ rx((char*) entry->rx_buf, entry->len); ++ msg->actual_length += entry->len; ++ } else if(!entry->tx_buf && !entry->rx_buf){ //error ++ dev_err(&spidev->sdev->dev, "do_transfer: no buffers allocated\n"); ++ msg->status = -EFAULT; ++ } ++ } ++ spin_lock(&spidev->message_lock); ++ list_del(mptr); ++ spin_unlock(&spidev->message_lock); ++ msg->complete(msg->context); ++ } ++} ++ ++static int locomospi_setup(struct spi_device *spi) ++{ ++ if((spi->mode & SPI_CS_HIGH) != (spidev->spimode & SPI_CS_HIGH)) ++ locomospi_setcs(spi->mode & SPI_CS_HIGH ? 1 : 0 ); ++ if(spidev->clock != spi->max_speed_hz){ ++ locomospi_clock(spi->max_speed_hz); ++ } ++ spidev->spimode = spi->mode; ++ ++ return 0; ++} ++ ++static int locomospi_transfer(struct spi_device *spi, struct spi_message *msg) ++{ ++ ++ spin_lock(&spidev->message_lock); ++ list_add_tail(&msg->queue, &spidev->message_list); ++ spin_unlock(&spidev->message_lock); ++ schedule_work(&transfer_wq); ++ return 0; ++} ++ ++static struct locomo_driver locomo_spi_driver = { ++ .drv = { ++ .name = "locomo-spi", ++ }, ++ .devid = LOCOMO_DEVID_SPI, ++ .probe = locomospi_probe, ++ .remove = locomospi_remove, ++#ifdef CONFIG_PM ++ .suspend = locomospi_suspend, ++ .resume = locomospi_resume, ++#endif ++}; ++ ++static struct spi_board_info board = { ++ .modalias = "mmc_spi", ++ .platform_data = (void*) &colliemmc, ++ .controller_data= NULL, ++ .irq = 0, ++ .max_speed_hz = 25000000, ++ .bus_num = 0, ++ .chip_select = 0, ++ .mode = 0, ++}; ++ ++#ifdef CONFIG_PM ++static int locomospi_suspend(struct locomo_dev *dev, pm_message_t state) ++{ ++ disable_irq(IRQ_LOCOMO_CARDDETECT); ++ return 0; ++} ++ ++static int locomospi_resume(struct locomo_dev *dev) ++{ ++ enable_irq(IRQ_LOCOMO_CARDDETECT); ++ return 0; ++} ++#endif ++ ++static int locomospi_probe(struct locomo_dev *dev) ++{ ++ int result=0; ++ printk(KERN_DEBUG "Collie MMC over SPI Driver\n"); ++ spidev=kmalloc(sizeof(struct locomospi_dev),GFP_KERNEL); ++ if(!spidev){ ++ return -ENOMEM; ++ } ++ spidev->ldev = dev; ++ spidev->card_power = 1; ++ spidev->spimode = 0; ++ ++ if(!request_mem_region((unsigned long) dev->mapbase, dev->length, LOCOMO_DRIVER_NAME(dev))) { ++ dev_err(&dev->dev, " Can't aquire access to io memory\n"); ++ return -EBUSY; ++ } ++ spidev->base=(unsigned long) dev->mapbase; ++ locomospi_reg_open(); ++ ++ locomo_gpio_set_dir(dev->dev.parent, LOCOMO_GPIO_CARD_POWER, 0); ++ locomo_gpio_set_dir(dev->dev.parent, LOCOMO_GPIO_CARD_DETECT, 1); ++ locomo_gpio_set_dir(dev->dev.parent, LOCOMO_GPIO_WRITE_PROT, 1); ++ ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_cardpower); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_carddetect); ++ if(result){ ++ dev_err(&dev->dev,"error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_cardwriteprotect); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spimd); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spict); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spist); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spitd); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spird); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spits); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spirs); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_clock); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_delay); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_reset); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_regs); ++ if(result){ ++ dev_err(&dev->dev, "error creating driver attribute\n"); ++ goto region; ++ } ++ INIT_WORK(&transfer_wq, locomospi_do_transfer); ++ INIT_LIST_HEAD(&spidev->message_list); ++ spin_lock_init(&spidev->message_lock); ++ init_waitqueue_head(&spidev->waitqueue); ++ spidev->master=spi_alloc_master(&dev->dev,0); ++ if(!spidev->master){ ++ result=-ENOMEM; ++ goto region; ++ } ++ spidev->master->bus_num = 0; ++ spidev->master->num_chipselect = 1; ++ spidev->master->setup = locomospi_setup; ++ spidev->master->transfer = locomospi_transfer; ++ spidev->sdev = spi_new_device(spidev->master, &board); ++ if(!spidev->sdev){ ++ dev_err(&dev->dev, "failed to register spi device\n"); ++ result = -EINVAL; ++ goto master; ++ } ++/* result=request_irq(IRQ_LOCOMO_SPI_RFR, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev); ++ if(result) { ++ dev_err(&dev->dev, "Could not get IRQ: RFR\n"); ++ goto regdev; ++ } ++ //disable_irq(IRQ_LOCOMO_SPI_RFR); ++*//* result=request_irq(IRQ_LOCOMO_SPI_RFW, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev); ++ if(result) { ++ dev_err(&dev->dev, "Could not get IRQ: RFW\n"); ++ goto irq1; ++ } ++ //disable_irq(IRQ_LOCOMO_SPI_RFW); ++*//* result=request_irq(IRQ_LOCOMO_SPI_REND, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev); ++ if(result) { ++ dev_err(&dev->dev, "Could not get IRQ: REND\n"); ++ goto irq2; ++ } ++*//* result=request_irq(IRQ_LOCOMO_SPI_TEND, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev); ++ if(result) { ++ dev_err(&dev->dev, "Could not get IRQ: TEND\n"); ++ goto irq3; ++ } ++ //disable_irq(IRQ_LOCOMO_SPI_TEND); ++*/ spidev->workqueue = create_singlethread_workqueue("locomo-spi"); ++ if(!spidev->workqueue){ ++ dev_err(&dev->dev, "failed to create workqueue\n"); ++ goto irq4; ++ } ++ result=spi_register_master(spidev->master); ++ if(result){ ++ dev_err(&dev->dev, "failed to register spimaster\n"); ++ goto wq; ++ } ++ return 0; ++wq: ++ destroy_workqueue(spidev->workqueue); ++irq4: ++// free_irq(IRQ_LOCOMO_SPI_TEND, (void*) spidev); ++irq3: ++// free_irq(IRQ_LOCOMO_SPI_REND, (void*) spidev); ++irq2: ++// free_irq(IRQ_LOCOMO_SPI_RFW, (void*) spidev); ++irq1: ++// free_irq(IRQ_LOCOMO_SPI_RFR, (void*) spidev); ++regdev: ++ spi_unregister_device(spidev->sdev); ++master: ++ spi_master_put(spidev->master); ++region: ++ release_mem_region((unsigned long) dev->mapbase, dev->length); ++ kfree(spidev); ++ return result; ++ ++} ++ ++static int locomospi_remove(struct locomo_dev *dev) ++{ ++ spi_unregister_device(spidev->sdev); ++ spi_unregister_master(spidev->master); ++ destroy_workqueue(spidev->workqueue); ++ locomospi_reg_release(); ++// free_irq(IRQ_LOCOMO_SPI_TEND, (void*) spidev); ++// free_irq(IRQ_LOCOMO_SPI_REND, (void*) spidev); ++// free_irq(IRQ_LOCOMO_SPI_RFW, (void*) spidev); ++// free_irq(IRQ_LOCOMO_SPI_RFR, (void*) spidev); ++ spi_master_put(spidev->master); ++ release_mem_region((unsigned long) dev->mapbase, dev->length); ++ kfree(spidev); ++ return 0; ++} ++ ++ ++ ++static int __init locomospi_init(void) ++{ ++ int ret = locomo_driver_register(&locomo_spi_driver); ++ if (ret) ++ return ret; ++ ++ ++ return 0; ++} ++ ++static void __exit locomospi_exit(void) ++{ ++ locomo_driver_unregister(&locomo_spi_driver); ++} ++ ++module_init(locomospi_init); ++module_exit(locomospi_exit); ++ ++MODULE_AUTHOR("Thomas Kunze thommy@tabao.de"); ++MODULE_DESCRIPTION("Collie mmc driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/spi/locomo_spi.h b/drivers/spi/locomo_spi.h +new file mode 100644 +index 0000000..7e1c0ce +--- /dev/null ++++ b/drivers/spi/locomo_spi.h +@@ -0,0 +1,75 @@ ++#include <asm/hardware/locomo.h> ++#ifndef __LOCOMO_SPI_H__ ++#define __LOCOMO_SPI_H__ ++ ++/* locomo-spi status register LOCOMO_SPIST */ ++#define LOCOMO_SPI_TEND (1 << 3) /* Transfer end bit */ ++#define LOCOMO_SPI_REND (1 << 2) /* Receive end bit */ ++#define LOCOMO_SPI_RFW (1 << 1) /* write buffer bit */ ++#define LOCOMO_SPI_RFR (1) /* read buffer bit */ ++ ++/* locomo-spi mode register LOCOMO_SPIMD */ ++#define LOCOMO_SPI_LOOPBACK (1 << 15) /* loopback tx to rx */ ++#define LOCOMO_SPI_MSB1ST (1 << 14) /* send MSB first */ ++#define LOCOMO_SPI_DOSTAT (1 << 13) /* transmit line is idle high */ ++#define LOCOMO_SPI_TCPOL (1 << 11) /* transmit CPOL (maybe affects CPHA too) */ ++#define LOCOMO_SPI_RCPOL (1 << 10) /* receive CPOL (maybe affects CPHA too) */ ++#define LOCOMO_SPI_TDINV (1 << 9) /* invert transmit line */ ++#define LOCOMO_SPI_RDINV (1 << 8) /* invert receive line */ ++#define LOCOMO_SPI_XON (1 << 7) /* enable spi controller clock */ ++#define LOCOMO_SPI_XEN (1 << 6) /* clock bit write enable xon must be off, wait 300 us before xon->1 */ ++#define LOCOMO_SPI_XSEL 0x0018 /* clock select */ ++#define CLOCK_18MHZ 0 /* 18,432 MHz clock */ ++#define CLOCK_22MHZ 1 /* 22,5792 MHz clock */ ++#define CLOCK_25MHZ 2 /* 24,576 MHz clock */ ++#define LOCOMO_SPI_CLKSEL 0x7 ++#define DIV_1 0 /* don't divide clock */ ++#define DIV_2 1 /* divide clock by two */ ++#define DIV_4 2 /* divide clock by four */ ++#define DIV_8 3 /* divide clock by eight*/ ++#define DIV_64 4 /* divide clock by 64 */ ++ ++/* locomo-spi control register LOCOMO_SPICT */ ++#define LOCOMO_SPI_CRC16_7_B (1 << 15) /* 0: crc16 1: crc7 */ ++#define LOCOMO_SPI_CRCRX_TX_B (1 << 14) ++#define LOCOMO_SPI_CRCRESET_B (1 << 13) ++#define LOCOMO_SPI_CEN (1 << 7) /* ?? enable */ ++#define LOCOMO_SPI_CS (1 << 6) /* chip select */ ++#define LOCOMO_SPI_UNIT16 (1 << 5) /* 0: 8 bit units, 1: 16 bit unit */ ++#define LOCOMO_SPI_ALIGNEN (1 << 2) /* align transfer enable */ ++#define LOCOMO_SPI_RXWEN (1 << 1) /* continous receive */ ++#define LOCOMO_SPI_RXUEN (1 << 0) /* aligned receive */ ++ ++#define IRQ_LOCOMO_CARDDETECT IRQ_LOCOMO_GPIO13 ++ ++ ++struct locomospi_dev { ++ struct locomo_dev *ldev; ++ struct spi_master *master; ++ struct spi_device *sdev; ++ int card_power; ++ int clock_base; ++ int clock_div; ++ int clock; ++ unsigned long base; ++ u8 spimode; ++ wait_queue_head_t waitqueue; ++ struct workqueue_struct *workqueue; ++ struct list_head message_list; ++ spinlock_t message_lock; ++}; ++ ++ ++static irqreturn_t locomospi_cardisr(int, void*); ++static int locomospi_probe(struct locomo_dev*); ++static int locomospi_remove(struct locomo_dev*); ++static int locomospi_carddetect(void); ++static void locomospi_reg_open(void); ++static void locomospi_reg_release(void); ++static int tx(const char*, int); ++static int rx(char *, int); ++static void locomospi_power(int on); ++static int locomospi_suspend(struct locomo_dev *dev, pm_message_t state); ++static int locomospi_resume(struct locomo_dev *dev); ++static void locomospi_setcs(int high); ++#endif |