1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2*46dd6078SRandy Dunlap /*
369ebb222SKristoffer Ericson  *  arch/arm/mac-sa1100/jornada720_ssp.c
469ebb222SKristoffer Ericson  *
569ebb222SKristoffer Ericson  *  Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
669ebb222SKristoffer Ericson  *   Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl>
769ebb222SKristoffer Ericson  *
869ebb222SKristoffer Ericson  *  SSP driver for the HP Jornada 710/720/728
969ebb222SKristoffer Ericson  */
1069ebb222SKristoffer Ericson 
1169ebb222SKristoffer Ericson #include <linux/delay.h>
1269ebb222SKristoffer Ericson #include <linux/errno.h>
1369ebb222SKristoffer Ericson #include <linux/init.h>
1469ebb222SKristoffer Ericson #include <linux/kernel.h>
1569ebb222SKristoffer Ericson #include <linux/module.h>
1669ebb222SKristoffer Ericson #include <linux/platform_device.h>
1769ebb222SKristoffer Ericson #include <linux/sched.h>
183169663aSRussell King #include <linux/io.h>
1969ebb222SKristoffer Ericson 
20a09e64fbSRussell King #include <mach/hardware.h>
21a09e64fbSRussell King #include <mach/jornada720.h>
2258005b32SKristoffer Ericson #include <asm/hardware/ssp.h>
2369ebb222SKristoffer Ericson 
2469ebb222SKristoffer Ericson static DEFINE_SPINLOCK(jornada_ssp_lock);
2569ebb222SKristoffer Ericson static unsigned long jornada_ssp_flags;
2669ebb222SKristoffer Ericson 
2769ebb222SKristoffer Ericson /**
2869ebb222SKristoffer Ericson  * jornada_ssp_reverse - reverses input byte
29*46dd6078SRandy Dunlap  * @byte: input byte to reverse
3069ebb222SKristoffer Ericson  *
3125985edcSLucas De Marchi  * we need to reverse all data we receive from the mcu due to its physical location
3269ebb222SKristoffer Ericson  * returns : 01110111 -> 11101110
3369ebb222SKristoffer Ericson  */
jornada_ssp_reverse(u8 byte)341e90d0edSJoe Perches inline u8 jornada_ssp_reverse(u8 byte)
3569ebb222SKristoffer Ericson {
3669ebb222SKristoffer Ericson 	return
3769ebb222SKristoffer Ericson 		((0x80 & byte) >> 7) |
3869ebb222SKristoffer Ericson 		((0x40 & byte) >> 5) |
3969ebb222SKristoffer Ericson 		((0x20 & byte) >> 3) |
4069ebb222SKristoffer Ericson 		((0x10 & byte) >> 1) |
4169ebb222SKristoffer Ericson 		((0x08 & byte) << 1) |
4269ebb222SKristoffer Ericson 		((0x04 & byte) << 3) |
4369ebb222SKristoffer Ericson 		((0x02 & byte) << 5) |
4469ebb222SKristoffer Ericson 		((0x01 & byte) << 7);
4569ebb222SKristoffer Ericson };
4669ebb222SKristoffer Ericson EXPORT_SYMBOL(jornada_ssp_reverse);
4769ebb222SKristoffer Ericson 
4869ebb222SKristoffer Ericson /**
4969ebb222SKristoffer Ericson  * jornada_ssp_byte - waits for ready ssp bus and sends byte
50*46dd6078SRandy Dunlap  * @byte: input byte to transmit
5169ebb222SKristoffer Ericson  *
5269ebb222SKristoffer Ericson  * waits for fifo buffer to clear and then transmits, if it doesn't then we will
5369ebb222SKristoffer Ericson  * timeout after <timeout> rounds. Needs mcu running before its called.
5469ebb222SKristoffer Ericson  *
5569ebb222SKristoffer Ericson  * returns : %mcu output on success
563ac49a1cSJean Delvare  *	   : %-ETIMEDOUT on timeout
5769ebb222SKristoffer Ericson  */
jornada_ssp_byte(u8 byte)5869ebb222SKristoffer Ericson int jornada_ssp_byte(u8 byte)
5969ebb222SKristoffer Ericson {
6069ebb222SKristoffer Ericson 	int timeout = 400000;
6169ebb222SKristoffer Ericson 	u16 ret;
6269ebb222SKristoffer Ericson 
6369ebb222SKristoffer Ericson 	while ((GPLR & GPIO_GPIO10)) {
6469ebb222SKristoffer Ericson 		if (!--timeout) {
6569ebb222SKristoffer Ericson 			printk(KERN_WARNING "SSP: timeout while waiting for transmit\n");
6669ebb222SKristoffer Ericson 			return -ETIMEDOUT;
6769ebb222SKristoffer Ericson 		}
6869ebb222SKristoffer Ericson 		cpu_relax();
6969ebb222SKristoffer Ericson 	}
7069ebb222SKristoffer Ericson 
7169ebb222SKristoffer Ericson 	ret = jornada_ssp_reverse(byte) << 8;
7269ebb222SKristoffer Ericson 
7369ebb222SKristoffer Ericson 	ssp_write_word(ret);
7469ebb222SKristoffer Ericson 	ssp_read_word(&ret);
7569ebb222SKristoffer Ericson 
7669ebb222SKristoffer Ericson 	return jornada_ssp_reverse(ret);
7769ebb222SKristoffer Ericson };
7869ebb222SKristoffer Ericson EXPORT_SYMBOL(jornada_ssp_byte);
7969ebb222SKristoffer Ericson 
8069ebb222SKristoffer Ericson /**
8169ebb222SKristoffer Ericson  * jornada_ssp_inout - decide if input is command or trading byte
82*46dd6078SRandy Dunlap  * @byte: input byte to send (may be %TXDUMMY)
8369ebb222SKristoffer Ericson  *
8469ebb222SKristoffer Ericson  * returns : (jornada_ssp_byte(byte)) on success
853ac49a1cSJean Delvare  *         : %-ETIMEDOUT on timeout failure
8669ebb222SKristoffer Ericson  */
jornada_ssp_inout(u8 byte)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  */
jornada_ssp_start(void)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  */
jornada_ssp_end(void)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 
jornada_ssp_probe(struct platform_device * dev)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 
jornada_ssp_remove(struct platform_device * dev)18169ebb222SKristoffer Ericson static void 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 };
18869ebb222SKristoffer Ericson 
18969ebb222SKristoffer Ericson struct platform_driver jornadassp_driver = {
19069ebb222SKristoffer Ericson 	.probe	= jornada_ssp_probe,
19169ebb222SKristoffer Ericson 	.remove_new = jornada_ssp_remove,
19269ebb222SKristoffer Ericson 	.driver	= {
19369ebb222SKristoffer Ericson 		.name	= "jornada_ssp",
19469ebb222SKristoffer Ericson 	},
19569ebb222SKristoffer Ericson };
19669ebb222SKristoffer Ericson 
jornada_ssp_init(void)19769ebb222SKristoffer Ericson static int __init jornada_ssp_init(void)
19869ebb222SKristoffer Ericson {
19969ebb222SKristoffer Ericson 	return platform_driver_register(&jornadassp_driver);
20069ebb222SKristoffer Ericson }
20169ebb222SKristoffer Ericson 
202b3945bcbSLinus Walleij module_init(jornada_ssp_init);
203b3945bcbSLinus Walleij