// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mac-sa1100/jornada720_ssp.c * * Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com> * Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl> * * SSP driver for the HP Jornada 710/720/728 */ #include <linux/delay.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/sched.h> #include <linux/io.h> #include <mach/hardware.h> #include <mach/jornada720.h> #include <asm/hardware/ssp.h> static DEFINE_SPINLOCK(jornada_ssp_lock); static unsigned long jornada_ssp_flags; /** * jornada_ssp_reverse - reverses input byte * @byte: input byte to reverse * * we need to reverse all data we receive from the mcu due to its physical location * returns : 01110111 -> 11101110 */ inline u8 jornada_ssp_reverse(u8 byte) { return ((0x80 & byte) >> 7) | ((0x40 & byte) >> 5) | ((0x20 & byte) >> 3) | ((0x10 & byte) >> 1) | ((0x08 & byte) << 1) | ((0x04 & byte) << 3) | ((0x02 & byte) << 5) | ((0x01 & byte) << 7); }; EXPORT_SYMBOL(jornada_ssp_reverse); /** * jornada_ssp_byte - waits for ready ssp bus and sends byte * @byte: input byte to transmit * * waits for fifo buffer to clear and then transmits, if it doesn't then we will * timeout after <timeout> rounds. Needs mcu running before its called. * * returns : %mcu output on success * : %-ETIMEDOUT on timeout */ int jornada_ssp_byte(u8 byte) { int timeout = 400000; u16 ret; while ((GPLR & GPIO_GPIO10)) { if (!--timeout) { printk(KERN_WARNING "SSP: timeout while waiting for transmit\n"); return -ETIMEDOUT; } cpu_relax(); } ret = jornada_ssp_reverse(byte) << 8; ssp_write_word(ret); ssp_read_word(&ret); return jornada_ssp_reverse(ret); }; EXPORT_SYMBOL(jornada_ssp_byte); /** * jornada_ssp_inout - decide if input is command or trading byte * @byte: input byte to send (may be %TXDUMMY) * * returns : (jornada_ssp_byte(byte)) on success * : %-ETIMEDOUT on timeout failure */ int jornada_ssp_inout(u8 byte) { int ret, i; /* true means command byte */ if (byte != TXDUMMY) { ret = jornada_ssp_byte(byte); /* Proper return to commands is TxDummy */ if (ret != TXDUMMY) { for (i = 0; i < 256; i++)/* flushing bus */ if (jornada_ssp_byte(TXDUMMY) == -1) break; return -ETIMEDOUT; } } else /* Exchange TxDummy for data */ ret = jornada_ssp_byte(TXDUMMY); return ret; }; EXPORT_SYMBOL(jornada_ssp_inout); /** * jornada_ssp_start - enable mcu * */ void jornada_ssp_start(void) { spin_lock_irqsave(&jornada_ssp_lock, jornada_ssp_flags); GPCR = GPIO_GPIO25; udelay(50); return; }; EXPORT_SYMBOL(jornada_ssp_start); /** * jornada_ssp_end - disable mcu and turn off lock * */ void jornada_ssp_end(void) { GPSR = GPIO_GPIO25; spin_unlock_irqrestore(&jornada_ssp_lock, jornada_ssp_flags); return; }; EXPORT_SYMBOL(jornada_ssp_end); static int jornada_ssp_probe(struct platform_device *dev) { int ret; GPSR = GPIO_GPIO25; ret = ssp_init(); /* worked fine, lets not bother with anything else */ if (!ret) { printk(KERN_INFO "SSP: device initialized with irq\n"); return ret; } printk(KERN_WARNING "SSP: initialization failed, trying non-irq solution \n"); /* init of Serial 4 port */ Ser4MCCR0 = 0; Ser4SSCR0 = 0x0387; Ser4SSCR1 = 0x18; /* clear out any left over data */ ssp_flush(); /* enable MCU */ jornada_ssp_start(); /* see if return value makes sense */ ret = jornada_ssp_inout(GETBRIGHTNESS); /* seems like it worked, just feed it with TxDummy to get rid of data */ if (ret == TXDUMMY) jornada_ssp_inout(TXDUMMY); jornada_ssp_end(); /* failed, lets just kill everything */ if (ret == -ETIMEDOUT) { printk(KERN_WARNING "SSP: attempts failed, bailing\n"); ssp_exit(); return -ENODEV; } /* all fine */ printk(KERN_INFO "SSP: device initialized\n"); return 0; }; static void jornada_ssp_remove(struct platform_device *dev) { /* Note that this doesn't actually remove the driver, since theres nothing to remove * It just makes sure everything is turned off */ GPSR = GPIO_GPIO25; ssp_exit(); }; struct platform_driver jornadassp_driver = { .probe = jornada_ssp_probe, .remove_new = jornada_ssp_remove, .driver = { .name = "jornada_ssp", }, }; static int __init jornada_ssp_init(void) { return platform_driver_register(&jornadassp_driver); } module_init(jornada_ssp_init);