18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  arch/arm/mac-sa1100/jornada720_ssp.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
68c2ecf20Sopenharmony_ci *   Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  SSP driver for the HP Jornada 710/720/728
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/sched.h>
188c2ecf20Sopenharmony_ci#include <linux/io.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <mach/hardware.h>
218c2ecf20Sopenharmony_ci#include <mach/jornada720.h>
228c2ecf20Sopenharmony_ci#include <asm/hardware/ssp.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(jornada_ssp_lock);
258c2ecf20Sopenharmony_cistatic unsigned long jornada_ssp_flags;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/**
288c2ecf20Sopenharmony_ci * jornada_ssp_reverse - reverses input byte
298c2ecf20Sopenharmony_ci * @byte: input byte to reverse
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * we need to reverse all data we receive from the mcu due to its physical location
328c2ecf20Sopenharmony_ci * returns : 01110111 -> 11101110
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ciinline u8 jornada_ssp_reverse(u8 byte)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	return
378c2ecf20Sopenharmony_ci		((0x80 & byte) >> 7) |
388c2ecf20Sopenharmony_ci		((0x40 & byte) >> 5) |
398c2ecf20Sopenharmony_ci		((0x20 & byte) >> 3) |
408c2ecf20Sopenharmony_ci		((0x10 & byte) >> 1) |
418c2ecf20Sopenharmony_ci		((0x08 & byte) << 1) |
428c2ecf20Sopenharmony_ci		((0x04 & byte) << 3) |
438c2ecf20Sopenharmony_ci		((0x02 & byte) << 5) |
448c2ecf20Sopenharmony_ci		((0x01 & byte) << 7);
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(jornada_ssp_reverse);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/**
498c2ecf20Sopenharmony_ci * jornada_ssp_byte - waits for ready ssp bus and sends byte
508c2ecf20Sopenharmony_ci * @byte: input byte to transmit
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * waits for fifo buffer to clear and then transmits, if it doesn't then we will
538c2ecf20Sopenharmony_ci * timeout after <timeout> rounds. Needs mcu running before its called.
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * returns : %mcu output on success
568c2ecf20Sopenharmony_ci *	   : %-ETIMEDOUT on timeout
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_ciint jornada_ssp_byte(u8 byte)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	int timeout = 400000;
618c2ecf20Sopenharmony_ci	u16 ret;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	while ((GPLR & GPIO_GPIO10)) {
648c2ecf20Sopenharmony_ci		if (!--timeout) {
658c2ecf20Sopenharmony_ci			printk(KERN_WARNING "SSP: timeout while waiting for transmit\n");
668c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
678c2ecf20Sopenharmony_ci		}
688c2ecf20Sopenharmony_ci		cpu_relax();
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	ret = jornada_ssp_reverse(byte) << 8;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	ssp_write_word(ret);
748c2ecf20Sopenharmony_ci	ssp_read_word(&ret);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return jornada_ssp_reverse(ret);
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(jornada_ssp_byte);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/**
818c2ecf20Sopenharmony_ci * jornada_ssp_inout - decide if input is command or trading byte
828c2ecf20Sopenharmony_ci * @byte: input byte to send (may be %TXDUMMY)
838c2ecf20Sopenharmony_ci *
848c2ecf20Sopenharmony_ci * returns : (jornada_ssp_byte(byte)) on success
858c2ecf20Sopenharmony_ci *         : %-ETIMEDOUT on timeout failure
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_ciint jornada_ssp_inout(u8 byte)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	int ret, i;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* true means command byte */
928c2ecf20Sopenharmony_ci	if (byte != TXDUMMY) {
938c2ecf20Sopenharmony_ci		ret = jornada_ssp_byte(byte);
948c2ecf20Sopenharmony_ci		/* Proper return to commands is TxDummy */
958c2ecf20Sopenharmony_ci		if (ret != TXDUMMY) {
968c2ecf20Sopenharmony_ci			for (i = 0; i < 256; i++)/* flushing bus */
978c2ecf20Sopenharmony_ci				if (jornada_ssp_byte(TXDUMMY) == -1)
988c2ecf20Sopenharmony_ci					break;
998c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci	} else /* Exchange TxDummy for data */
1028c2ecf20Sopenharmony_ci		ret = jornada_ssp_byte(TXDUMMY);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return ret;
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(jornada_ssp_inout);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/**
1098c2ecf20Sopenharmony_ci * jornada_ssp_start - enable mcu
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_civoid jornada_ssp_start(void)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&jornada_ssp_lock, jornada_ssp_flags);
1158c2ecf20Sopenharmony_ci	GPCR = GPIO_GPIO25;
1168c2ecf20Sopenharmony_ci	udelay(50);
1178c2ecf20Sopenharmony_ci	return;
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(jornada_ssp_start);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/**
1228c2ecf20Sopenharmony_ci * jornada_ssp_end - disable mcu and turn off lock
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_civoid jornada_ssp_end(void)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	GPSR = GPIO_GPIO25;
1288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&jornada_ssp_lock, jornada_ssp_flags);
1298c2ecf20Sopenharmony_ci	return;
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(jornada_ssp_end);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int jornada_ssp_probe(struct platform_device *dev)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	int ret;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	GPSR = GPIO_GPIO25;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	ret = ssp_init();
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* worked fine, lets not bother with anything else */
1428c2ecf20Sopenharmony_ci	if (!ret) {
1438c2ecf20Sopenharmony_ci		printk(KERN_INFO "SSP: device initialized with irq\n");
1448c2ecf20Sopenharmony_ci		return ret;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	printk(KERN_WARNING "SSP: initialization failed, trying non-irq solution \n");
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* init of Serial 4 port */
1508c2ecf20Sopenharmony_ci	Ser4MCCR0 = 0;
1518c2ecf20Sopenharmony_ci	Ser4SSCR0 = 0x0387;
1528c2ecf20Sopenharmony_ci	Ser4SSCR1 = 0x18;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* clear out any left over data */
1558c2ecf20Sopenharmony_ci	ssp_flush();
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* enable MCU */
1588c2ecf20Sopenharmony_ci	jornada_ssp_start();
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* see if return value makes sense */
1618c2ecf20Sopenharmony_ci	ret = jornada_ssp_inout(GETBRIGHTNESS);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* seems like it worked, just feed it with TxDummy to get rid of data */
1648c2ecf20Sopenharmony_ci	if (ret == TXDUMMY)
1658c2ecf20Sopenharmony_ci		jornada_ssp_inout(TXDUMMY);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	jornada_ssp_end();
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* failed, lets just kill everything */
1708c2ecf20Sopenharmony_ci	if (ret == -ETIMEDOUT) {
1718c2ecf20Sopenharmony_ci		printk(KERN_WARNING "SSP: attempts failed, bailing\n");
1728c2ecf20Sopenharmony_ci		ssp_exit();
1738c2ecf20Sopenharmony_ci		return -ENODEV;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* all fine */
1778c2ecf20Sopenharmony_ci	printk(KERN_INFO "SSP: device initialized\n");
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int jornada_ssp_remove(struct platform_device *dev)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	/* Note that this doesn't actually remove the driver, since theres nothing to remove
1848c2ecf20Sopenharmony_ci	 * It just makes sure everything is turned off */
1858c2ecf20Sopenharmony_ci	GPSR = GPIO_GPIO25;
1868c2ecf20Sopenharmony_ci	ssp_exit();
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistruct platform_driver jornadassp_driver = {
1918c2ecf20Sopenharmony_ci	.probe	= jornada_ssp_probe,
1928c2ecf20Sopenharmony_ci	.remove	= jornada_ssp_remove,
1938c2ecf20Sopenharmony_ci	.driver	= {
1948c2ecf20Sopenharmony_ci		.name	= "jornada_ssp",
1958c2ecf20Sopenharmony_ci	},
1968c2ecf20Sopenharmony_ci};
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int __init jornada_ssp_init(void)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	return platform_driver_register(&jornadassp_driver);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cimodule_init(jornada_ssp_init);
204