diff options
author | Denys Dmytriyenko <denis@denix.org> | 2009-03-17 14:32:59 -0400 |
---|---|---|
committer | Denys Dmytriyenko <denis@denix.org> | 2009-03-17 14:32:59 -0400 |
commit | 709c4d66e0b107ca606941b988bad717c0b45d9b (patch) | |
tree | 37ee08b1eb308f3b2b6426d5793545c38396b838 /recipes/linux/linux-rp-2.6.26/collie.patch | |
parent | fa6cd5a3b993f16c27de4ff82b42684516d433ba (diff) |
rename packages/ to recipes/ per earlier agreement
See links below for more details:
http://thread.gmane.org/gmane.comp.handhelds.openembedded/21326
http://thread.gmane.org/gmane.comp.handhelds.openembedded/21816
Signed-off-by: Denys Dmytriyenko <denis@denix.org>
Acked-by: Mike Westerhof <mwester@dls.net>
Acked-by: Philip Balister <philip@balister.org>
Acked-by: Khem Raj <raj.khem@gmail.com>
Acked-by: Marcin Juszkiewicz <hrw@openembedded.org>
Acked-by: Koen Kooi <koen@openembedded.org>
Acked-by: Frans Meulenbroeks <fransmeulenbroeks@gmail.com>
Diffstat (limited to 'recipes/linux/linux-rp-2.6.26/collie.patch')
-rw-r--r-- | recipes/linux/linux-rp-2.6.26/collie.patch | 2601 |
1 files changed, 2601 insertions, 0 deletions
diff --git a/recipes/linux/linux-rp-2.6.26/collie.patch b/recipes/linux/linux-rp-2.6.26/collie.patch new file mode 100644 index 0000000000..750be8ecf3 --- /dev/null +++ b/recipes/linux/linux-rp-2.6.26/collie.patch @@ -0,0 +1,2601 @@ +Index: linux-2.6.26/arch/arm/Kconfig +=================================================================== +--- linux-2.6.26.orig/arch/arm/Kconfig 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/arch/arm/Kconfig 2008-10-17 18:15:31.391792839 +0200 +@@ -967,7 +967,7 @@ + + 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 +Index: linux-2.6.26/arch/arm/mach-sa1100/collie.c +=================================================================== +--- linux-2.6.26.orig/arch/arm/mach-sa1100/collie.c 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/arch/arm/mach-sa1100/collie.c 2008-10-17 18:15:31.391792839 +0200 +@@ -206,7 +206,7 @@ + } + + static struct flash_platform_data collie_flash_data = { +- .map_name = "cfi_probe", ++ .map_name = "sharp", + .set_vpp = collie_set_vpp, + .parts = collie_partitions, + .nr_parts = ARRAY_SIZE(collie_partitions), +Index: linux-2.6.26/arch/arm/mach-sa1100/dma.c +=================================================================== +--- linux-2.6.26.orig/arch/arm/mach-sa1100/dma.c 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/arch/arm/mach-sa1100/dma.c 2008-10-17 18:15:31.399789199 +0200 +@@ -39,7 +39,7 @@ + + 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) +Index: linux-2.6.26/drivers/input/keyboard/locomokbd.c +=================================================================== +--- linux-2.6.26.orig/drivers/input/keyboard/locomokbd.c 2008-10-17 18:13:16.000000000 +0200 ++++ linux-2.6.26/drivers/input/keyboard/locomokbd.c 2008-10-17 18:15:31.403791239 +0200 +@@ -272,6 +272,7 @@ + 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); +Index: linux-2.6.26/drivers/mfd/Kconfig +=================================================================== +--- linux-2.6.26.orig/drivers/mfd/Kconfig 2008-10-17 18:13:21.000000000 +0200 ++++ linux-2.6.26/drivers/mfd/Kconfig 2008-10-17 18:15:31.403791239 +0200 +@@ -77,4 +77,10 @@ + 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 +Index: linux-2.6.26/drivers/mfd/Makefile +=================================================================== +--- linux-2.6.26.orig/drivers/mfd/Makefile 2008-10-17 18:13:21.000000000 +0200 ++++ linux-2.6.26/drivers/mfd/Makefile 2008-10-17 18:15:31.407791679 +0200 +@@ -14,7 +14,7 @@ + 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 +Index: linux-2.6.26/drivers/mfd/collie-ts.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.26/drivers/mfd/collie-ts.c 2008-10-17 18:15:31.415790559 +0200 +@@ -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 <linux/semaphore.h> ++ ++#include <asm/dma.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"); +Index: linux-2.6.26/drivers/mfd/ucb1x00.h +=================================================================== +--- linux-2.6.26.orig/drivers/mfd/ucb1x00.h 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/drivers/mfd/ucb1x00.h 2008-10-17 18:15:31.415790559 +0200 +@@ -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 */ +Index: linux-2.6.26/drivers/mtd/chips/Kconfig +=================================================================== +--- linux-2.6.26.orig/drivers/mtd/chips/Kconfig 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/drivers/mtd/chips/Kconfig 2008-10-17 18:15:31.419791479 +0200 +@@ -239,5 +239,13 @@ + used for XIP purposes. If you're not sure what this is all about + then say N. + ++config MTD_SHARP ++ tristate "pre-CFI Sharp chip support" ++ depends on MTD ++ help ++ This option enables support for flash chips using Sharp-compatible ++ commands, including some which are not CFI-compatible and hence ++ cannot be used with the CONFIG_MTD_CFI_INTELxxx options. ++ + endmenu + +Index: linux-2.6.26/drivers/mtd/chips/Makefile +=================================================================== +--- linux-2.6.26.orig/drivers/mtd/chips/Makefile 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/drivers/mtd/chips/Makefile 2008-10-17 18:15:31.419791479 +0200 +@@ -12,4 +12,5 @@ + obj-$(CONFIG_MTD_JEDECPROBE) += jedec_probe.o + obj-$(CONFIG_MTD_RAM) += map_ram.o + obj-$(CONFIG_MTD_ROM) += map_rom.o ++obj-$(CONFIG_MTD_SHARP) += sharp.o + obj-$(CONFIG_MTD_ABSENT) += map_absent.o +Index: linux-2.6.26/drivers/mtd/chips/sharp.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.26/drivers/mtd/chips/sharp.c 2008-10-17 18:15:31.423790399 +0200 +@@ -0,0 +1,645 @@ ++/* ++ * MTD chip driver for pre-CFI Sharp flash chips ++ * ++ * Copyright 2000,2001 David A. Schleef <ds@schleef.org> ++ * 2000,2001 Lineo, Inc. ++ * ++ * $Id: sharp.c,v 1.17 2005/11/29 14:28:28 gleixner Exp $ ++ * ++ * Devices supported: ++ * LH28F016SCT Symmetrical block flash memory, 2Mx8 ++ * LH28F008SCT Symmetrical block flash memory, 1Mx8 ++ * ++ * Documentation: ++ * http://www.sharpmeg.com/datasheets/memic/flashcmp/ ++ * http://www.sharpmeg.com/datasheets/memic/flashcmp/01symf/16m/016sctl9.pdf ++ * 016sctl9.pdf ++ * ++ * Limitations: ++ * This driver only supports 4x1 arrangement of chips. ++ * Not tested on anything but PowerPC. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/sched.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/cfi.h> ++#include <linux/delay.h> ++#include <linux/init.h> ++ ++#define CMD_RESET 0xffffffff ++#define CMD_READ_ID 0x90909090 ++#define CMD_READ_STATUS 0x70707070 ++#define CMD_CLEAR_STATUS 0x50505050 ++#define CMD_BLOCK_ERASE_1 0x20202020 ++#define CMD_BLOCK_ERASE_2 0xd0d0d0d0 ++#define CMD_BYTE_WRITE 0x40404040 ++#define CMD_SUSPEND 0xb0b0b0b0 ++#define CMD_RESUME 0xd0d0d0d0 ++#define CMD_SET_BLOCK_LOCK_1 0x60606060 ++#define CMD_SET_BLOCK_LOCK_2 0x01010101 ++#define CMD_SET_MASTER_LOCK_1 0x60606060 ++#define CMD_SET_MASTER_LOCK_2 0xf1f1f1f1 ++#define CMD_CLEAR_BLOCK_LOCKS_1 0x60606060 ++#define CMD_CLEAR_BLOCK_LOCKS_2 0xd0d0d0d0 ++ ++#define SR_READY 0x80808080 // 1 = ready ++#define SR_ERASE_SUSPEND 0x40404040 // 1 = block erase suspended ++#define SR_ERROR_ERASE 0x20202020 // 1 = error in block erase or clear lock bits ++#define SR_ERROR_WRITE 0x10101010 // 1 = error in byte write or set lock bit ++#define SR_VPP 0x08080808 // 1 = Vpp is low ++#define SR_WRITE_SUSPEND 0x04040404 // 1 = byte write suspended ++#define SR_PROTECT 0x02020202 // 1 = lock bit set ++#define SR_RESERVED 0x01010101 ++ ++#define SR_ERRORS (SR_ERROR_ERASE|SR_ERROR_WRITE|SR_VPP|SR_PROTECT) ++ ++#define BLOCK_MASK 0xfffe0000 ++ ++/* Configuration options */ ++ ++#define AUTOUNLOCK /* automatically unlocks blocks before erasing */ ++ ++static struct mtd_info *sharp_probe(struct map_info *); ++ ++static int sharp_probe_map(struct map_info *map, struct mtd_info *mtd); ++ ++static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf); ++static int sharp_write(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, const u_char *buf); ++static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr); ++static void sharp_sync(struct mtd_info *mtd); ++static int sharp_suspend(struct mtd_info *mtd); ++static void sharp_resume(struct mtd_info *mtd); ++static void sharp_destroy(struct mtd_info *mtd); ++ ++static int sharp_write_oneword(struct map_info *map, struct flchip *chip, ++ unsigned long adr, __u32 datum); ++static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip, ++ unsigned long adr); ++#ifdef AUTOUNLOCK ++static inline void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip, ++ unsigned long adr); ++#endif ++ ++ ++struct sharp_info{ ++ struct flchip *chip; ++ int bogus; ++ int chipshift; ++ int numchips; ++ struct flchip chips[1]; ++}; ++ ++static void sharp_destroy(struct mtd_info *mtd); ++ ++static struct mtd_chip_driver sharp_chipdrv = { ++ .probe = sharp_probe, ++ .destroy = sharp_destroy, ++ .name = "sharp", ++ .module = THIS_MODULE ++}; ++ ++static void sharp_udelay(unsigned long i) { ++ if (in_interrupt()) { ++ udelay(i); ++ } else { ++ schedule(); ++ } ++} ++ ++static struct mtd_info *sharp_probe(struct map_info *map) ++{ ++ struct mtd_info *mtd = NULL; ++ struct sharp_info *sharp = NULL; ++ int width; ++ ++ mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); ++ if(!mtd) ++ return NULL; ++ ++ sharp = kzalloc(sizeof(*sharp), GFP_KERNEL); ++ if(!sharp) { ++ kfree(mtd); ++ return NULL; ++ } ++ ++ width = sharp_probe_map(map,mtd); ++ if(!width){ ++ kfree(mtd); ++ kfree(sharp); ++ return NULL; ++ } ++ ++ mtd->priv = map; ++ mtd->type = MTD_NORFLASH; ++ mtd->erase = sharp_erase; ++ mtd->read = sharp_read; ++ mtd->write = sharp_write; ++ mtd->sync = sharp_sync; ++ mtd->suspend = sharp_suspend; ++ mtd->resume = sharp_resume; ++ mtd->flags = MTD_CAP_NORFLASH; ++ mtd->writesize = 1; ++ mtd->name = map->name; ++ ++ sharp->chipshift = 24; ++ sharp->numchips = 1; ++ sharp->chips[0].start = 0; ++ sharp->chips[0].state = FL_READY; ++ sharp->chips[0].mutex = &sharp->chips[0]._spinlock; ++ sharp->chips[0].word_write_time = 0; ++ init_waitqueue_head(&sharp->chips[0].wq); ++ spin_lock_init(&sharp->chips[0]._spinlock); ++ ++ map->fldrv = &sharp_chipdrv; ++ map->fldrv_priv = sharp; ++ ++ __module_get(THIS_MODULE); ++ return mtd; ++} ++ ++static inline void sharp_send_cmd(struct map_info *map, unsigned long cmd, unsigned long adr) ++{ ++ map_word map_cmd; ++ map_cmd.x[0] = cmd; ++ map_write(map, map_cmd, adr); ++} ++ ++static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd) ++{ ++ map_word tmp, read0, read4; ++ unsigned long base = 0; ++ int width = 4; ++ ++ tmp = map_read(map, base+0); ++ ++ sharp_send_cmd(map, CMD_READ_ID, base+0); ++ ++ read0 = map_read(map, base+0); ++ read4 = map_read(map, base+4); ++ if (read0.x[0] == 0x00b000b0) { ++ printk("Sharp chip, %lx, %lx, width = %d\n", read0.x[0], read4.x[0], width); ++ /* Prints b000b0, b000b0, width = 4 on collie */ ++ switch(read4.x[0]){ ++ case 0xaaaaaaaa: ++ case 0xa0a0a0a0: ++ /* aa - LH28F016SCT-L95 2Mx8, 32 64k blocks*/ ++ /* a0 - LH28F016SCT-Z4 2Mx8, 32 64k blocks*/ ++ mtd->erasesize = 0x10000 * width; ++ mtd->size = 0x200000 * width; ++ return width; ++ case 0xa6a6a6a6: ++ /* a6 - LH28F008SCT-L12 1Mx8, 16 64k blocks*/ ++ /* a6 - LH28F008SCR-L85 1Mx8, 16 64k blocks*/ ++ mtd->erasesize = 0x10000 * width; ++ mtd->size = 0x100000 * width; ++ return width; ++ case 0x00b000b0: ++ /* a6 - LH28F640BFHE 8 64k * 2 chip blocks*/ ++ mtd->erasesize = 0x10000 * width / 2; ++ mtd->size = 0x800000 * width / 2; ++ return width; ++ default: ++ printk("Sort-of looks like sharp flash, 0x%08lx 0x%08lx\n", ++ read0.x[0], read4.x[0]); ++ } ++ } else if ((map_read(map, base+0).x[0] == CMD_READ_ID)){ ++ /* RAM, probably */ ++ printk("Looks like RAM\n"); ++ map_write(map, tmp, base+0); ++ }else{ ++ printk("Doesn't look like sharp flash, 0x%08lx 0x%08lx\n", ++ read0.x[0], read4.x[0]); ++ } ++ ++ return 0; ++} ++ ++/* This function returns with the chip->mutex lock held. */ ++static int sharp_wait(struct map_info *map, struct flchip *chip) ++{ ++ map_word status; ++ unsigned long timeo = jiffies + HZ; ++ DECLARE_WAITQUEUE(wait, current); ++ int adr = 0; ++ ++retry: ++ spin_lock_bh(chip->mutex); ++ ++ switch (chip->state) { ++ case FL_READY: ++ sharp_send_cmd(map, CMD_READ_STATUS, adr); ++ chip->state = FL_STATUS; ++ case FL_STATUS: ++ status = map_read(map, adr); ++ if ((status.x[0] & SR_READY) == SR_READY) ++ break; ++ spin_unlock_bh(chip->mutex); ++ if (time_after(jiffies, timeo)) { ++ printk("Waiting for chip to be ready timed out in erase\n"); ++ return -EIO; ++ } ++ sharp_udelay(1); ++ goto retry; ++ default: ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ ++ spin_unlock_bh(chip->mutex); ++ ++ sharp_udelay(1); ++ ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&chip->wq, &wait); ++ ++ if(signal_pending(current)) ++ return -EINTR; ++ ++ timeo = jiffies + HZ; ++ ++ goto retry; ++ } ++ ++ sharp_send_cmd(map, CMD_RESET, adr); ++ ++ chip->state = FL_READY; ++ ++ return 0; ++} ++ ++static void sharp_release(struct flchip *chip) ++{ ++ wake_up(&chip->wq); ++ spin_unlock_bh(chip->mutex); ++} ++ ++static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ struct map_info *map = mtd->priv; ++ struct sharp_info *sharp = map->fldrv_priv; ++ int chipnum; ++ int ret = 0; ++ int ofs = 0; ++ ++ chipnum = (from >> sharp->chipshift); ++ ofs = from & ((1 << sharp->chipshift)-1); ++ ++ *retlen = 0; ++ ++ while(len){ ++ unsigned long thislen; ++ ++ if(chipnum>=sharp->numchips) ++ break; ++ ++ thislen = len; ++ if(ofs+thislen >= (1<<sharp->chipshift)) ++ thislen = (1<<sharp->chipshift) - ofs; ++ ++ ret = sharp_wait(map,&sharp->chips[chipnum]); ++ if(ret<0) ++ break; ++ ++ map_copy_from(map,buf,ofs,thislen); ++ ++ sharp_release(&sharp->chips[chipnum]); ++ ++ *retlen += thislen; ++ len -= thislen; ++ buf += thislen; ++ ++ ofs = 0; ++ chipnum++; ++ } ++ return ret; ++} ++ ++static int sharp_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct map_info *map = mtd->priv; ++ struct sharp_info *sharp = map->fldrv_priv; ++ int ret = 0; ++ int i,j; ++ int chipnum; ++ unsigned long ofs; ++ union { u32 l; unsigned char uc[4]; } tbuf; ++ ++ *retlen = 0; ++ ++ while(len){ ++ tbuf.l = 0xffffffff; ++ chipnum = to >> sharp->chipshift; ++ ofs = to & ((1<<sharp->chipshift)-1); ++ ++ j=0; ++ for(i=ofs&3;i<4 && len;i++){ ++ tbuf.uc[i] = *buf; ++ buf++; ++ to++; ++ len--; ++ j++; ++ } ++ sharp_write_oneword(map, &sharp->chips[chipnum], ofs&~3, tbuf.l); ++ if(ret<0) ++ return ret; ++ (*retlen)+=j; ++ } ++ ++ return 0; ++} ++ ++static int sharp_write_oneword(struct map_info *map, struct flchip *chip, ++ unsigned long adr, __u32 datum) ++{ ++ int ret; ++ int try; ++ int i; ++ map_word data, status; ++ ++ status.x[0] = 0; ++ ret = sharp_wait(map,chip); ++ if (ret < 0) ++ return ret; ++ ++ for (try=0; try<10; try++) { ++ long timeo; ++ ++ sharp_send_cmd(map, CMD_BYTE_WRITE, adr); ++ /* cpu_to_le32 -> hack to fix the writel be->le conversion */ ++ data.x[0] = cpu_to_le32(datum); ++ map_write(map, data, adr); ++ ++ chip->state = FL_WRITING; ++ timeo = jiffies + (HZ/2); ++ ++ sharp_send_cmd(map, CMD_READ_STATUS, adr); ++ for(i=0;i<100;i++){ ++ status = map_read(map, adr); ++ if((status.x[0] & SR_READY) == SR_READY) ++ break; ++ } ++#ifdef AUTOUNLOCK ++ if (status.x[0] & SR_PROTECT) { /* lock block */ ++ sharp_send_cmd(map, CMD_CLEAR_STATUS, adr); ++ sharp_unlock_oneblock(map,chip,adr); ++ sharp_send_cmd(map, CMD_CLEAR_STATUS, adr); ++ sharp_send_cmd(map, CMD_RESET, adr); ++ continue; ++ } ++#endif ++ if(i==100){ ++ printk("sharp: timed out writing\n"); ++ } ++ ++ if (!(status.x[0] & SR_ERRORS)) ++ break; ++ ++ printk("sharp: error writing byte at addr=%08lx status=%08lx\n", adr, status.x[0]); ++ ++ sharp_send_cmd(map, CMD_CLEAR_STATUS, adr); ++ } ++ sharp_send_cmd(map, CMD_RESET, adr); ++ chip->state = FL_READY; ++ ++ sharp_release(chip); ++ ++ return 0; ++} ++ ++static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct map_info *map = mtd->priv; ++ struct sharp_info *sharp = map->fldrv_priv; ++ unsigned long adr,len; ++ int chipnum, ret=0; ++ ++ if(instr->addr & (mtd->erasesize - 1)) ++ return -EINVAL; ++ if(instr->len & (mtd->erasesize - 1)) ++ return -EINVAL; ++ if(instr->len + instr->addr > mtd->size) ++ return -EINVAL; ++ ++ chipnum = instr->addr >> sharp->chipshift; ++ adr = instr->addr & ((1<<sharp->chipshift)-1); ++ len = instr->len; ++ ++ while(len){ ++ ret = sharp_erase_oneblock(map, &sharp->chips[chipnum], adr); ++ if(ret)return ret; ++ ++ if (adr >= 0xfe0000) { ++ adr += mtd->erasesize / 8; ++ len -= mtd->erasesize / 8; ++ } else { ++ adr += mtd->erasesize; ++ len -= mtd->erasesize; ++ } ++ if(adr >> sharp->chipshift){ ++ adr = 0; ++ chipnum++; ++ if(chipnum>=sharp->numchips) ++ break; ++ } ++ } ++ ++ instr->state = MTD_ERASE_DONE; ++ mtd_erase_callback(instr); ++ ++ return 0; ++} ++ ++static inline int sharp_do_wait_for_ready(struct map_info *map, struct flchip *chip, ++ unsigned long adr) ++{ ++ int ret; ++ unsigned long timeo; ++ map_word status; ++ DECLARE_WAITQUEUE(wait, current); ++ ++ sharp_send_cmd(map, CMD_READ_STATUS, adr); ++ status = map_read(map, adr); ++ ++ timeo = jiffies + HZ * 10; ++ ++ while (time_before(jiffies, timeo)) { ++ sharp_send_cmd(map, CMD_READ_STATUS, adr); ++ status = map_read(map, adr); ++ if ((status.x[0] & SR_READY) == SR_READY) { ++ ret = 0; ++ goto out; ++ } ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ ++ spin_unlock_bh(chip->mutex); ++ ++ schedule_timeout(1); ++ schedule(); ++ ++ spin_lock_bh(chip->mutex); ++ ++ remove_wait_queue(&chip->wq, &wait); ++ set_current_state(TASK_RUNNING); ++ } ++ ret = -ETIME; ++out: ++ return ret; ++} ++ ++static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip, ++ unsigned long adr) ++{ ++ int ret; ++ map_word status; ++ ++ ret = sharp_wait(map,chip); ++ if (ret < 0) ++ return ret; ++ ++#ifdef AUTOUNLOCK ++ /* This seems like a good place to do an unlock */ ++ sharp_unlock_oneblock(map,chip,adr); ++#endif ++ ++ sharp_send_cmd(map, CMD_BLOCK_ERASE_1, adr); ++ sharp_send_cmd(map, CMD_BLOCK_ERASE_2, adr); ++ ++ chip->state = FL_ERASING; ++ ++ ret = sharp_do_wait_for_ready(map,chip,adr); ++ if(ret<0) { ++ spin_unlock_bh(chip->mutex); ++ return ret; ++ } ++ ++ sharp_send_cmd(map, CMD_READ_STATUS, adr); ++ status = map_read(map, adr); ++ ++ if (!(status.x[0] & SR_ERRORS)) { ++ sharp_send_cmd(map, CMD_RESET, adr); ++ chip->state = FL_READY; ++ spin_unlock_bh(chip->mutex); ++ return 0; ++ } ++ ++ printk("sharp: error erasing block at addr=%08lx status=%08lx\n", adr, status.x[0]); ++ sharp_send_cmd(map, CMD_CLEAR_STATUS, adr); ++ ++ sharp_release(chip); ++ ++ return -EIO; ++} ++ ++#ifdef AUTOUNLOCK ++static inline void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip, ++ unsigned long adr) ++{ ++ map_word status; ++ ++ sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_1, adr & BLOCK_MASK); ++ sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_2, adr & BLOCK_MASK); ++ ++ sharp_do_wait_for_ready(map,chip,adr); ++ ++ status = map_read(map, adr); ++ ++ if (!(status.x[0] & SR_ERRORS)) { ++ sharp_send_cmd(map, CMD_RESET, adr); ++ chip->state = FL_READY; ++ return; ++ } ++ ++ printk("sharp: error unlocking block at addr=%08lx status=%08lx\n", adr, status.x[0]); ++ sharp_send_cmd(map, CMD_CLEAR_STATUS, adr); ++} ++#endif ++ ++static void sharp_sync(struct mtd_info *mtd) ++{ ++} ++ ++static int sharp_suspend(struct mtd_info *mtd) ++{ ++ struct map_info *map = mtd->priv; ++ struct sharp_info *sharp = map->fldrv_priv; ++ int i; ++ struct flchip *chip; ++ int ret = 0; ++ ++ for (i = 0; !ret && i < sharp->numchips; i++) { ++ chip = &sharp->chips[i]; ++ ret = sharp_wait(map,chip); ++ ++ if (ret) { ++ ret = -EAGAIN; ++ } else { ++ chip->state = FL_PM_SUSPENDED; ++ spin_unlock_bh(chip->mutex); ++ } ++ } ++ return ret; ++} ++ ++static void sharp_resume(struct mtd_info *mtd) ++{ ++ struct map_info *map = mtd->priv; ++ struct sharp_info *sharp = map->fldrv_priv; ++ int i; ++ struct flchip *chip; ++ ++ for (i = 0; i < sharp->numchips; i++) { ++ chip = &sharp->chips[i]; ++ ++ spin_lock_bh(chip->mutex); ++ ++ if (chip->state == FL_PM_SUSPENDED) { ++ /* We need to force it back to a known state */ ++ sharp_send_cmd(map, CMD_RESET, chip->start); ++ chip->state = FL_READY; ++ wake_up(&chip->wq); ++ } ++ ++ spin_unlock_bh(chip->mutex); ++ } ++} ++ ++static void sharp_destroy(struct mtd_info *mtd) ++{ ++ struct map_info *map = mtd->priv; ++ struct sharp_info *sharp = map->fldrv_priv; ++ ++ kfree(sharp); ++} ++ ++static int __init sharp_probe_init(void) ++{ ++ printk("MTD Sharp chip driver <ds@lineo.com>\n"); ++ ++ register_mtd_chip_driver(&sharp_chipdrv); ++ ++ return 0; ++} ++ ++static void __exit sharp_probe_exit(void) ++{ ++ unregister_mtd_chip_driver(&sharp_chipdrv); ++} ++ ++module_init(sharp_probe_init); ++module_exit(sharp_probe_exit); ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Schleef <ds@schleef.org>"); ++MODULE_DESCRIPTION("Old MTD chip driver for pre-CFI Sharp flash chips"); +Index: linux-2.6.26/drivers/mtd/maps/Kconfig +=================================================================== +--- linux-2.6.26.orig/drivers/mtd/maps/Kconfig 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/drivers/mtd/maps/Kconfig 2008-10-17 18:15:31.431789839 +0200 +@@ -392,7 +392,7 @@ + + config MTD_SA1100 + tristate "CFI Flash device mapped on StrongARM SA11x0" +- depends on MTD_CFI && ARCH_SA1100 && MTD_PARTITIONS ++ depends on (MTD_CFI || MTD_SHARP) && ARCH_SA1100 && MTD_PARTITIONS + help + This enables access to the flash chips on most platforms based on + the SA1100 and SA1110, including the Assabet and the Compaq iPAQ. +Index: linux-2.6.26/drivers/mtd/maps/sa1100-flash.c +=================================================================== +--- linux-2.6.26.orig/drivers/mtd/maps/sa1100-flash.c 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/drivers/mtd/maps/sa1100-flash.c 2008-10-17 18:15:31.431789839 +0200 +@@ -210,6 +210,12 @@ + goto err; + } + subdev->mtd->owner = THIS_MODULE; ++ ++#ifdef CONFIG_SA1100_COLLIE ++ /* collie flash starts locked */ ++// if (subdev->mtd->unlock) ++// subdev->mtd->unlock(subdev->mtd, 0xc0000, subdev->mtd->size - 0xc0000); ++#endif + + printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " + "%d-bit\n", phys, subdev->mtd->size >> 20, +Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_cs.c +=================================================================== +--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_cs.c 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/drivers/net/wireless/hostap/hostap_cs.c 2008-10-17 18:15:31.435790279 +0200 +@@ -35,7 +35,7 @@ + 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 @@ + + 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; + } +Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_hw.c +=================================================================== +--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_hw.c 2008-10-17 18:13:21.000000000 +0200 ++++ linux-2.6.26/drivers/net/wireless/hostap/hostap_hw.c 2008-10-17 18:15:31.443789719 +0200 +@@ -54,6 +54,7 @@ + #include "hostap.h" + #include "hostap_ap.h" + ++extern int activar; + + /* #define final_version */ + +@@ -1534,6 +1535,8 @@ + if (local->hw_downloading) + return 1; + ++ activar=1; ++ + if (prism2_hw_init(dev, initial)) { + return local->no_pri ? 0 : 1; + } +@@ -2665,8 +2668,15 @@ + 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; + + if(dev->base_addr == 0) + { +Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_pci.c +=================================================================== +--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_pci.c 2008-10-17 18:13:18.000000000 +0200 ++++ linux-2.6.26/drivers/net/wireless/hostap/hostap_pci.c 2008-10-17 18:15:31.447790279 +0200 +@@ -19,6 +19,7 @@ + + #include "hostap_wlan.h" + ++int activar=1; + + static char *dev_info = "hostap_pci"; + +Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_plx.c +=================================================================== +--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_plx.c 2008-10-17 18:13:18.000000000 +0200 ++++ linux-2.6.26/drivers/net/wireless/hostap/hostap_plx.c 2008-10-17 18:15:31.451790719 +0200 +@@ -21,7 +21,7 @@ + #include <asm/io.h> + + #include "hostap_wlan.h" +- ++int activar=1; + + static char *dev_info = "hostap_plx"; + +Index: linux-2.6.26/drivers/pcmcia/sa1100_generic.c +=================================================================== +--- linux-2.6.26.orig/drivers/pcmcia/sa1100_generic.c 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/drivers/pcmcia/sa1100_generic.c 2008-10-17 18:15:31.459789719 +0200 +@@ -81,13 +81,14 @@ + 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 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 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>"); +Index: linux-2.6.26/drivers/spi/Kconfig +=================================================================== +--- linux-2.6.26.orig/drivers/spi/Kconfig 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/drivers/spi/Kconfig 2008-10-17 18:15:31.463790519 +0200 +@@ -123,6 +123,10 @@ + 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 +Index: linux-2.6.26/drivers/spi/Makefile +=================================================================== +--- linux-2.6.26.orig/drivers/spi/Makefile 2008-07-13 23:51:29.000000000 +0200 ++++ linux-2.6.26/drivers/spi/Makefile 2008-10-17 18:15:31.463790519 +0200 +@@ -28,6 +28,7 @@ + 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) +Index: linux-2.6.26/drivers/spi/locomo_spi.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.26/drivers/spi/locomo_spi.c 2008-10-17 18:15:31.471790439 +0200 +@@ -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"); +Index: linux-2.6.26/drivers/spi/locomo_spi.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.26/drivers/spi/locomo_spi.h 2008-10-17 18:15:31.471790439 +0200 +@@ -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 |