169ebb222SKristoffer Ericson /**
269ebb222SKristoffer Ericson  *  arch/arm/mac-sa1100/jornada720_ssp.c
369ebb222SKristoffer Ericson  *
469ebb222SKristoffer Ericson  *  Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
569ebb222SKristoffer Ericson  *   Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl>
669ebb222SKristoffer Ericson  *
769ebb222SKristoffer Ericson  * This program is free software; you can redistribute it and/or modify
869ebb222SKristoffer Ericson  * it under the terms of the GNU General Public License version 2 as
969ebb222SKristoffer Ericson  * published by the Free Software Foundation.
1069ebb222SKristoffer Ericson  *
1169ebb222SKristoffer Ericson  *  SSP driver for the HP Jornada 710/720/728
1269ebb222SKristoffer Ericson  */
1369ebb222SKristoffer Ericson 
1469ebb222SKristoffer Ericson #include <linux/delay.h>
1569ebb222SKristoffer Ericson #include <linux/errno.h>
1669ebb222SKristoffer Ericson #include <linux/init.h>
1769ebb222SKristoffer Ericson #include <linux/kernel.h>
1869ebb222SKristoffer Ericson #include <linux/module.h>
1969ebb222SKristoffer Ericson #include <linux/platform_device.h>
2069ebb222SKristoffer Ericson #include <linux/sched.h>
213169663aSRussell King #include <linux/io.h>
2269ebb222SKristoffer Ericson 
23a09e64fbSRussell King #include <mach/hardware.h>
24a09e64fbSRussell King #include <mach/jornada720.h>
2558005b32SKristoffer Ericson #include <asm/hardware/ssp.h>
2669ebb222SKristoffer Ericson 
2769ebb222SKristoffer Ericson static DEFINE_SPINLOCK(jornada_ssp_lock);
2869ebb222SKristoffer Ericson static unsigned long jornada_ssp_flags;
2969ebb222SKristoffer Ericson 
3069ebb222SKristoffer Ericson /**
3169ebb222SKristoffer Ericson  * jornada_ssp_reverse - reverses input byte
3269ebb222SKristoffer Ericson  *
3325985edcSLucas De Marchi  * we need to reverse all data we receive from the mcu due to its physical location
3469ebb222SKristoffer Ericson  * returns : 01110111 -> 11101110
3569ebb222SKristoffer Ericson  */
361e90d0edSJoe Perches inline u8 jornada_ssp_reverse(u8 byte)
3769ebb222SKristoffer Ericson {
3869ebb222SKristoffer Ericson 	return
3969ebb222SKristoffer Ericson 		((0x80 & byte) >> 7) |
4069ebb222SKristoffer Ericson 		((0x40 & byte) >> 5) |
4169ebb222SKristoffer Ericson 		((0x20 & byte) >> 3) |
4269ebb222SKristoffer Ericson 		((0x10 & byte) >> 1) |
4369ebb222SKristoffer Ericson 		((0x08 & byte) << 1) |
4469ebb222SKristoffer Ericson 		((0x04 & byte) << 3) |
4569ebb222SKristoffer Ericson 		((0x02 & byte) << 5) |
4669ebb222SKristoffer Ericson 		((0x01 & byte) << 7);
4769ebb222SKristoffer Ericson };
4869ebb222SKristoffer Ericson EXPORT_SYMBOL(jornada_ssp_reverse);
4969ebb222SKristoffer Ericson 
5069ebb222SKristoffer Ericson /**
5169ebb222SKristoffer Ericson  * jornada_ssp_byte - waits for ready ssp bus and sends byte
5269ebb222SKristoffer Ericson  *
5369ebb222SKristoffer Ericson  * waits for fifo buffer to clear and then transmits, if it doesn't then we will
5469ebb222SKristoffer Ericson  * timeout after <timeout> rounds. Needs mcu running before its called.
5569ebb222SKristoffer Ericson  *
5669ebb222SKristoffer Ericson  * returns : %mcu output on success
573ac49a1cSJean Delvare  *	   : %-ETIMEDOUT on timeout
5869ebb222SKristoffer Ericson  */
5969ebb222SKristoffer Ericson int jornada_ssp_byte(u8 byte)
6069ebb222SKristoffer Ericson {
6169ebb222SKristoffer Ericson 	int timeout = 400000;
6269ebb222SKristoffer Ericson 	u16 ret;
6369ebb222SKristoffer Ericson 
6469ebb222SKristoffer Ericson 	while ((GPLR & GPIO_GPIO10)) {
6569ebb222SKristoffer Ericson 		if (!--timeout) {
6669ebb222SKristoffer Ericson 			printk(KERN_WARNING "SSP: timeout while waiting for transmit\n");
6769ebb222SKristoffer Ericson 			return -ETIMEDOUT;
6869ebb222SKristoffer Ericson 		}
6969ebb222SKristoffer Ericson 		cpu_relax();
7069ebb222SKristoffer Ericson 	}
7169ebb222SKristoffer Ericson 
7269ebb222SKristoffer Ericson 	ret = jornada_ssp_reverse(byte) << 8;
7369ebb222SKristoffer Ericson 
7469ebb222SKristoffer Ericson 	ssp_write_word(ret);
7569ebb222SKristoffer Ericson 	ssp_read_word(&ret);
7669ebb222SKristoffer Ericson 
7769ebb222SKristoffer Ericson 	return jornada_ssp_reverse(ret);
7869ebb222SKristoffer Ericson };
7969ebb222SKristoffer Ericson EXPORT_SYMBOL(jornada_ssp_byte);
8069ebb222SKristoffer Ericson 
8169ebb222SKristoffer Ericson /**
8269ebb222SKristoffer Ericson  * jornada_ssp_inout - decide if input is command or trading byte
8369ebb222SKristoffer Ericson  *
8469ebb222SKristoffer Ericson  * returns : (jornada_ssp_byte(byte)) on success
853ac49a1cSJean Delvare  *         : %-ETIMEDOUT on timeout failure
8669ebb222SKristoffer Ericson  */
8769ebb222SKristoffer Ericson int jornada_ssp_inout(u8 byte)
8869ebb222SKristoffer Ericson {
8969ebb222SKristoffer Ericson 	int ret, i;
9069ebb222SKristoffer Ericson 
9169ebb222SKristoffer Ericson 	/* true means command byte */
9269ebb222SKristoffer Ericson 	if (byte != TXDUMMY) {
9369ebb222SKristoffer Ericson 		ret = jornada_ssp_byte(byte);
9469ebb222SKristoffer Ericson 		/* Proper return to commands is TxDummy */
9569ebb222SKristoffer Ericson 		if (ret != TXDUMMY) {
9669ebb222SKristoffer Ericson 			for (i = 0; i < 256; i++)/* flushing bus */
9769ebb222SKristoffer Ericson 				if (jornada_ssp_byte(TXDUMMY) == -1)
9869ebb222SKristoffer Ericson 					break;
9969ebb222SKristoffer Ericson 			return -ETIMEDOUT;
10069ebb222SKristoffer Ericson 		}
10169ebb222SKristoffer Ericson 	} else /* Exchange TxDummy for data */
10269ebb222SKristoffer Ericson 		ret = jornada_ssp_byte(TXDUMMY);
10369ebb222SKristoffer Ericson 
10469ebb222SKristoffer Ericson 	return ret;
10569ebb222SKristoffer Ericson };
10669ebb222SKristoffer Ericson EXPORT_SYMBOL(jornada_ssp_inout);
10769ebb222SKristoffer Ericson 
10869ebb222SKristoffer Ericson /**
10969ebb222SKristoffer Ericson  * jornada_ssp_start - enable mcu
11069ebb222SKristoffer Ericson  *
11169ebb222SKristoffer Ericson  */
11258005b32SKristoffer Ericson void jornada_ssp_start(void)
11369ebb222SKristoffer Ericson {
11469ebb222SKristoffer Ericson 	spin_lock_irqsave(&jornada_ssp_lock, jornada_ssp_flags);
11569ebb222SKristoffer Ericson 	GPCR = GPIO_GPIO25;
11669ebb222SKristoffer Ericson 	udelay(50);
11758005b32SKristoffer Ericson 	return;
11869ebb222SKristoffer Ericson };
11969ebb222SKristoffer Ericson EXPORT_SYMBOL(jornada_ssp_start);
12069ebb222SKristoffer Ericson 
12169ebb222SKristoffer Ericson /**
12269ebb222SKristoffer Ericson  * jornada_ssp_end - disable mcu and turn off lock
12369ebb222SKristoffer Ericson  *
12469ebb222SKristoffer Ericson  */
12558005b32SKristoffer Ericson void jornada_ssp_end(void)
12669ebb222SKristoffer Ericson {
12769ebb222SKristoffer Ericson 	GPSR = GPIO_GPIO25;
12869ebb222SKristoffer Ericson 	spin_unlock_irqrestore(&jornada_ssp_lock, jornada_ssp_flags);
12958005b32SKristoffer Ericson 	return;
13069ebb222SKristoffer Ericson };
13169ebb222SKristoffer Ericson EXPORT_SYMBOL(jornada_ssp_end);
13269ebb222SKristoffer Ericson 
133351a102dSGreg Kroah-Hartman static int jornada_ssp_probe(struct platform_device *dev)
13469ebb222SKristoffer Ericson {
13569ebb222SKristoffer Ericson 	int ret;
13669ebb222SKristoffer Ericson 
13769ebb222SKristoffer Ericson 	GPSR = GPIO_GPIO25;
13869ebb222SKristoffer Ericson 
13969ebb222SKristoffer Ericson 	ret = ssp_init();
14069ebb222SKristoffer Ericson 
14169ebb222SKristoffer Ericson 	/* worked fine, lets not bother with anything else */
14269ebb222SKristoffer Ericson 	if (!ret) {
14369ebb222SKristoffer Ericson 		printk(KERN_INFO "SSP: device initialized with irq\n");
14469ebb222SKristoffer Ericson 		return ret;
14569ebb222SKristoffer Ericson 	}
14669ebb222SKristoffer Ericson 
14769ebb222SKristoffer Ericson 	printk(KERN_WARNING "SSP: initialization failed, trying non-irq solution \n");
14869ebb222SKristoffer Ericson 
14969ebb222SKristoffer Ericson 	/* init of Serial 4 port */
15069ebb222SKristoffer Ericson 	Ser4MCCR0 = 0;
15169ebb222SKristoffer Ericson 	Ser4SSCR0 = 0x0387;
15269ebb222SKristoffer Ericson 	Ser4SSCR1 = 0x18;
15369ebb222SKristoffer Ericson 
15469ebb222SKristoffer Ericson 	/* clear out any left over data */
15569ebb222SKristoffer Ericson 	ssp_flush();
15669ebb222SKristoffer Ericson 
15769ebb222SKristoffer Ericson 	/* enable MCU */
15869ebb222SKristoffer Ericson 	jornada_ssp_start();
15969ebb222SKristoffer Ericson 
16069ebb222SKristoffer Ericson 	/* see if return value makes sense */
16169ebb222SKristoffer Ericson 	ret = jornada_ssp_inout(GETBRIGHTNESS);
16269ebb222SKristoffer Ericson 
16369ebb222SKristoffer Ericson 	/* seems like it worked, just feed it with TxDummy to get rid of data */
164219e3dcdSKristoffer Ericson 	if (ret == TXDUMMY)
16569ebb222SKristoffer Ericson 		jornada_ssp_inout(TXDUMMY);
16669ebb222SKristoffer Ericson 
16769ebb222SKristoffer Ericson 	jornada_ssp_end();
16869ebb222SKristoffer Ericson 
16969ebb222SKristoffer Ericson 	/* failed, lets just kill everything */
17069ebb222SKristoffer Ericson 	if (ret == -ETIMEDOUT) {
17169ebb222SKristoffer Ericson 		printk(KERN_WARNING "SSP: attempts failed, bailing\n");
17269ebb222SKristoffer Ericson 		ssp_exit();
17369ebb222SKristoffer Ericson 		return -ENODEV;
17469ebb222SKristoffer Ericson 	}
17569ebb222SKristoffer Ericson 
17669ebb222SKristoffer Ericson 	/* all fine */
17769ebb222SKristoffer Ericson 	printk(KERN_INFO "SSP: device initialized\n");
17869ebb222SKristoffer Ericson 	return 0;
17969ebb222SKristoffer Ericson };
18069ebb222SKristoffer Ericson 
18169ebb222SKristoffer Ericson static int jornada_ssp_remove(struct platform_device *dev)
18269ebb222SKristoffer Ericson {
18325985edcSLucas De Marchi 	/* Note that this doesn't actually remove the driver, since theres nothing to remove
18469ebb222SKristoffer Ericson 	 * It just makes sure everything is turned off */
18569ebb222SKristoffer Ericson 	GPSR = GPIO_GPIO25;
18669ebb222SKristoffer Ericson 	ssp_exit();
18769ebb222SKristoffer Ericson 	return 0;
18869ebb222SKristoffer Ericson };
18969ebb222SKristoffer Ericson 
19069ebb222SKristoffer Ericson struct platform_driver jornadassp_driver = {
19169ebb222SKristoffer Ericson 	.probe	= jornada_ssp_probe,
19269ebb222SKristoffer Ericson 	.remove	= jornada_ssp_remove,
19369ebb222SKristoffer Ericson 	.driver	= {
19469ebb222SKristoffer Ericson 		.name	= "jornada_ssp",
19569ebb222SKristoffer Ericson 	},
19669ebb222SKristoffer Ericson };
19769ebb222SKristoffer Ericson 
19869ebb222SKristoffer Ericson static int __init jornada_ssp_init(void)
19969ebb222SKristoffer Ericson {
20069ebb222SKristoffer Ericson 	return platform_driver_register(&jornadassp_driver);
20169ebb222SKristoffer Ericson }
202b3945bcbSLinus Walleij 
203b3945bcbSLinus Walleij module_init(jornada_ssp_init);
204