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 : + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 "); +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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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<chipshift)) + thislen = (1<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<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<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 \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 "); +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 #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 "); 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; ibase+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; ibase+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; ibase+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; imessage_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 +#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