18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Device driver for the Cuda and Egret system controllers found on PowerMacs 48c2ecf20Sopenharmony_ci * and 68k Macs. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * The Cuda or Egret is a 6805 microcontroller interfaced to the 6522 VIA. 78c2ecf20Sopenharmony_ci * This MCU controls system power, Parameter RAM, Real Time Clock and the 88c2ecf20Sopenharmony_ci * Apple Desktop Bus (ADB) that connects to the keyboard and mouse. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci#include <stdarg.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/adb.h> 188c2ecf20Sopenharmony_ci#include <linux/cuda.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC 228c2ecf20Sopenharmony_ci#include <asm/prom.h> 238c2ecf20Sopenharmony_ci#include <asm/machdep.h> 248c2ecf20Sopenharmony_ci#else 258c2ecf20Sopenharmony_ci#include <asm/macintosh.h> 268c2ecf20Sopenharmony_ci#include <asm/macints.h> 278c2ecf20Sopenharmony_ci#include <asm/mac_via.h> 288c2ecf20Sopenharmony_ci#endif 298c2ecf20Sopenharmony_ci#include <asm/io.h> 308c2ecf20Sopenharmony_ci#include <linux/init.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic volatile unsigned char __iomem *via; 338c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cuda_lock); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* VIA registers - spaced 0x200 bytes apart */ 368c2ecf20Sopenharmony_ci#define RS 0x200 /* skip between registers */ 378c2ecf20Sopenharmony_ci#define B 0 /* B-side data */ 388c2ecf20Sopenharmony_ci#define A RS /* A-side data */ 398c2ecf20Sopenharmony_ci#define DIRB (2*RS) /* B-side direction (1=output) */ 408c2ecf20Sopenharmony_ci#define DIRA (3*RS) /* A-side direction (1=output) */ 418c2ecf20Sopenharmony_ci#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ 428c2ecf20Sopenharmony_ci#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ 438c2ecf20Sopenharmony_ci#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ 448c2ecf20Sopenharmony_ci#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ 458c2ecf20Sopenharmony_ci#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */ 468c2ecf20Sopenharmony_ci#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */ 478c2ecf20Sopenharmony_ci#define SR (10*RS) /* Shift register */ 488c2ecf20Sopenharmony_ci#define ACR (11*RS) /* Auxiliary control register */ 498c2ecf20Sopenharmony_ci#define PCR (12*RS) /* Peripheral control register */ 508c2ecf20Sopenharmony_ci#define IFR (13*RS) /* Interrupt flag register */ 518c2ecf20Sopenharmony_ci#define IER (14*RS) /* Interrupt enable register */ 528c2ecf20Sopenharmony_ci#define ANH (15*RS) /* A-side data, no handshake */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * When the Cuda design replaced the Egret, some signal names and 568c2ecf20Sopenharmony_ci * logic sense changed. They all serve the same purposes, however. 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * VIA pin | Egret pin 598c2ecf20Sopenharmony_ci * ----------------+------------------------------------------ 608c2ecf20Sopenharmony_ci * PB3 (input) | Transceiver session (active low) 618c2ecf20Sopenharmony_ci * PB4 (output) | VIA full (active high) 628c2ecf20Sopenharmony_ci * PB5 (output) | System session (active high) 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * VIA pin | Cuda pin 658c2ecf20Sopenharmony_ci * ----------------+------------------------------------------ 668c2ecf20Sopenharmony_ci * PB3 (input) | Transfer request (active low) 678c2ecf20Sopenharmony_ci * PB4 (output) | Byte acknowledge (active low) 688c2ecf20Sopenharmony_ci * PB5 (output) | Transfer in progress (active low) 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* Bits in Port B data register */ 728c2ecf20Sopenharmony_ci#define TREQ 0x08 /* Transfer request */ 738c2ecf20Sopenharmony_ci#define TACK 0x10 /* Transfer acknowledge */ 748c2ecf20Sopenharmony_ci#define TIP 0x20 /* Transfer in progress */ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Bits in ACR */ 778c2ecf20Sopenharmony_ci#define SR_CTRL 0x1c /* Shift register control bits */ 788c2ecf20Sopenharmony_ci#define SR_EXT 0x0c /* Shift on external clock */ 798c2ecf20Sopenharmony_ci#define SR_OUT 0x10 /* Shift out if 1 */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* Bits in IFR and IER */ 828c2ecf20Sopenharmony_ci#define IER_SET 0x80 /* set bits in IER */ 838c2ecf20Sopenharmony_ci#define IER_CLR 0 /* clear bits in IER */ 848c2ecf20Sopenharmony_ci#define SR_INT 0x04 /* Shift register full/empty */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* Duration of byte acknowledgement pulse (us) */ 878c2ecf20Sopenharmony_ci#define EGRET_TACK_ASSERTED_DELAY 300 888c2ecf20Sopenharmony_ci#define EGRET_TACK_NEGATED_DELAY 400 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* Interval from interrupt to start of session (us) */ 918c2ecf20Sopenharmony_ci#define EGRET_SESSION_DELAY 450 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC 948c2ecf20Sopenharmony_ci#define mcu_is_egret false 958c2ecf20Sopenharmony_ci#else 968c2ecf20Sopenharmony_cistatic bool mcu_is_egret; 978c2ecf20Sopenharmony_ci#endif 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic inline bool TREQ_asserted(u8 portb) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci return !(portb & TREQ); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic inline void assert_TIP(void) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci if (mcu_is_egret) { 1078c2ecf20Sopenharmony_ci udelay(EGRET_SESSION_DELAY); 1088c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) | TIP); 1098c2ecf20Sopenharmony_ci } else 1108c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) & ~TIP); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic inline void assert_TIP_and_TACK(void) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci if (mcu_is_egret) { 1168c2ecf20Sopenharmony_ci udelay(EGRET_SESSION_DELAY); 1178c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) | TIP | TACK); 1188c2ecf20Sopenharmony_ci } else 1198c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) & ~(TIP | TACK)); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic inline void assert_TACK(void) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci if (mcu_is_egret) { 1258c2ecf20Sopenharmony_ci udelay(EGRET_TACK_NEGATED_DELAY); 1268c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) | TACK); 1278c2ecf20Sopenharmony_ci } else 1288c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) & ~TACK); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic inline void toggle_TACK(void) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) ^ TACK); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic inline void negate_TACK(void) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci if (mcu_is_egret) { 1398c2ecf20Sopenharmony_ci udelay(EGRET_TACK_ASSERTED_DELAY); 1408c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) & ~TACK); 1418c2ecf20Sopenharmony_ci } else 1428c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) | TACK); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic inline void negate_TIP_and_TACK(void) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci if (mcu_is_egret) { 1488c2ecf20Sopenharmony_ci udelay(EGRET_TACK_ASSERTED_DELAY); 1498c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) & ~(TIP | TACK)); 1508c2ecf20Sopenharmony_ci } else 1518c2ecf20Sopenharmony_ci out_8(&via[B], in_8(&via[B]) | TIP | TACK); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic enum cuda_state { 1558c2ecf20Sopenharmony_ci idle, 1568c2ecf20Sopenharmony_ci sent_first_byte, 1578c2ecf20Sopenharmony_ci sending, 1588c2ecf20Sopenharmony_ci reading, 1598c2ecf20Sopenharmony_ci read_done, 1608c2ecf20Sopenharmony_ci awaiting_reply 1618c2ecf20Sopenharmony_ci} cuda_state; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic struct adb_request *current_req; 1648c2ecf20Sopenharmony_cistatic struct adb_request *last_req; 1658c2ecf20Sopenharmony_cistatic unsigned char cuda_rbuf[16]; 1668c2ecf20Sopenharmony_cistatic unsigned char *reply_ptr; 1678c2ecf20Sopenharmony_cistatic int reading_reply; 1688c2ecf20Sopenharmony_cistatic int data_index; 1698c2ecf20Sopenharmony_cistatic int cuda_irq; 1708c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC 1718c2ecf20Sopenharmony_cistatic struct device_node *vias; 1728c2ecf20Sopenharmony_ci#endif 1738c2ecf20Sopenharmony_cistatic int cuda_fully_inited; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB 1768c2ecf20Sopenharmony_cistatic int cuda_probe(void); 1778c2ecf20Sopenharmony_cistatic int cuda_send_request(struct adb_request *req, int sync); 1788c2ecf20Sopenharmony_cistatic int cuda_adb_autopoll(int devs); 1798c2ecf20Sopenharmony_cistatic int cuda_reset_adb_bus(void); 1808c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB */ 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int cuda_init_via(void); 1838c2ecf20Sopenharmony_cistatic void cuda_start(void); 1848c2ecf20Sopenharmony_cistatic irqreturn_t cuda_interrupt(int irq, void *arg); 1858c2ecf20Sopenharmony_cistatic void cuda_input(unsigned char *buf, int nb); 1868c2ecf20Sopenharmony_civoid cuda_poll(void); 1878c2ecf20Sopenharmony_cistatic int cuda_write(struct adb_request *req); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ciint cuda_request(struct adb_request *req, 1908c2ecf20Sopenharmony_ci void (*done)(struct adb_request *), int nbytes, ...); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB 1938c2ecf20Sopenharmony_cistruct adb_driver via_cuda_driver = { 1948c2ecf20Sopenharmony_ci .name = "CUDA", 1958c2ecf20Sopenharmony_ci .probe = cuda_probe, 1968c2ecf20Sopenharmony_ci .send_request = cuda_send_request, 1978c2ecf20Sopenharmony_ci .autopoll = cuda_adb_autopoll, 1988c2ecf20Sopenharmony_ci .poll = cuda_poll, 1998c2ecf20Sopenharmony_ci .reset_bus = cuda_reset_adb_bus, 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB */ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC 2048c2ecf20Sopenharmony_ciint __init find_via_cuda(void) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct adb_request req; 2078c2ecf20Sopenharmony_ci int err; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (macintosh_config->adb_type != MAC_ADB_CUDA && 2108c2ecf20Sopenharmony_ci macintosh_config->adb_type != MAC_ADB_EGRET) 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci via = via1; 2148c2ecf20Sopenharmony_ci cuda_state = idle; 2158c2ecf20Sopenharmony_ci mcu_is_egret = macintosh_config->adb_type == MAC_ADB_EGRET; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci err = cuda_init_via(); 2188c2ecf20Sopenharmony_ci if (err) { 2198c2ecf20Sopenharmony_ci printk(KERN_ERR "cuda_init_via() failed\n"); 2208c2ecf20Sopenharmony_ci via = NULL; 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* enable autopoll */ 2258c2ecf20Sopenharmony_ci cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1); 2268c2ecf20Sopenharmony_ci while (!req.complete) 2278c2ecf20Sopenharmony_ci cuda_poll(); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return 1; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci#else 2328c2ecf20Sopenharmony_ciint __init find_via_cuda(void) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct adb_request req; 2358c2ecf20Sopenharmony_ci phys_addr_t taddr; 2368c2ecf20Sopenharmony_ci const u32 *reg; 2378c2ecf20Sopenharmony_ci int err; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (vias != 0) 2408c2ecf20Sopenharmony_ci return 1; 2418c2ecf20Sopenharmony_ci vias = of_find_node_by_name(NULL, "via-cuda"); 2428c2ecf20Sopenharmony_ci if (vias == 0) 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci reg = of_get_property(vias, "reg", NULL); 2468c2ecf20Sopenharmony_ci if (reg == NULL) { 2478c2ecf20Sopenharmony_ci printk(KERN_ERR "via-cuda: No \"reg\" property !\n"); 2488c2ecf20Sopenharmony_ci goto fail; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci taddr = of_translate_address(vias, reg); 2518c2ecf20Sopenharmony_ci if (taddr == 0) { 2528c2ecf20Sopenharmony_ci printk(KERN_ERR "via-cuda: Can't translate address !\n"); 2538c2ecf20Sopenharmony_ci goto fail; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci via = ioremap(taddr, 0x2000); 2568c2ecf20Sopenharmony_ci if (via == NULL) { 2578c2ecf20Sopenharmony_ci printk(KERN_ERR "via-cuda: Can't map address !\n"); 2588c2ecf20Sopenharmony_ci goto fail; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci cuda_state = idle; 2628c2ecf20Sopenharmony_ci sys_ctrler = SYS_CTRLER_CUDA; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci err = cuda_init_via(); 2658c2ecf20Sopenharmony_ci if (err) { 2668c2ecf20Sopenharmony_ci printk(KERN_ERR "cuda_init_via() failed\n"); 2678c2ecf20Sopenharmony_ci via = NULL; 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Clear and enable interrupts, but only on PPC. On 68K it's done */ 2728c2ecf20Sopenharmony_ci /* for us by the main VIA driver in arch/m68k/mac/via.c */ 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci out_8(&via[IFR], 0x7f); /* clear interrupts by writing 1s */ 2758c2ecf20Sopenharmony_ci out_8(&via[IER], IER_SET|SR_INT); /* enable interrupt from SR */ 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* enable autopoll */ 2788c2ecf20Sopenharmony_ci cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1); 2798c2ecf20Sopenharmony_ci while (!req.complete) 2808c2ecf20Sopenharmony_ci cuda_poll(); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 1; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci fail: 2858c2ecf20Sopenharmony_ci of_node_put(vias); 2868c2ecf20Sopenharmony_ci vias = NULL; 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci#endif /* !defined CONFIG_MAC */ 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int __init via_cuda_start(void) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci if (via == NULL) 2948c2ecf20Sopenharmony_ci return -ENODEV; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC 2978c2ecf20Sopenharmony_ci cuda_irq = IRQ_MAC_ADB; 2988c2ecf20Sopenharmony_ci#else 2998c2ecf20Sopenharmony_ci cuda_irq = irq_of_parse_and_map(vias, 0); 3008c2ecf20Sopenharmony_ci if (!cuda_irq) { 3018c2ecf20Sopenharmony_ci printk(KERN_ERR "via-cuda: can't map interrupts for %pOF\n", 3028c2ecf20Sopenharmony_ci vias); 3038c2ecf20Sopenharmony_ci return -ENODEV; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci#endif 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (request_irq(cuda_irq, cuda_interrupt, 0, "ADB", cuda_interrupt)) { 3088c2ecf20Sopenharmony_ci printk(KERN_ERR "via-cuda: can't request irq %d\n", cuda_irq); 3098c2ecf20Sopenharmony_ci return -EAGAIN; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci pr_info("Macintosh Cuda and Egret driver.\n"); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci cuda_fully_inited = 1; 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cidevice_initcall(via_cuda_start); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB 3218c2ecf20Sopenharmony_cistatic int 3228c2ecf20Sopenharmony_cicuda_probe(void) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC 3258c2ecf20Sopenharmony_ci if (sys_ctrler != SYS_CTRLER_CUDA) 3268c2ecf20Sopenharmony_ci return -ENODEV; 3278c2ecf20Sopenharmony_ci#else 3288c2ecf20Sopenharmony_ci if (macintosh_config->adb_type != MAC_ADB_CUDA && 3298c2ecf20Sopenharmony_ci macintosh_config->adb_type != MAC_ADB_EGRET) 3308c2ecf20Sopenharmony_ci return -ENODEV; 3318c2ecf20Sopenharmony_ci#endif 3328c2ecf20Sopenharmony_ci if (via == NULL) 3338c2ecf20Sopenharmony_ci return -ENODEV; 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB */ 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int __init sync_egret(void) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci if (TREQ_asserted(in_8(&via[B]))) { 3418c2ecf20Sopenharmony_ci /* Complete the inbound transfer */ 3428c2ecf20Sopenharmony_ci assert_TIP_and_TACK(); 3438c2ecf20Sopenharmony_ci while (1) { 3448c2ecf20Sopenharmony_ci negate_TACK(); 3458c2ecf20Sopenharmony_ci mdelay(1); 3468c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 3478c2ecf20Sopenharmony_ci assert_TACK(); 3488c2ecf20Sopenharmony_ci if (!TREQ_asserted(in_8(&via[B]))) 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci negate_TIP_and_TACK(); 3528c2ecf20Sopenharmony_ci } else if (in_8(&via[B]) & TIP) { 3538c2ecf20Sopenharmony_ci /* Terminate the outbound transfer */ 3548c2ecf20Sopenharmony_ci negate_TACK(); 3558c2ecf20Sopenharmony_ci assert_TACK(); 3568c2ecf20Sopenharmony_ci mdelay(1); 3578c2ecf20Sopenharmony_ci negate_TIP_and_TACK(); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci /* Clear shift register interrupt */ 3608c2ecf20Sopenharmony_ci if (in_8(&via[IFR]) & SR_INT) 3618c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci#define WAIT_FOR(cond, what) \ 3668c2ecf20Sopenharmony_ci do { \ 3678c2ecf20Sopenharmony_ci int x; \ 3688c2ecf20Sopenharmony_ci for (x = 1000; !(cond); --x) { \ 3698c2ecf20Sopenharmony_ci if (x == 0) { \ 3708c2ecf20Sopenharmony_ci pr_err("Timeout waiting for " what "\n"); \ 3718c2ecf20Sopenharmony_ci return -ENXIO; \ 3728c2ecf20Sopenharmony_ci } \ 3738c2ecf20Sopenharmony_ci udelay(100); \ 3748c2ecf20Sopenharmony_ci } \ 3758c2ecf20Sopenharmony_ci } while (0) 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int 3788c2ecf20Sopenharmony_ci__init cuda_init_via(void) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC 3818c2ecf20Sopenharmony_ci out_8(&via[IER], 0x7f); /* disable interrupts from VIA */ 3828c2ecf20Sopenharmony_ci (void)in_8(&via[IER]); 3838c2ecf20Sopenharmony_ci#else 3848c2ecf20Sopenharmony_ci out_8(&via[IER], SR_INT); /* disable SR interrupt from VIA */ 3858c2ecf20Sopenharmony_ci#endif 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci out_8(&via[DIRB], (in_8(&via[DIRB]) | TACK | TIP) & ~TREQ); /* TACK & TIP out */ 3888c2ecf20Sopenharmony_ci out_8(&via[ACR], (in_8(&via[ACR]) & ~SR_CTRL) | SR_EXT); /* SR data in */ 3898c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); /* clear any left-over data */ 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (mcu_is_egret) 3928c2ecf20Sopenharmony_ci return sync_egret(); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci negate_TIP_and_TACK(); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* delay 4ms and then clear any pending interrupt */ 3978c2ecf20Sopenharmony_ci mdelay(4); 3988c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 3998c2ecf20Sopenharmony_ci out_8(&via[IFR], SR_INT); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* sync with the CUDA - assert TACK without TIP */ 4028c2ecf20Sopenharmony_ci assert_TACK(); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* wait for the CUDA to assert TREQ in response */ 4058c2ecf20Sopenharmony_ci WAIT_FOR(TREQ_asserted(in_8(&via[B])), "CUDA response to sync"); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* wait for the interrupt and then clear it */ 4088c2ecf20Sopenharmony_ci WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (2)"); 4098c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 4108c2ecf20Sopenharmony_ci out_8(&via[IFR], SR_INT); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* finish the sync by negating TACK */ 4138c2ecf20Sopenharmony_ci negate_TACK(); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* wait for the CUDA to negate TREQ and the corresponding interrupt */ 4168c2ecf20Sopenharmony_ci WAIT_FOR(!TREQ_asserted(in_8(&via[B])), "CUDA response to sync (3)"); 4178c2ecf20Sopenharmony_ci WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (4)"); 4188c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 4198c2ecf20Sopenharmony_ci out_8(&via[IFR], SR_INT); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB 4258c2ecf20Sopenharmony_ci/* Send an ADB command */ 4268c2ecf20Sopenharmony_cistatic int 4278c2ecf20Sopenharmony_cicuda_send_request(struct adb_request *req, int sync) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci int i; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if ((via == NULL) || !cuda_fully_inited) { 4328c2ecf20Sopenharmony_ci req->complete = 1; 4338c2ecf20Sopenharmony_ci return -ENXIO; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci req->reply_expected = 1; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci i = cuda_write(req); 4398c2ecf20Sopenharmony_ci if (i) 4408c2ecf20Sopenharmony_ci return i; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (sync) { 4438c2ecf20Sopenharmony_ci while (!req->complete) 4448c2ecf20Sopenharmony_ci cuda_poll(); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/* Enable/disable autopolling */ 4518c2ecf20Sopenharmony_cistatic int 4528c2ecf20Sopenharmony_cicuda_adb_autopoll(int devs) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct adb_request req; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if ((via == NULL) || !cuda_fully_inited) 4578c2ecf20Sopenharmony_ci return -ENXIO; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, (devs? 1: 0)); 4608c2ecf20Sopenharmony_ci while (!req.complete) 4618c2ecf20Sopenharmony_ci cuda_poll(); 4628c2ecf20Sopenharmony_ci return 0; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci/* Reset adb bus - how do we do this?? */ 4668c2ecf20Sopenharmony_cistatic int 4678c2ecf20Sopenharmony_cicuda_reset_adb_bus(void) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct adb_request req; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if ((via == NULL) || !cuda_fully_inited) 4728c2ecf20Sopenharmony_ci return -ENXIO; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci cuda_request(&req, NULL, 2, ADB_PACKET, 0); /* maybe? */ 4758c2ecf20Sopenharmony_ci while (!req.complete) 4768c2ecf20Sopenharmony_ci cuda_poll(); 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB */ 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci/* Construct and send a cuda request */ 4828c2ecf20Sopenharmony_ciint 4838c2ecf20Sopenharmony_cicuda_request(struct adb_request *req, void (*done)(struct adb_request *), 4848c2ecf20Sopenharmony_ci int nbytes, ...) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci va_list list; 4878c2ecf20Sopenharmony_ci int i; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (via == NULL) { 4908c2ecf20Sopenharmony_ci req->complete = 1; 4918c2ecf20Sopenharmony_ci return -ENXIO; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci req->nbytes = nbytes; 4958c2ecf20Sopenharmony_ci req->done = done; 4968c2ecf20Sopenharmony_ci va_start(list, nbytes); 4978c2ecf20Sopenharmony_ci for (i = 0; i < nbytes; ++i) 4988c2ecf20Sopenharmony_ci req->data[i] = va_arg(list, int); 4998c2ecf20Sopenharmony_ci va_end(list); 5008c2ecf20Sopenharmony_ci req->reply_expected = 1; 5018c2ecf20Sopenharmony_ci return cuda_write(req); 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cuda_request); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int 5068c2ecf20Sopenharmony_cicuda_write(struct adb_request *req) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci unsigned long flags; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (req->nbytes < 2 || req->data[0] > CUDA_PACKET) { 5118c2ecf20Sopenharmony_ci req->complete = 1; 5128c2ecf20Sopenharmony_ci return -EINVAL; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci req->next = NULL; 5158c2ecf20Sopenharmony_ci req->sent = 0; 5168c2ecf20Sopenharmony_ci req->complete = 0; 5178c2ecf20Sopenharmony_ci req->reply_len = 0; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci spin_lock_irqsave(&cuda_lock, flags); 5208c2ecf20Sopenharmony_ci if (current_req != 0) { 5218c2ecf20Sopenharmony_ci last_req->next = req; 5228c2ecf20Sopenharmony_ci last_req = req; 5238c2ecf20Sopenharmony_ci } else { 5248c2ecf20Sopenharmony_ci current_req = req; 5258c2ecf20Sopenharmony_ci last_req = req; 5268c2ecf20Sopenharmony_ci if (cuda_state == idle) 5278c2ecf20Sopenharmony_ci cuda_start(); 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cuda_lock, flags); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic void 5358c2ecf20Sopenharmony_cicuda_start(void) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci /* assert cuda_state == idle */ 5388c2ecf20Sopenharmony_ci if (current_req == NULL) 5398c2ecf20Sopenharmony_ci return; 5408c2ecf20Sopenharmony_ci data_index = 0; 5418c2ecf20Sopenharmony_ci if (TREQ_asserted(in_8(&via[B]))) 5428c2ecf20Sopenharmony_ci return; /* a byte is coming in from the CUDA */ 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* set the shift register to shift out and send a byte */ 5458c2ecf20Sopenharmony_ci out_8(&via[ACR], in_8(&via[ACR]) | SR_OUT); 5468c2ecf20Sopenharmony_ci out_8(&via[SR], current_req->data[data_index++]); 5478c2ecf20Sopenharmony_ci if (mcu_is_egret) 5488c2ecf20Sopenharmony_ci assert_TIP_and_TACK(); 5498c2ecf20Sopenharmony_ci else 5508c2ecf20Sopenharmony_ci assert_TIP(); 5518c2ecf20Sopenharmony_ci cuda_state = sent_first_byte; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_civoid 5558c2ecf20Sopenharmony_cicuda_poll(void) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci cuda_interrupt(0, NULL); 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cuda_poll); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci#define ARRAY_FULL(a, p) ((p) - (a) == ARRAY_SIZE(a)) 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic irqreturn_t 5648c2ecf20Sopenharmony_cicuda_interrupt(int irq, void *arg) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci unsigned long flags; 5678c2ecf20Sopenharmony_ci u8 status; 5688c2ecf20Sopenharmony_ci struct adb_request *req = NULL; 5698c2ecf20Sopenharmony_ci unsigned char ibuf[16]; 5708c2ecf20Sopenharmony_ci int ibuf_len = 0; 5718c2ecf20Sopenharmony_ci int complete = 0; 5728c2ecf20Sopenharmony_ci bool full; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci spin_lock_irqsave(&cuda_lock, flags); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* On powermacs, this handler is registered for the VIA IRQ. But they use 5778c2ecf20Sopenharmony_ci * just the shift register IRQ -- other VIA interrupt sources are disabled. 5788c2ecf20Sopenharmony_ci * On m68k macs, the VIA IRQ sources are dispatched individually. Unless 5798c2ecf20Sopenharmony_ci * we are polling, the shift register IRQ flag has already been cleared. 5808c2ecf20Sopenharmony_ci */ 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC 5838c2ecf20Sopenharmony_ci if (!arg) 5848c2ecf20Sopenharmony_ci#endif 5858c2ecf20Sopenharmony_ci { 5868c2ecf20Sopenharmony_ci if ((in_8(&via[IFR]) & SR_INT) == 0) { 5878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cuda_lock, flags); 5888c2ecf20Sopenharmony_ci return IRQ_NONE; 5898c2ecf20Sopenharmony_ci } else { 5908c2ecf20Sopenharmony_ci out_8(&via[IFR], SR_INT); 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci status = in_8(&via[B]) & (TIP | TACK | TREQ); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci switch (cuda_state) { 5978c2ecf20Sopenharmony_ci case idle: 5988c2ecf20Sopenharmony_ci /* System controller has unsolicited data for us */ 5998c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 6008c2ecf20Sopenharmony_ciidle_state: 6018c2ecf20Sopenharmony_ci assert_TIP(); 6028c2ecf20Sopenharmony_ci cuda_state = reading; 6038c2ecf20Sopenharmony_ci reply_ptr = cuda_rbuf; 6048c2ecf20Sopenharmony_ci reading_reply = 0; 6058c2ecf20Sopenharmony_ci break; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci case awaiting_reply: 6088c2ecf20Sopenharmony_ci /* System controller has reply data for us */ 6098c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 6108c2ecf20Sopenharmony_ci assert_TIP(); 6118c2ecf20Sopenharmony_ci cuda_state = reading; 6128c2ecf20Sopenharmony_ci reply_ptr = current_req->reply; 6138c2ecf20Sopenharmony_ci reading_reply = 1; 6148c2ecf20Sopenharmony_ci break; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci case sent_first_byte: 6178c2ecf20Sopenharmony_ci if (TREQ_asserted(status)) { 6188c2ecf20Sopenharmony_ci /* collision */ 6198c2ecf20Sopenharmony_ci out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT); 6208c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 6218c2ecf20Sopenharmony_ci negate_TIP_and_TACK(); 6228c2ecf20Sopenharmony_ci cuda_state = idle; 6238c2ecf20Sopenharmony_ci /* Egret does not raise an "aborted" interrupt */ 6248c2ecf20Sopenharmony_ci if (mcu_is_egret) 6258c2ecf20Sopenharmony_ci goto idle_state; 6268c2ecf20Sopenharmony_ci } else { 6278c2ecf20Sopenharmony_ci out_8(&via[SR], current_req->data[data_index++]); 6288c2ecf20Sopenharmony_ci toggle_TACK(); 6298c2ecf20Sopenharmony_ci if (mcu_is_egret) 6308c2ecf20Sopenharmony_ci assert_TACK(); 6318c2ecf20Sopenharmony_ci cuda_state = sending; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci break; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci case sending: 6368c2ecf20Sopenharmony_ci req = current_req; 6378c2ecf20Sopenharmony_ci if (data_index >= req->nbytes) { 6388c2ecf20Sopenharmony_ci out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT); 6398c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 6408c2ecf20Sopenharmony_ci negate_TIP_and_TACK(); 6418c2ecf20Sopenharmony_ci req->sent = 1; 6428c2ecf20Sopenharmony_ci if (req->reply_expected) { 6438c2ecf20Sopenharmony_ci cuda_state = awaiting_reply; 6448c2ecf20Sopenharmony_ci } else { 6458c2ecf20Sopenharmony_ci current_req = req->next; 6468c2ecf20Sopenharmony_ci complete = 1; 6478c2ecf20Sopenharmony_ci /* not sure about this */ 6488c2ecf20Sopenharmony_ci cuda_state = idle; 6498c2ecf20Sopenharmony_ci cuda_start(); 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci } else { 6528c2ecf20Sopenharmony_ci out_8(&via[SR], req->data[data_index++]); 6538c2ecf20Sopenharmony_ci toggle_TACK(); 6548c2ecf20Sopenharmony_ci if (mcu_is_egret) 6558c2ecf20Sopenharmony_ci assert_TACK(); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci case reading: 6608c2ecf20Sopenharmony_ci full = reading_reply ? ARRAY_FULL(current_req->reply, reply_ptr) 6618c2ecf20Sopenharmony_ci : ARRAY_FULL(cuda_rbuf, reply_ptr); 6628c2ecf20Sopenharmony_ci if (full) 6638c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 6648c2ecf20Sopenharmony_ci else 6658c2ecf20Sopenharmony_ci *reply_ptr++ = in_8(&via[SR]); 6668c2ecf20Sopenharmony_ci if (!TREQ_asserted(status) || full) { 6678c2ecf20Sopenharmony_ci if (mcu_is_egret) 6688c2ecf20Sopenharmony_ci assert_TACK(); 6698c2ecf20Sopenharmony_ci /* that's all folks */ 6708c2ecf20Sopenharmony_ci negate_TIP_and_TACK(); 6718c2ecf20Sopenharmony_ci cuda_state = read_done; 6728c2ecf20Sopenharmony_ci /* Egret does not raise a "read done" interrupt */ 6738c2ecf20Sopenharmony_ci if (mcu_is_egret) 6748c2ecf20Sopenharmony_ci goto read_done_state; 6758c2ecf20Sopenharmony_ci } else { 6768c2ecf20Sopenharmony_ci toggle_TACK(); 6778c2ecf20Sopenharmony_ci if (mcu_is_egret) 6788c2ecf20Sopenharmony_ci negate_TACK(); 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci case read_done: 6838c2ecf20Sopenharmony_ci (void)in_8(&via[SR]); 6848c2ecf20Sopenharmony_ciread_done_state: 6858c2ecf20Sopenharmony_ci if (reading_reply) { 6868c2ecf20Sopenharmony_ci req = current_req; 6878c2ecf20Sopenharmony_ci req->reply_len = reply_ptr - req->reply; 6888c2ecf20Sopenharmony_ci if (req->data[0] == ADB_PACKET) { 6898c2ecf20Sopenharmony_ci /* Have to adjust the reply from ADB commands */ 6908c2ecf20Sopenharmony_ci if (req->reply_len <= 2 || (req->reply[1] & 2) != 0) { 6918c2ecf20Sopenharmony_ci /* the 0x2 bit indicates no response */ 6928c2ecf20Sopenharmony_ci req->reply_len = 0; 6938c2ecf20Sopenharmony_ci } else { 6948c2ecf20Sopenharmony_ci /* leave just the command and result bytes in the reply */ 6958c2ecf20Sopenharmony_ci req->reply_len -= 2; 6968c2ecf20Sopenharmony_ci memmove(req->reply, req->reply + 2, req->reply_len); 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci current_req = req->next; 7008c2ecf20Sopenharmony_ci complete = 1; 7018c2ecf20Sopenharmony_ci reading_reply = 0; 7028c2ecf20Sopenharmony_ci } else { 7038c2ecf20Sopenharmony_ci /* This is tricky. We must break the spinlock to call 7048c2ecf20Sopenharmony_ci * cuda_input. However, doing so means we might get 7058c2ecf20Sopenharmony_ci * re-entered from another CPU getting an interrupt 7068c2ecf20Sopenharmony_ci * or calling cuda_poll(). I ended up using the stack 7078c2ecf20Sopenharmony_ci * (it's only for 16 bytes) and moving the actual 7088c2ecf20Sopenharmony_ci * call to cuda_input to outside of the lock. 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_ci ibuf_len = reply_ptr - cuda_rbuf; 7118c2ecf20Sopenharmony_ci memcpy(ibuf, cuda_rbuf, ibuf_len); 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci reply_ptr = cuda_rbuf; 7148c2ecf20Sopenharmony_ci cuda_state = idle; 7158c2ecf20Sopenharmony_ci cuda_start(); 7168c2ecf20Sopenharmony_ci if (cuda_state == idle && TREQ_asserted(in_8(&via[B]))) { 7178c2ecf20Sopenharmony_ci assert_TIP(); 7188c2ecf20Sopenharmony_ci cuda_state = reading; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci break; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci default: 7238c2ecf20Sopenharmony_ci pr_err("cuda_interrupt: unknown cuda_state %d?\n", cuda_state); 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cuda_lock, flags); 7268c2ecf20Sopenharmony_ci if (complete && req) { 7278c2ecf20Sopenharmony_ci void (*done)(struct adb_request *) = req->done; 7288c2ecf20Sopenharmony_ci mb(); 7298c2ecf20Sopenharmony_ci req->complete = 1; 7308c2ecf20Sopenharmony_ci /* Here, we assume that if the request has a done member, the 7318c2ecf20Sopenharmony_ci * struct request will survive to setting req->complete to 1 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_ci if (done) 7348c2ecf20Sopenharmony_ci (*done)(req); 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci if (ibuf_len) 7378c2ecf20Sopenharmony_ci cuda_input(ibuf, ibuf_len); 7388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic void 7428c2ecf20Sopenharmony_cicuda_input(unsigned char *buf, int nb) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci switch (buf[0]) { 7458c2ecf20Sopenharmony_ci case ADB_PACKET: 7468c2ecf20Sopenharmony_ci#ifdef CONFIG_XMON 7478c2ecf20Sopenharmony_ci if (nb == 5 && buf[2] == 0x2c) { 7488c2ecf20Sopenharmony_ci extern int xmon_wants_key, xmon_adb_keycode; 7498c2ecf20Sopenharmony_ci if (xmon_wants_key) { 7508c2ecf20Sopenharmony_ci xmon_adb_keycode = buf[3]; 7518c2ecf20Sopenharmony_ci return; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci#endif /* CONFIG_XMON */ 7558c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB 7568c2ecf20Sopenharmony_ci adb_input(buf+2, nb-2, buf[1] & 0x40); 7578c2ecf20Sopenharmony_ci#endif /* CONFIG_ADB */ 7588c2ecf20Sopenharmony_ci break; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci case TIMER_PACKET: 7618c2ecf20Sopenharmony_ci /* Egret sends these periodically. Might be useful as a 'heartbeat' 7628c2ecf20Sopenharmony_ci * to trigger a recovery for the VIA shift register errata. 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci break; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci default: 7678c2ecf20Sopenharmony_ci print_hex_dump(KERN_INFO, "cuda_input: ", DUMP_PREFIX_NONE, 32, 1, 7688c2ecf20Sopenharmony_ci buf, nb, false); 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/* Offset between Unix time (1970-based) and Mac time (1904-based) */ 7738c2ecf20Sopenharmony_ci#define RTC_OFFSET 2082844800 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_citime64_t cuda_get_time(void) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci struct adb_request req; 7788c2ecf20Sopenharmony_ci u32 now; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0) 7818c2ecf20Sopenharmony_ci return 0; 7828c2ecf20Sopenharmony_ci while (!req.complete) 7838c2ecf20Sopenharmony_ci cuda_poll(); 7848c2ecf20Sopenharmony_ci if (req.reply_len != 7) 7858c2ecf20Sopenharmony_ci pr_err("%s: got %d byte reply\n", __func__, req.reply_len); 7868c2ecf20Sopenharmony_ci now = (req.reply[3] << 24) + (req.reply[4] << 16) + 7878c2ecf20Sopenharmony_ci (req.reply[5] << 8) + req.reply[6]; 7888c2ecf20Sopenharmony_ci return (time64_t)now - RTC_OFFSET; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ciint cuda_set_rtc_time(struct rtc_time *tm) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci u32 now; 7948c2ecf20Sopenharmony_ci struct adb_request req; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci now = lower_32_bits(rtc_tm_to_time64(tm) + RTC_OFFSET); 7978c2ecf20Sopenharmony_ci if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME, 7988c2ecf20Sopenharmony_ci now >> 24, now >> 16, now >> 8, now) < 0) 7998c2ecf20Sopenharmony_ci return -ENXIO; 8008c2ecf20Sopenharmony_ci while (!req.complete) 8018c2ecf20Sopenharmony_ci cuda_poll(); 8028c2ecf20Sopenharmony_ci if ((req.reply_len != 3) && (req.reply_len != 7)) 8038c2ecf20Sopenharmony_ci pr_err("%s: got %d byte reply\n", __func__, req.reply_len); 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci} 806