1 /*
2  *  linux/arch/arm/mach-sa1100/ssp.c
3  *
4  *  Copyright (C) 2003 Russell King.
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  *  Generic SSP driver.  This provides the generic core for simple
11  *  IO-based SSP applications.
12  */
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/sched.h>
16 #include <linux/errno.h>
17 #include <linux/interrupt.h>
18 #include <linux/ioport.h>
19 #include <linux/init.h>
20 
21 #include <asm/io.h>
22 #include <asm/irq.h>
23 #include <asm/hardware.h>
24 #include <asm/hardware/ssp.h>
25 
ssp_interrupt(int irq,void * dev_id,struct pt_regs * regs)26 static void ssp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
27 {
28 	unsigned int status = Ser4SSSR;
29 
30 	if (status & SSSR_ROR) {
31 		printk(KERN_WARNING "SSP: receiver overrun\n");
32 	}
33 
34 	Ser4SSSR = SSSR_ROR;
35 }
36 
37 /**
38  * ssp_write_word - write a word to the SSP port
39  * @data: 16-bit, MSB justified data to write.
40  *
41  * Wait for a free entry in the SSP transmit FIFO, and write a data
42  * word to the SSP port.
43  *
44  * The caller is expected to perform the necessary locking.
45  *
46  * Returns:
47  *   %-ETIMEDOUT	timeout occurred (for future)
48  *   0			success
49  */
ssp_write_word(u16 data)50 int ssp_write_word(u16 data)
51 {
52 	while (!(Ser4SSSR & SSSR_TNF))
53 		cpu_relax();
54 
55 	Ser4SSDR = data;
56 
57 	return 0;
58 }
59 
60 /**
61  * ssp_read_word - read a word from the SSP port
62  *
63  * Wait for a data word in the SSP receive FIFO, and return the
64  * received data.  Data is LSB justified.
65  *
66  * Note: Currently, if data is not expected to be received, this
67  * function will wait for ever.
68  *
69  * The caller is expected to perform the necessary locking.
70  *
71  * Returns:
72  *   %-ETIMEDOUT	timeout occurred (for future)
73  *   16-bit data	success
74  */
ssp_read_word(void)75 int ssp_read_word(void)
76 {
77 	while (!(Ser4SSSR & SSSR_RNE))
78 		cpu_relax();
79 
80 	return Ser4SSDR;
81 }
82 
83 /**
84  * ssp_flush - flush the transmit and receive FIFOs
85  *
86  * Wait for the SSP to idle, and ensure that the receive FIFO
87  * is empty.
88  *
89  * The caller is expected to perform the necessary locking.
90  */
ssp_flush(void)91 void ssp_flush(void)
92 {
93 	do {
94 		while (Ser4SSSR & SSSR_RNE) {
95 			(void) Ser4SSDR;
96 		}
97 	} while (Ser4SSSR & SSSR_BSY);
98 }
99 
100 /**
101  * ssp_enable - enable the SSP port
102  *
103  * Turn on the SSP port.
104  */
ssp_enable(void)105 void ssp_enable(void)
106 {
107 	Ser4SSCR0 |= SSCR0_SSE;
108 }
109 
110 /**
111  * ssp_disable - shut down the SSP port
112  *
113  * Turn off the SSP port, optionally powering it down.
114  */
ssp_disable(void)115 void ssp_disable(void)
116 {
117 	Ser4SSCR0 &= ~SSCR0_SSE;
118 }
119 
120 /**
121  * ssp_save_state - save the SSP configuration
122  * @ssp: pointer to structure to save SSP configuration
123  *
124  * Save the configured SSP state for suspend.
125  */
ssp_save_state(struct ssp_state * ssp)126 void ssp_save_state(struct ssp_state *ssp)
127 {
128 	ssp->cr0 = Ser4SSCR0;
129 	ssp->cr1 = Ser4SSCR1;
130 
131 	Ser4SSCR0 &= ~SSCR0_SSE;
132 }
133 
134 /**
135  * ssp_restore_state - restore a previously saved SSP configuration
136  * @ssp: pointer to configuration saved by ssp_save_state
137  *
138  * Restore the SSP configuration saved previously by ssp_save_state.
139  */
ssp_restore_state(struct ssp_state * ssp)140 void ssp_restore_state(struct ssp_state *ssp)
141 {
142 	Ser4SSSR = SSSR_ROR;
143 
144 	Ser4SSCR0 = ssp->cr0 & ~SSCR0_SSE;
145 	Ser4SSCR1 = ssp->cr1;
146 	Ser4SSCR0 = ssp->cr0;
147 }
148 
149 /**
150  * ssp_init - setup the SSP port
151  *
152  * initialise and claim resources for the SSP port.
153  *
154  * Returns:
155  *   %-ENODEV	if the SSP port is unavailable
156  *   %-EBUSY	if the resources are already in use
157  *   %0		on success
158  */
ssp_init(void)159 int ssp_init(void)
160 {
161 	int ret;
162 
163 	if (!(PPAR & PPAR_SPR) && (Ser4MCCR0 & MCCR0_MCE))
164 		return -ENODEV;
165 
166 	if (!request_mem_region(__PREG(Ser4SSCR0), 0x18, "SSP")) {
167 		return -EBUSY;
168 	}
169 
170 	Ser4SSSR = SSSR_ROR;
171 
172 	ret = request_irq(IRQ_Ser4SSP, ssp_interrupt, 0, "SSP", NULL);
173 	if (ret)
174 		goto out_region;
175 
176 	return 0;
177 
178  out_region:
179 	release_mem_region(__PREG(Ser4SSCR0), 0x18);
180 	return ret;
181 }
182 
183 /**
184  * ssp_exit - undo the effects of ssp_init
185  *
186  * release and free resources for the SSP port.
187  */
ssp_exit(void)188 void ssp_exit(void)
189 {
190 	Ser4SSCR0 &= ~SSCR0_SSE;
191 
192 	free_irq(IRQ_Ser4SSP, NULL);
193 	release_mem_region(__PREG(Ser4SSCR0), 0x18);
194 }
195 
196 MODULE_AUTHOR("Russell King");
197 MODULE_DESCRIPTION("SA11x0 SSP PIO driver");
198 MODULE_LICENSE("GPL");
199 
200 EXPORT_SYMBOL(ssp_write_word);
201 EXPORT_SYMBOL(ssp_read_word);
202 EXPORT_SYMBOL(ssp_flush);
203 EXPORT_SYMBOL(ssp_enable);
204 EXPORT_SYMBOL(ssp_disable);
205 EXPORT_SYMBOL(ssp_save_state);
206 EXPORT_SYMBOL(ssp_restore_state);
207 EXPORT_SYMBOL(ssp_init);
208 EXPORT_SYMBOL(ssp_exit);
209