1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * arch/arm/mac-sa1100/jornada720_ssp.c |
4 | * |
5 | * Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com> |
6 | * Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl> |
7 | * |
8 | * SSP driver for the HP Jornada 710/720/728 |
9 | */ |
10 | |
11 | #include <linux/delay.h> |
12 | #include <linux/errno.h> |
13 | #include <linux/init.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/sched.h> |
18 | #include <linux/io.h> |
19 | |
20 | #include <mach/hardware.h> |
21 | #include <mach/jornada720.h> |
22 | #include <asm/hardware/ssp.h> |
23 | |
24 | static DEFINE_SPINLOCK(jornada_ssp_lock); |
25 | static unsigned long jornada_ssp_flags; |
26 | |
27 | /** |
28 | * jornada_ssp_reverse - reverses input byte |
29 | * @byte: input byte to reverse |
30 | * |
31 | * we need to reverse all data we receive from the mcu due to its physical location |
32 | * returns : 01110111 -> 11101110 |
33 | */ |
34 | inline u8 jornada_ssp_reverse(u8 byte) |
35 | { |
36 | return |
37 | ((0x80 & byte) >> 7) | |
38 | ((0x40 & byte) >> 5) | |
39 | ((0x20 & byte) >> 3) | |
40 | ((0x10 & byte) >> 1) | |
41 | ((0x08 & byte) << 1) | |
42 | ((0x04 & byte) << 3) | |
43 | ((0x02 & byte) << 5) | |
44 | ((0x01 & byte) << 7); |
45 | }; |
46 | EXPORT_SYMBOL(jornada_ssp_reverse); |
47 | |
48 | /** |
49 | * jornada_ssp_byte - waits for ready ssp bus and sends byte |
50 | * @byte: input byte to transmit |
51 | * |
52 | * waits for fifo buffer to clear and then transmits, if it doesn't then we will |
53 | * timeout after <timeout> rounds. Needs mcu running before its called. |
54 | * |
55 | * returns : %mcu output on success |
56 | * : %-ETIMEDOUT on timeout |
57 | */ |
58 | int jornada_ssp_byte(u8 byte) |
59 | { |
60 | int timeout = 400000; |
61 | u16 ret; |
62 | |
63 | while ((GPLR & GPIO_GPIO10)) { |
64 | if (!--timeout) { |
65 | printk(KERN_WARNING "SSP: timeout while waiting for transmit\n" ); |
66 | return -ETIMEDOUT; |
67 | } |
68 | cpu_relax(); |
69 | } |
70 | |
71 | ret = jornada_ssp_reverse(byte) << 8; |
72 | |
73 | ssp_write_word(ret); |
74 | ssp_read_word(&ret); |
75 | |
76 | return jornada_ssp_reverse(byte: ret); |
77 | }; |
78 | EXPORT_SYMBOL(jornada_ssp_byte); |
79 | |
80 | /** |
81 | * jornada_ssp_inout - decide if input is command or trading byte |
82 | * @byte: input byte to send (may be %TXDUMMY) |
83 | * |
84 | * returns : (jornada_ssp_byte(byte)) on success |
85 | * : %-ETIMEDOUT on timeout failure |
86 | */ |
87 | int jornada_ssp_inout(u8 byte) |
88 | { |
89 | int ret, i; |
90 | |
91 | /* true means command byte */ |
92 | if (byte != TXDUMMY) { |
93 | ret = jornada_ssp_byte(byte); |
94 | /* Proper return to commands is TxDummy */ |
95 | if (ret != TXDUMMY) { |
96 | for (i = 0; i < 256; i++)/* flushing bus */ |
97 | if (jornada_ssp_byte(byte: TXDUMMY) == -1) |
98 | break; |
99 | return -ETIMEDOUT; |
100 | } |
101 | } else /* Exchange TxDummy for data */ |
102 | ret = jornada_ssp_byte(byte: TXDUMMY); |
103 | |
104 | return ret; |
105 | }; |
106 | EXPORT_SYMBOL(jornada_ssp_inout); |
107 | |
108 | /** |
109 | * jornada_ssp_start - enable mcu |
110 | * |
111 | */ |
112 | void jornada_ssp_start(void) |
113 | { |
114 | spin_lock_irqsave(&jornada_ssp_lock, jornada_ssp_flags); |
115 | GPCR = GPIO_GPIO25; |
116 | udelay(50); |
117 | return; |
118 | }; |
119 | EXPORT_SYMBOL(jornada_ssp_start); |
120 | |
121 | /** |
122 | * jornada_ssp_end - disable mcu and turn off lock |
123 | * |
124 | */ |
125 | void jornada_ssp_end(void) |
126 | { |
127 | GPSR = GPIO_GPIO25; |
128 | spin_unlock_irqrestore(lock: &jornada_ssp_lock, flags: jornada_ssp_flags); |
129 | return; |
130 | }; |
131 | EXPORT_SYMBOL(jornada_ssp_end); |
132 | |
133 | static int jornada_ssp_probe(struct platform_device *dev) |
134 | { |
135 | int ret; |
136 | |
137 | GPSR = GPIO_GPIO25; |
138 | |
139 | ret = ssp_init(); |
140 | |
141 | /* worked fine, lets not bother with anything else */ |
142 | if (!ret) { |
143 | printk(KERN_INFO "SSP: device initialized with irq\n" ); |
144 | return ret; |
145 | } |
146 | |
147 | printk(KERN_WARNING "SSP: initialization failed, trying non-irq solution \n" ); |
148 | |
149 | /* init of Serial 4 port */ |
150 | Ser4MCCR0 = 0; |
151 | Ser4SSCR0 = 0x0387; |
152 | Ser4SSCR1 = 0x18; |
153 | |
154 | /* clear out any left over data */ |
155 | ssp_flush(); |
156 | |
157 | /* enable MCU */ |
158 | jornada_ssp_start(); |
159 | |
160 | /* see if return value makes sense */ |
161 | ret = jornada_ssp_inout(byte: GETBRIGHTNESS); |
162 | |
163 | /* seems like it worked, just feed it with TxDummy to get rid of data */ |
164 | if (ret == TXDUMMY) |
165 | jornada_ssp_inout(byte: TXDUMMY); |
166 | |
167 | jornada_ssp_end(); |
168 | |
169 | /* failed, lets just kill everything */ |
170 | if (ret == -ETIMEDOUT) { |
171 | printk(KERN_WARNING "SSP: attempts failed, bailing\n" ); |
172 | ssp_exit(); |
173 | return -ENODEV; |
174 | } |
175 | |
176 | /* all fine */ |
177 | printk(KERN_INFO "SSP: device initialized\n" ); |
178 | return 0; |
179 | }; |
180 | |
181 | static void jornada_ssp_remove(struct platform_device *dev) |
182 | { |
183 | /* Note that this doesn't actually remove the driver, since theres nothing to remove |
184 | * It just makes sure everything is turned off */ |
185 | GPSR = GPIO_GPIO25; |
186 | ssp_exit(); |
187 | }; |
188 | |
189 | struct platform_driver jornadassp_driver = { |
190 | .probe = jornada_ssp_probe, |
191 | .remove_new = jornada_ssp_remove, |
192 | .driver = { |
193 | .name = "jornada_ssp" , |
194 | }, |
195 | }; |
196 | |
197 | static int __init jornada_ssp_init(void) |
198 | { |
199 | return platform_driver_register(&jornadassp_driver); |
200 | } |
201 | |
202 | module_init(jornada_ssp_init); |
203 | |