]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - arch/arm/mach-pxa/corgi_ssp.c
Pull bugzilla-5653 into release branch
[linux-2.6.git] / arch / arm / mach-pxa / corgi_ssp.c
1 /*
2  *  SSP control code for Sharp Corgi devices
3  *
4  *  Copyright (c) 2004-2005 Richard Purdie
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License version 2 as
8  *  published by the Free Software Foundation.
9  *
10  */
11
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/sched.h>
16 #include <linux/slab.h>
17 #include <linux/delay.h>
18 #include <linux/platform_device.h>
19 #include <asm/hardware.h>
20 #include <asm/mach-types.h>
21
22 #include <asm/arch/ssp.h>
23 #include <asm/arch/pxa-regs.h>
24 #include "sharpsl.h"
25
26 static DEFINE_SPINLOCK(corgi_ssp_lock);
27 static struct ssp_dev corgi_ssp_dev;
28 static struct ssp_state corgi_ssp_state;
29 static struct corgissp_machinfo *ssp_machinfo;
30
31 /*
32  * There are three devices connected to the SSP interface:
33  *   1. A touchscreen controller (TI ADS7846 compatible)
34  *   2. An LCD contoller (with some Backlight functionality)
35  *   3. A battery moinitoring IC (Maxim MAX1111)
36  *
37  * Each device uses a different speed/mode of communication.
38  *
39  * The touchscreen is very sensitive and the most frequently used
40  * so the port is left configured for this.
41  *
42  * Devices are selected using Chip Selects on GPIOs.
43  */
44
45 /*
46  *  ADS7846 Routines
47  */
48 unsigned long corgi_ssp_ads7846_putget(ulong data)
49 {
50         unsigned long ret,flag;
51
52         spin_lock_irqsave(&corgi_ssp_lock, flag);
53         GPCR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846);
54
55         ssp_write_word(&corgi_ssp_dev,data);
56         ret = ssp_read_word(&corgi_ssp_dev);
57
58         GPSR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846);
59         spin_unlock_irqrestore(&corgi_ssp_lock, flag);
60
61         return ret;
62 }
63
64 /*
65  * NOTE: These functions should always be called in interrupt context
66  * and use the _lock and _unlock functions. They are very time sensitive.
67  */
68 void corgi_ssp_ads7846_lock(void)
69 {
70         spin_lock(&corgi_ssp_lock);
71         GPCR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846);
72 }
73
74 void corgi_ssp_ads7846_unlock(void)
75 {
76         GPSR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846);
77         spin_unlock(&corgi_ssp_lock);
78 }
79
80 void corgi_ssp_ads7846_put(ulong data)
81 {
82         ssp_write_word(&corgi_ssp_dev,data);
83 }
84
85 unsigned long corgi_ssp_ads7846_get(void)
86 {
87         return ssp_read_word(&corgi_ssp_dev);
88 }
89
90 EXPORT_SYMBOL(corgi_ssp_ads7846_putget);
91 EXPORT_SYMBOL(corgi_ssp_ads7846_lock);
92 EXPORT_SYMBOL(corgi_ssp_ads7846_unlock);
93 EXPORT_SYMBOL(corgi_ssp_ads7846_put);
94 EXPORT_SYMBOL(corgi_ssp_ads7846_get);
95
96
97 /*
98  *  LCD/Backlight Routines
99  */
100 unsigned long corgi_ssp_dac_put(ulong data)
101 {
102         unsigned long flag, sscr1 = SSCR1_SPH;
103
104         spin_lock_irqsave(&corgi_ssp_lock, flag);
105
106         if (machine_is_spitz() || machine_is_akita() || machine_is_borzoi())
107                 sscr1 = 0;
108
109         ssp_disable(&corgi_ssp_dev);
110         ssp_config(&corgi_ssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), sscr1, 0, SSCR0_SerClkDiv(ssp_machinfo->clk_lcdcon));
111         ssp_enable(&corgi_ssp_dev);
112
113         GPCR(ssp_machinfo->cs_lcdcon) = GPIO_bit(ssp_machinfo->cs_lcdcon);
114         ssp_write_word(&corgi_ssp_dev,data);
115         /* Read null data back from device to prevent SSP overflow */
116         ssp_read_word(&corgi_ssp_dev);
117         GPSR(ssp_machinfo->cs_lcdcon) = GPIO_bit(ssp_machinfo->cs_lcdcon);
118
119         ssp_disable(&corgi_ssp_dev);
120         ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(ssp_machinfo->clk_ads7846));
121         ssp_enable(&corgi_ssp_dev);
122
123         spin_unlock_irqrestore(&corgi_ssp_lock, flag);
124
125         return 0;
126 }
127
128 void corgi_ssp_lcdtg_send(u8 adrs, u8 data)
129 {
130         corgi_ssp_dac_put(((adrs & 0x07) << 5) | (data & 0x1f));
131 }
132
133 void corgi_ssp_blduty_set(int duty)
134 {
135         corgi_ssp_lcdtg_send(0x02,duty);
136 }
137
138 EXPORT_SYMBOL(corgi_ssp_lcdtg_send);
139 EXPORT_SYMBOL(corgi_ssp_blduty_set);
140
141 /*
142  *  Max1111 Routines
143  */
144 int corgi_ssp_max1111_get(ulong data)
145 {
146         unsigned long flag;
147         int voltage,voltage1,voltage2;
148
149         spin_lock_irqsave(&corgi_ssp_lock, flag);
150         GPCR(ssp_machinfo->cs_max1111) = GPIO_bit(ssp_machinfo->cs_max1111);
151         ssp_disable(&corgi_ssp_dev);
152         ssp_config(&corgi_ssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(ssp_machinfo->clk_max1111));
153         ssp_enable(&corgi_ssp_dev);
154
155         udelay(1);
156
157         /* TB1/RB1 */
158         ssp_write_word(&corgi_ssp_dev,data);
159         ssp_read_word(&corgi_ssp_dev); /* null read */
160
161         /* TB12/RB2 */
162         ssp_write_word(&corgi_ssp_dev,0);
163         voltage1=ssp_read_word(&corgi_ssp_dev);
164
165         /* TB13/RB3*/
166         ssp_write_word(&corgi_ssp_dev,0);
167         voltage2=ssp_read_word(&corgi_ssp_dev);
168
169         ssp_disable(&corgi_ssp_dev);
170         ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(ssp_machinfo->clk_ads7846));
171         ssp_enable(&corgi_ssp_dev);
172         GPSR(ssp_machinfo->cs_max1111) = GPIO_bit(ssp_machinfo->cs_max1111);
173         spin_unlock_irqrestore(&corgi_ssp_lock, flag);
174
175         if (voltage1 & 0xc0 || voltage2 & 0x3f)
176                 voltage = -1;
177         else
178                 voltage = ((voltage1 << 2) & 0xfc) | ((voltage2 >> 6) & 0x03);
179
180         return voltage;
181 }
182
183 EXPORT_SYMBOL(corgi_ssp_max1111_get);
184
185 /*
186  *  Support Routines
187  */
188
189 void __init corgi_ssp_set_machinfo(struct corgissp_machinfo *machinfo)
190 {
191         ssp_machinfo = machinfo;
192 }
193
194 static int __init corgi_ssp_probe(struct platform_device *dev)
195 {
196         int ret;
197
198         /* Chip Select - Disable All */
199         pxa_gpio_mode(ssp_machinfo->cs_lcdcon  | GPIO_OUT | GPIO_DFLT_HIGH);
200         pxa_gpio_mode(ssp_machinfo->cs_max1111 | GPIO_OUT | GPIO_DFLT_HIGH);
201         pxa_gpio_mode(ssp_machinfo->cs_ads7846 | GPIO_OUT | GPIO_DFLT_HIGH);
202
203         ret = ssp_init(&corgi_ssp_dev, ssp_machinfo->port, 0);
204
205         if (ret)
206                 printk(KERN_ERR "Unable to register SSP handler!\n");
207         else {
208                 ssp_disable(&corgi_ssp_dev);
209                 ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(ssp_machinfo->clk_ads7846));
210                 ssp_enable(&corgi_ssp_dev);
211         }
212
213         return ret;
214 }
215
216 static int corgi_ssp_remove(struct platform_device *dev)
217 {
218         ssp_exit(&corgi_ssp_dev);
219         return 0;
220 }
221
222 static int corgi_ssp_suspend(struct platform_device *dev, pm_message_t state)
223 {
224         ssp_flush(&corgi_ssp_dev);
225         ssp_save_state(&corgi_ssp_dev,&corgi_ssp_state);
226
227         return 0;
228 }
229
230 static int corgi_ssp_resume(struct platform_device *dev)
231 {
232         GPSR(ssp_machinfo->cs_lcdcon) = GPIO_bit(ssp_machinfo->cs_lcdcon);  /* High - Disable LCD Control/Timing Gen */
233         GPSR(ssp_machinfo->cs_max1111) = GPIO_bit(ssp_machinfo->cs_max1111); /* High - Disable MAX1111*/
234         GPSR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846); /* High - Disable ADS7846*/
235         ssp_restore_state(&corgi_ssp_dev,&corgi_ssp_state);
236         ssp_enable(&corgi_ssp_dev);
237
238         return 0;
239 }
240
241 static struct platform_driver corgissp_driver = {
242         .probe          = corgi_ssp_probe,
243         .remove         = corgi_ssp_remove,
244         .suspend        = corgi_ssp_suspend,
245         .resume         = corgi_ssp_resume,
246         .driver         = {
247                 .name   = "corgi-ssp",
248         },
249 };
250
251 int __init corgi_ssp_init(void)
252 {
253         return platform_driver_register(&corgissp_driver);
254 }
255
256 arch_initcall(corgi_ssp_init);