/* * SSP control code for Sharp Corgi devices * * Copyright (c) 2004-2005 Richard Purdie * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include "sharpsl.h" static DEFINE_SPINLOCK(corgi_ssp_lock); static struct ssp_dev corgi_ssp_dev; static struct ssp_state corgi_ssp_state; static struct corgissp_machinfo *ssp_machinfo; /* * There are three devices connected to the SSP interface: * 1. A touchscreen controller (TI ADS7846 compatible) * 2. An LCD contoller (with some Backlight functionality) * 3. A battery moinitoring IC (Maxim MAX1111) * * Each device uses a different speed/mode of communication. * * The touchscreen is very sensitive and the most frequently used * so the port is left configured for this. * * Devices are selected using Chip Selects on GPIOs. */ /* * ADS7846 Routines */ unsigned long corgi_ssp_ads7846_putget(ulong data) { unsigned long ret,flag; spin_lock_irqsave(&corgi_ssp_lock, flag); GPCR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846); ssp_write_word(&corgi_ssp_dev,data); ret = ssp_read_word(&corgi_ssp_dev); GPSR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846); spin_unlock_irqrestore(&corgi_ssp_lock, flag); return ret; } /* * NOTE: These functions should always be called in interrupt context * and use the _lock and _unlock functions. They are very time sensitive. */ void corgi_ssp_ads7846_lock(void) { spin_lock(&corgi_ssp_lock); GPCR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846); } void corgi_ssp_ads7846_unlock(void) { GPSR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846); spin_unlock(&corgi_ssp_lock); } void corgi_ssp_ads7846_put(ulong data) { ssp_write_word(&corgi_ssp_dev,data); } unsigned long corgi_ssp_ads7846_get(void) { return ssp_read_word(&corgi_ssp_dev); } EXPORT_SYMBOL(corgi_ssp_ads7846_putget); EXPORT_SYMBOL(corgi_ssp_ads7846_lock); EXPORT_SYMBOL(corgi_ssp_ads7846_unlock); EXPORT_SYMBOL(corgi_ssp_ads7846_put); EXPORT_SYMBOL(corgi_ssp_ads7846_get); /* * LCD/Backlight Routines */ unsigned long corgi_ssp_dac_put(ulong data) { unsigned long flag, sscr1 = SSCR1_SPH; spin_lock_irqsave(&corgi_ssp_lock, flag); if (machine_is_spitz() || machine_is_akita() || machine_is_borzoi()) sscr1 = 0; ssp_disable(&corgi_ssp_dev); ssp_config(&corgi_ssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), sscr1, 0, SSCR0_SerClkDiv(ssp_machinfo->clk_lcdcon)); ssp_enable(&corgi_ssp_dev); GPCR(ssp_machinfo->cs_lcdcon) = GPIO_bit(ssp_machinfo->cs_lcdcon); ssp_write_word(&corgi_ssp_dev,data); /* Read null data back from device to prevent SSP overflow */ ssp_read_word(&corgi_ssp_dev); GPSR(ssp_machinfo->cs_lcdcon) = GPIO_bit(ssp_machinfo->cs_lcdcon); ssp_disable(&corgi_ssp_dev); ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(ssp_machinfo->clk_ads7846)); ssp_enable(&corgi_ssp_dev); spin_unlock_irqrestore(&corgi_ssp_lock, flag); return 0; } void corgi_ssp_lcdtg_send(u8 adrs, u8 data) { corgi_ssp_dac_put(((adrs & 0x07) << 5) | (data & 0x1f)); } void corgi_ssp_blduty_set(int duty) { corgi_ssp_lcdtg_send(0x02,duty); } EXPORT_SYMBOL(corgi_ssp_lcdtg_send); EXPORT_SYMBOL(corgi_ssp_blduty_set); /* * Max1111 Routines */ int corgi_ssp_max1111_get(ulong data) { unsigned long flag; int voltage,voltage1,voltage2; spin_lock_irqsave(&corgi_ssp_lock, flag); GPCR(ssp_machinfo->cs_max1111) = GPIO_bit(ssp_machinfo->cs_max1111); ssp_disable(&corgi_ssp_dev); ssp_config(&corgi_ssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(ssp_machinfo->clk_max1111)); ssp_enable(&corgi_ssp_dev); udelay(1); /* TB1/RB1 */ ssp_write_word(&corgi_ssp_dev,data); ssp_read_word(&corgi_ssp_dev); /* null read */ /* TB12/RB2 */ ssp_write_word(&corgi_ssp_dev,0); voltage1=ssp_read_word(&corgi_ssp_dev); /* TB13/RB3*/ ssp_write_word(&corgi_ssp_dev,0); voltage2=ssp_read_word(&corgi_ssp_dev); ssp_disable(&corgi_ssp_dev); ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(ssp_machinfo->clk_ads7846)); ssp_enable(&corgi_ssp_dev); GPSR(ssp_machinfo->cs_max1111) = GPIO_bit(ssp_machinfo->cs_max1111); spin_unlock_irqrestore(&corgi_ssp_lock, flag); if (voltage1 & 0xc0 || voltage2 & 0x3f) voltage = -1; else voltage = ((voltage1 << 2) & 0xfc) | ((voltage2 >> 6) & 0x03); return voltage; } EXPORT_SYMBOL(corgi_ssp_max1111_get); /* * Support Routines */ void __init corgi_ssp_set_machinfo(struct corgissp_machinfo *machinfo) { ssp_machinfo = machinfo; } static int __init corgi_ssp_probe(struct platform_device *dev) { int ret; /* Chip Select - Disable All */ GPDR(ssp_machinfo->cs_lcdcon) |= GPIO_bit(ssp_machinfo->cs_lcdcon); /* output */ GPSR(ssp_machinfo->cs_lcdcon) = GPIO_bit(ssp_machinfo->cs_lcdcon); /* High - Disable LCD Control/Timing Gen */ GPDR(ssp_machinfo->cs_max1111) |= GPIO_bit(ssp_machinfo->cs_max1111); /* output */ GPSR(ssp_machinfo->cs_max1111) = GPIO_bit(ssp_machinfo->cs_max1111); /* High - Disable MAX1111*/ GPDR(ssp_machinfo->cs_ads7846) |= GPIO_bit(ssp_machinfo->cs_ads7846); /* output */ GPSR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846); /* High - Disable ADS7846*/ ret = ssp_init(&corgi_ssp_dev, ssp_machinfo->port, 0); if (ret) printk(KERN_ERR "Unable to register SSP handler!\n"); else { ssp_disable(&corgi_ssp_dev); ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(ssp_machinfo->clk_ads7846)); ssp_enable(&corgi_ssp_dev); } return ret; } static int corgi_ssp_remove(struct platform_device *dev) { ssp_exit(&corgi_ssp_dev); return 0; } static int corgi_ssp_suspend(struct platform_device *dev, pm_message_t state) { ssp_flush(&corgi_ssp_dev); ssp_save_state(&corgi_ssp_dev,&corgi_ssp_state); return 0; } static int corgi_ssp_resume(struct platform_device *dev) { GPSR(ssp_machinfo->cs_lcdcon) = GPIO_bit(ssp_machinfo->cs_lcdcon); /* High - Disable LCD Control/Timing Gen */ GPSR(ssp_machinfo->cs_max1111) = GPIO_bit(ssp_machinfo->cs_max1111); /* High - Disable MAX1111*/ GPSR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846); /* High - Disable ADS7846*/ ssp_restore_state(&corgi_ssp_dev,&corgi_ssp_state); ssp_enable(&corgi_ssp_dev); return 0; } static struct platform_driver corgissp_driver = { .probe = corgi_ssp_probe, .remove = corgi_ssp_remove, .suspend = corgi_ssp_suspend, .resume = corgi_ssp_resume, .driver = { .name = "corgi-ssp", }, }; int __init corgi_ssp_init(void) { return platform_driver_register(&corgissp_driver); } arch_initcall(corgi_ssp_init);