1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Compaq iPAQ h3xxx Atmel microcontroller companion support
4 *
5 * This is an Atmel AT90LS8535 with a special flashed-in firmware that
6 * implements the special protocol used by this driver.
7 *
8 * based on previous kernel 2.4 version by Andrew Christian
9 * Author : Alessandro Gardich <gremlin@gremlin.it>
10 * Author : Dmitry Artamonow <mad_soft@inbox.ru>
11 * Author : Linus Walleij <linus.walleij@linaro.org>
12 */
13
14 #include <linux/module.h>
15 #include <linux/init.h>
16 #include <linux/interrupt.h>
17 #include <linux/pm.h>
18 #include <linux/delay.h>
19 #include <linux/device.h>
20 #include <linux/platform_device.h>
21 #include <linux/io.h>
22 #include <linux/mfd/core.h>
23 #include <linux/mfd/ipaq-micro.h>
24 #include <linux/string.h>
25 #include <linux/string_choices.h>
26 #include <linux/random.h>
27 #include <linux/slab.h>
28 #include <linux/list.h>
29
30 #include <mach/hardware.h>
31
ipaq_micro_trigger_tx(struct ipaq_micro * micro)32 static void ipaq_micro_trigger_tx(struct ipaq_micro *micro)
33 {
34 struct ipaq_micro_txdev *tx = µ->tx;
35 struct ipaq_micro_msg *msg = micro->msg;
36 int i, bp;
37 u8 checksum;
38 u32 val;
39
40 bp = 0;
41 tx->buf[bp++] = CHAR_SOF;
42
43 checksum = ((msg->id & 0x0f) << 4) | (msg->tx_len & 0x0f);
44 tx->buf[bp++] = checksum;
45
46 for (i = 0; i < msg->tx_len; i++) {
47 tx->buf[bp++] = msg->tx_data[i];
48 checksum += msg->tx_data[i];
49 }
50
51 tx->buf[bp++] = checksum;
52 tx->len = bp;
53 tx->index = 0;
54
55 /* Enable interrupt */
56 val = readl(micro->base + UTCR3);
57 val |= UTCR3_TIE;
58 writel(val, micro->base + UTCR3);
59 }
60
ipaq_micro_tx_msg(struct ipaq_micro * micro,struct ipaq_micro_msg * msg)61 int ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg)
62 {
63 unsigned long flags;
64
65 dev_dbg(micro->dev, "TX msg: %02x, %d bytes\n", msg->id, msg->tx_len);
66
67 spin_lock_irqsave(µ->lock, flags);
68 if (micro->msg) {
69 list_add_tail(&msg->node, µ->queue);
70 spin_unlock_irqrestore(µ->lock, flags);
71 return 0;
72 }
73 micro->msg = msg;
74 ipaq_micro_trigger_tx(micro);
75 spin_unlock_irqrestore(µ->lock, flags);
76 return 0;
77 }
78 EXPORT_SYMBOL(ipaq_micro_tx_msg);
79
micro_rx_msg(struct ipaq_micro * micro,u8 id,int len,u8 * data)80 static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data)
81 {
82 dev_dbg(micro->dev, "RX msg: %02x, %d bytes\n", id, len);
83
84 spin_lock(µ->lock);
85 switch (id) {
86 case MSG_VERSION:
87 case MSG_EEPROM_READ:
88 case MSG_EEPROM_WRITE:
89 case MSG_BACKLIGHT:
90 case MSG_NOTIFY_LED:
91 case MSG_THERMAL_SENSOR:
92 case MSG_BATTERY:
93 /* Handle synchronous messages */
94 if (micro->msg && micro->msg->id == id) {
95 struct ipaq_micro_msg *msg = micro->msg;
96
97 memcpy(msg->rx_data, data, len);
98 msg->rx_len = len;
99 complete(µ->msg->ack);
100 if (!list_empty(µ->queue)) {
101 micro->msg = list_entry(micro->queue.next,
102 struct ipaq_micro_msg,
103 node);
104 list_del_init(µ->msg->node);
105 ipaq_micro_trigger_tx(micro);
106 } else
107 micro->msg = NULL;
108 dev_dbg(micro->dev, "OK RX message 0x%02x\n", id);
109 } else {
110 dev_err(micro->dev,
111 "out of band RX message 0x%02x\n", id);
112 if (!micro->msg)
113 dev_info(micro->dev, "no message queued\n");
114 else
115 dev_info(micro->dev, "expected message %02x\n",
116 micro->msg->id);
117 }
118 break;
119 case MSG_KEYBOARD:
120 if (micro->key)
121 micro->key(micro->key_data, len, data);
122 else
123 dev_dbg(micro->dev, "key message ignored, no handle\n");
124 break;
125 case MSG_TOUCHSCREEN:
126 if (micro->ts)
127 micro->ts(micro->ts_data, len, data);
128 else
129 dev_dbg(micro->dev, "touchscreen message ignored, no handle\n");
130 break;
131 default:
132 dev_err(micro->dev,
133 "unknown msg %d [%d] %*ph\n", id, len, len, data);
134 break;
135 }
136 spin_unlock(µ->lock);
137 }
138
micro_process_char(struct ipaq_micro * micro,u8 ch)139 static void micro_process_char(struct ipaq_micro *micro, u8 ch)
140 {
141 struct ipaq_micro_rxdev *rx = µ->rx;
142
143 switch (rx->state) {
144 case STATE_SOF: /* Looking for SOF */
145 if (ch == CHAR_SOF)
146 rx->state = STATE_ID; /* Next byte is the id and len */
147 break;
148 case STATE_ID: /* Looking for id and len byte */
149 rx->id = (ch & 0xf0) >> 4;
150 rx->len = (ch & 0x0f);
151 rx->index = 0;
152 rx->chksum = ch;
153 rx->state = (rx->len > 0) ? STATE_DATA : STATE_CHKSUM;
154 break;
155 case STATE_DATA: /* Looking for 'len' data bytes */
156 rx->chksum += ch;
157 rx->buf[rx->index] = ch;
158 if (++rx->index == rx->len)
159 rx->state = STATE_CHKSUM;
160 break;
161 case STATE_CHKSUM: /* Looking for the checksum */
162 if (ch == rx->chksum)
163 micro_rx_msg(micro, rx->id, rx->len, rx->buf);
164 rx->state = STATE_SOF;
165 break;
166 }
167 }
168
micro_rx_chars(struct ipaq_micro * micro)169 static void micro_rx_chars(struct ipaq_micro *micro)
170 {
171 u32 status, ch;
172
173 while ((status = readl(micro->base + UTSR1)) & UTSR1_RNE) {
174 ch = readl(micro->base + UTDR);
175 if (status & UTSR1_PRE)
176 dev_err(micro->dev, "rx: parity error\n");
177 else if (status & UTSR1_FRE)
178 dev_err(micro->dev, "rx: framing error\n");
179 else if (status & UTSR1_ROR)
180 dev_err(micro->dev, "rx: overrun error\n");
181 micro_process_char(micro, ch);
182 }
183 }
184
ipaq_micro_get_version(struct ipaq_micro * micro)185 static void ipaq_micro_get_version(struct ipaq_micro *micro)
186 {
187 struct ipaq_micro_msg msg = {
188 .id = MSG_VERSION,
189 };
190
191 ipaq_micro_tx_msg_sync(micro, &msg);
192 if (msg.rx_len == 4) {
193 memcpy(micro->version, msg.rx_data, 4);
194 micro->version[4] = '\0';
195 } else if (msg.rx_len == 9) {
196 memcpy(micro->version, msg.rx_data, 4);
197 micro->version[4] = '\0';
198 /* Bytes 4-7 are "pack", byte 8 is "boot type" */
199 } else {
200 dev_err(micro->dev,
201 "illegal version message %d bytes\n", msg.rx_len);
202 }
203 }
204
ipaq_micro_eeprom_read(struct ipaq_micro * micro,u8 address,u8 len,u8 * data)205 static void ipaq_micro_eeprom_read(struct ipaq_micro *micro,
206 u8 address, u8 len, u8 *data)
207 {
208 struct ipaq_micro_msg msg = {
209 .id = MSG_EEPROM_READ,
210 };
211 u8 i;
212
213 for (i = 0; i < len; i++) {
214 msg.tx_data[0] = address + i;
215 msg.tx_data[1] = 1;
216 msg.tx_len = 2;
217 ipaq_micro_tx_msg_sync(micro, &msg);
218 memcpy(data + (i * 2), msg.rx_data, 2);
219 }
220 }
221
ipaq_micro_str(u8 * wchar,u8 len)222 static char *ipaq_micro_str(u8 *wchar, u8 len)
223 {
224 char retstr[256];
225 u8 i;
226
227 for (i = 0; i < len / 2; i++)
228 retstr[i] = wchar[i * 2];
229 return kstrdup(retstr, GFP_KERNEL);
230 }
231
ipaq_micro_to_u16(u8 * data)232 static u16 ipaq_micro_to_u16(u8 *data)
233 {
234 return data[1] << 8 | data[0];
235 }
236
ipaq_micro_eeprom_dump(struct ipaq_micro * micro)237 static void __init ipaq_micro_eeprom_dump(struct ipaq_micro *micro)
238 {
239 u8 dump[256];
240 char *str;
241
242 ipaq_micro_eeprom_read(micro, 0, 128, dump);
243 str = ipaq_micro_str(dump, 10);
244 if (str) {
245 dev_info(micro->dev, "HW version %s\n", str);
246 kfree(str);
247 }
248 str = ipaq_micro_str(dump+10, 40);
249 if (str) {
250 dev_info(micro->dev, "serial number: %s\n", str);
251 /* Feed the random pool with this */
252 add_device_randomness(str, strlen(str));
253 kfree(str);
254 }
255 str = ipaq_micro_str(dump+50, 20);
256 if (str) {
257 dev_info(micro->dev, "module ID: %s\n", str);
258 kfree(str);
259 }
260 str = ipaq_micro_str(dump+70, 10);
261 if (str) {
262 dev_info(micro->dev, "product revision: %s\n", str);
263 kfree(str);
264 }
265 dev_info(micro->dev, "product ID: %u\n", ipaq_micro_to_u16(dump+80));
266 dev_info(micro->dev, "frame rate: %u fps\n",
267 ipaq_micro_to_u16(dump+82));
268 dev_info(micro->dev, "page mode: %u\n", ipaq_micro_to_u16(dump+84));
269 dev_info(micro->dev, "country ID: %u\n", ipaq_micro_to_u16(dump+86));
270 dev_info(micro->dev, "color display: %s\n",
271 str_yes_no(ipaq_micro_to_u16(dump + 88)));
272 dev_info(micro->dev, "ROM size: %u MiB\n", ipaq_micro_to_u16(dump+90));
273 dev_info(micro->dev, "RAM size: %u KiB\n", ipaq_micro_to_u16(dump+92));
274 dev_info(micro->dev, "screen: %u x %u\n",
275 ipaq_micro_to_u16(dump+94), ipaq_micro_to_u16(dump+96));
276 }
277
micro_tx_chars(struct ipaq_micro * micro)278 static void micro_tx_chars(struct ipaq_micro *micro)
279 {
280 struct ipaq_micro_txdev *tx = µ->tx;
281 u32 val;
282
283 while ((tx->index < tx->len) &&
284 (readl(micro->base + UTSR1) & UTSR1_TNF)) {
285 writel(tx->buf[tx->index], micro->base + UTDR);
286 tx->index++;
287 }
288
289 /* Stop interrupts */
290 val = readl(micro->base + UTCR3);
291 val &= ~UTCR3_TIE;
292 writel(val, micro->base + UTCR3);
293 }
294
micro_reset_comm(struct ipaq_micro * micro)295 static void micro_reset_comm(struct ipaq_micro *micro)
296 {
297 struct ipaq_micro_rxdev *rx = µ->rx;
298 u32 val;
299
300 if (micro->msg)
301 complete(µ->msg->ack);
302
303 /* Initialize Serial channel protocol frame */
304 rx->state = STATE_SOF; /* Reset the state machine */
305
306 /* Set up interrupts */
307 writel(0x01, micro->sdlc + 0x0); /* Select UART mode */
308
309 /* Clean up CR3 */
310 writel(0x0, micro->base + UTCR3);
311
312 /* Format: 8N1 */
313 writel(UTCR0_8BitData | UTCR0_1StpBit, micro->base + UTCR0);
314
315 /* Baud rate: 115200 */
316 writel(0x0, micro->base + UTCR1);
317 writel(0x1, micro->base + UTCR2);
318
319 /* Clear SR0 */
320 writel(0xff, micro->base + UTSR0);
321
322 /* Enable RX int, disable TX int */
323 writel(UTCR3_TXE | UTCR3_RXE | UTCR3_RIE, micro->base + UTCR3);
324 val = readl(micro->base + UTCR3);
325 val &= ~UTCR3_TIE;
326 writel(val, micro->base + UTCR3);
327 }
328
micro_serial_isr(int irq,void * dev_id)329 static irqreturn_t micro_serial_isr(int irq, void *dev_id)
330 {
331 struct ipaq_micro *micro = dev_id;
332 struct ipaq_micro_txdev *tx = µ->tx;
333 u32 status;
334
335 status = readl(micro->base + UTSR0);
336 do {
337 if (status & (UTSR0_RID | UTSR0_RFS)) {
338 if (status & UTSR0_RID)
339 /* Clear the Receiver IDLE bit */
340 writel(UTSR0_RID, micro->base + UTSR0);
341 micro_rx_chars(micro);
342 }
343
344 /* Clear break bits */
345 if (status & (UTSR0_RBB | UTSR0_REB))
346 writel(status & (UTSR0_RBB | UTSR0_REB),
347 micro->base + UTSR0);
348
349 if (status & UTSR0_TFS)
350 micro_tx_chars(micro);
351
352 status = readl(micro->base + UTSR0);
353
354 } while (((tx->index < tx->len) && (status & UTSR0_TFS)) ||
355 (status & (UTSR0_RFS | UTSR0_RID)));
356
357 return IRQ_HANDLED;
358 }
359
360 static const struct mfd_cell micro_cells[] = {
361 { .name = "ipaq-micro-backlight", },
362 { .name = "ipaq-micro-battery", },
363 { .name = "ipaq-micro-keys", },
364 { .name = "ipaq-micro-ts", },
365 { .name = "ipaq-micro-leds", },
366 };
367
micro_resume(struct device * dev)368 static int __maybe_unused micro_resume(struct device *dev)
369 {
370 struct ipaq_micro *micro = dev_get_drvdata(dev);
371
372 micro_reset_comm(micro);
373 mdelay(10);
374
375 return 0;
376 }
377
micro_probe(struct platform_device * pdev)378 static int __init micro_probe(struct platform_device *pdev)
379 {
380 struct ipaq_micro *micro;
381 int ret;
382 int irq;
383
384 micro = devm_kzalloc(&pdev->dev, sizeof(*micro), GFP_KERNEL);
385 if (!micro)
386 return -ENOMEM;
387
388 micro->dev = &pdev->dev;
389
390 micro->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
391 if (IS_ERR(micro->base))
392 return PTR_ERR(micro->base);
393
394 micro->sdlc = devm_platform_ioremap_resource(pdev, 1);
395 if (IS_ERR(micro->sdlc))
396 return PTR_ERR(micro->sdlc);
397
398 micro_reset_comm(micro);
399
400 irq = platform_get_irq(pdev, 0);
401 if (irq < 0)
402 return -EINVAL;
403 ret = devm_request_irq(&pdev->dev, irq, micro_serial_isr,
404 IRQF_SHARED, "ipaq-micro",
405 micro);
406 if (ret) {
407 dev_err(&pdev->dev, "unable to grab serial port IRQ\n");
408 return ret;
409 } else
410 dev_info(&pdev->dev, "grabbed serial port IRQ\n");
411
412 spin_lock_init(µ->lock);
413 INIT_LIST_HEAD(µ->queue);
414 platform_set_drvdata(pdev, micro);
415
416 ret = mfd_add_devices(&pdev->dev, pdev->id, micro_cells,
417 ARRAY_SIZE(micro_cells), NULL, 0, NULL);
418 if (ret) {
419 dev_err(&pdev->dev, "error adding MFD cells");
420 return ret;
421 }
422
423 /* Check version */
424 ipaq_micro_get_version(micro);
425 dev_info(&pdev->dev, "Atmel micro ASIC version %s\n", micro->version);
426 ipaq_micro_eeprom_dump(micro);
427
428 return 0;
429 }
430
431 static const struct dev_pm_ops micro_dev_pm_ops = {
432 SET_SYSTEM_SLEEP_PM_OPS(NULL, micro_resume)
433 };
434
435 static struct platform_driver micro_device_driver = {
436 .driver = {
437 .name = "ipaq-h3xxx-micro",
438 .pm = µ_dev_pm_ops,
439 .suppress_bind_attrs = true,
440 },
441 };
442 builtin_platform_driver_probe(micro_device_driver, micro_probe);
443