18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 1999 Derrick J Brashear (shadow@dementia.org) 58c2ecf20Sopenharmony_ci * Copyright 2008 David S. Miller (davem@davemloft.net) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/ioport.h> 158c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/mm.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_device.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/openprom.h> 218c2ecf20Sopenharmony_ci#include <asm/oplib.h> 228c2ecf20Sopenharmony_ci#include <asm/irq.h> 238c2ecf20Sopenharmony_ci#include <asm/io.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DEBUG 1 268c2ecf20Sopenharmony_ci#ifdef DEBUG 278c2ecf20Sopenharmony_ci#define dprintk(x) printk x 288c2ecf20Sopenharmony_ci#else 298c2ecf20Sopenharmony_ci#define dprintk(x) 308c2ecf20Sopenharmony_ci#endif 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct uctrl_regs { 338c2ecf20Sopenharmony_ci u32 uctrl_intr; 348c2ecf20Sopenharmony_ci u32 uctrl_data; 358c2ecf20Sopenharmony_ci u32 uctrl_stat; 368c2ecf20Sopenharmony_ci u32 uctrl_xxx[5]; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct ts102_regs { 408c2ecf20Sopenharmony_ci u32 card_a_intr; 418c2ecf20Sopenharmony_ci u32 card_a_stat; 428c2ecf20Sopenharmony_ci u32 card_a_ctrl; 438c2ecf20Sopenharmony_ci u32 card_a_xxx; 448c2ecf20Sopenharmony_ci u32 card_b_intr; 458c2ecf20Sopenharmony_ci u32 card_b_stat; 468c2ecf20Sopenharmony_ci u32 card_b_ctrl; 478c2ecf20Sopenharmony_ci u32 card_b_xxx; 488c2ecf20Sopenharmony_ci u32 uctrl_intr; 498c2ecf20Sopenharmony_ci u32 uctrl_data; 508c2ecf20Sopenharmony_ci u32 uctrl_stat; 518c2ecf20Sopenharmony_ci u32 uctrl_xxx; 528c2ecf20Sopenharmony_ci u32 ts102_xxx[4]; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Bits for uctrl_intr register */ 568c2ecf20Sopenharmony_ci#define UCTRL_INTR_TXE_REQ 0x01 /* transmit FIFO empty int req */ 578c2ecf20Sopenharmony_ci#define UCTRL_INTR_TXNF_REQ 0x02 /* transmit FIFO not full int req */ 588c2ecf20Sopenharmony_ci#define UCTRL_INTR_RXNE_REQ 0x04 /* receive FIFO not empty int req */ 598c2ecf20Sopenharmony_ci#define UCTRL_INTR_RXO_REQ 0x08 /* receive FIFO overflow int req */ 608c2ecf20Sopenharmony_ci#define UCTRL_INTR_TXE_MSK 0x10 /* transmit FIFO empty mask */ 618c2ecf20Sopenharmony_ci#define UCTRL_INTR_TXNF_MSK 0x20 /* transmit FIFO not full mask */ 628c2ecf20Sopenharmony_ci#define UCTRL_INTR_RXNE_MSK 0x40 /* receive FIFO not empty mask */ 638c2ecf20Sopenharmony_ci#define UCTRL_INTR_RXO_MSK 0x80 /* receive FIFO overflow mask */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Bits for uctrl_stat register */ 668c2ecf20Sopenharmony_ci#define UCTRL_STAT_TXE_STA 0x01 /* transmit FIFO empty status */ 678c2ecf20Sopenharmony_ci#define UCTRL_STAT_TXNF_STA 0x02 /* transmit FIFO not full status */ 688c2ecf20Sopenharmony_ci#define UCTRL_STAT_RXNE_STA 0x04 /* receive FIFO not empty status */ 698c2ecf20Sopenharmony_ci#define UCTRL_STAT_RXO_STA 0x08 /* receive FIFO overflow status */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(uctrl_mutex); 728c2ecf20Sopenharmony_cistatic const char *uctrl_extstatus[16] = { 738c2ecf20Sopenharmony_ci "main power available", 748c2ecf20Sopenharmony_ci "internal battery attached", 758c2ecf20Sopenharmony_ci "external battery attached", 768c2ecf20Sopenharmony_ci "external VGA attached", 778c2ecf20Sopenharmony_ci "external keyboard attached", 788c2ecf20Sopenharmony_ci "external mouse attached", 798c2ecf20Sopenharmony_ci "lid down", 808c2ecf20Sopenharmony_ci "internal battery currently charging", 818c2ecf20Sopenharmony_ci "external battery currently charging", 828c2ecf20Sopenharmony_ci "internal battery currently discharging", 838c2ecf20Sopenharmony_ci "external battery currently discharging", 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* Everything required for one transaction with the uctrl */ 878c2ecf20Sopenharmony_cistruct uctrl_txn { 888c2ecf20Sopenharmony_ci u8 opcode; 898c2ecf20Sopenharmony_ci u8 inbits; 908c2ecf20Sopenharmony_ci u8 outbits; 918c2ecf20Sopenharmony_ci u8 *inbuf; 928c2ecf20Sopenharmony_ci u8 *outbuf; 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistruct uctrl_status { 968c2ecf20Sopenharmony_ci u8 current_temp; /* 0x07 */ 978c2ecf20Sopenharmony_ci u8 reset_status; /* 0x0b */ 988c2ecf20Sopenharmony_ci u16 event_status; /* 0x0c */ 998c2ecf20Sopenharmony_ci u16 error_status; /* 0x10 */ 1008c2ecf20Sopenharmony_ci u16 external_status; /* 0x11, 0x1b */ 1018c2ecf20Sopenharmony_ci u8 internal_charge; /* 0x18 */ 1028c2ecf20Sopenharmony_ci u8 external_charge; /* 0x19 */ 1038c2ecf20Sopenharmony_ci u16 control_lcd; /* 0x20 */ 1048c2ecf20Sopenharmony_ci u8 control_bitport; /* 0x21 */ 1058c2ecf20Sopenharmony_ci u8 speaker_volume; /* 0x23 */ 1068c2ecf20Sopenharmony_ci u8 control_tft_brightness; /* 0x24 */ 1078c2ecf20Sopenharmony_ci u8 control_kbd_repeat_delay; /* 0x28 */ 1088c2ecf20Sopenharmony_ci u8 control_kbd_repeat_period; /* 0x29 */ 1098c2ecf20Sopenharmony_ci u8 control_screen_contrast; /* 0x2F */ 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cienum uctrl_opcode { 1138c2ecf20Sopenharmony_ci READ_SERIAL_NUMBER=0x1, 1148c2ecf20Sopenharmony_ci READ_ETHERNET_ADDRESS=0x2, 1158c2ecf20Sopenharmony_ci READ_HARDWARE_VERSION=0x3, 1168c2ecf20Sopenharmony_ci READ_MICROCONTROLLER_VERSION=0x4, 1178c2ecf20Sopenharmony_ci READ_MAX_TEMPERATURE=0x5, 1188c2ecf20Sopenharmony_ci READ_MIN_TEMPERATURE=0x6, 1198c2ecf20Sopenharmony_ci READ_CURRENT_TEMPERATURE=0x7, 1208c2ecf20Sopenharmony_ci READ_SYSTEM_VARIANT=0x8, 1218c2ecf20Sopenharmony_ci READ_POWERON_CYCLES=0x9, 1228c2ecf20Sopenharmony_ci READ_POWERON_SECONDS=0xA, 1238c2ecf20Sopenharmony_ci READ_RESET_STATUS=0xB, 1248c2ecf20Sopenharmony_ci READ_EVENT_STATUS=0xC, 1258c2ecf20Sopenharmony_ci READ_REAL_TIME_CLOCK=0xD, 1268c2ecf20Sopenharmony_ci READ_EXTERNAL_VGA_PORT=0xE, 1278c2ecf20Sopenharmony_ci READ_MICROCONTROLLER_ROM_CHECKSUM=0xF, 1288c2ecf20Sopenharmony_ci READ_ERROR_STATUS=0x10, 1298c2ecf20Sopenharmony_ci READ_EXTERNAL_STATUS=0x11, 1308c2ecf20Sopenharmony_ci READ_USER_CONFIGURATION_AREA=0x12, 1318c2ecf20Sopenharmony_ci READ_MICROCONTROLLER_VOLTAGE=0x13, 1328c2ecf20Sopenharmony_ci READ_INTERNAL_BATTERY_VOLTAGE=0x14, 1338c2ecf20Sopenharmony_ci READ_DCIN_VOLTAGE=0x15, 1348c2ecf20Sopenharmony_ci READ_HORIZONTAL_POINTER_VOLTAGE=0x16, 1358c2ecf20Sopenharmony_ci READ_VERTICAL_POINTER_VOLTAGE=0x17, 1368c2ecf20Sopenharmony_ci READ_INTERNAL_BATTERY_CHARGE_LEVEL=0x18, 1378c2ecf20Sopenharmony_ci READ_EXTERNAL_BATTERY_CHARGE_LEVEL=0x19, 1388c2ecf20Sopenharmony_ci READ_REAL_TIME_CLOCK_ALARM=0x1A, 1398c2ecf20Sopenharmony_ci READ_EVENT_STATUS_NO_RESET=0x1B, 1408c2ecf20Sopenharmony_ci READ_INTERNAL_KEYBOARD_LAYOUT=0x1C, 1418c2ecf20Sopenharmony_ci READ_EXTERNAL_KEYBOARD_LAYOUT=0x1D, 1428c2ecf20Sopenharmony_ci READ_EEPROM_STATUS=0x1E, 1438c2ecf20Sopenharmony_ci CONTROL_LCD=0x20, 1448c2ecf20Sopenharmony_ci CONTROL_BITPORT=0x21, 1458c2ecf20Sopenharmony_ci SPEAKER_VOLUME=0x23, 1468c2ecf20Sopenharmony_ci CONTROL_TFT_BRIGHTNESS=0x24, 1478c2ecf20Sopenharmony_ci CONTROL_WATCHDOG=0x25, 1488c2ecf20Sopenharmony_ci CONTROL_FACTORY_EEPROM_AREA=0x26, 1498c2ecf20Sopenharmony_ci CONTROL_KBD_TIME_UNTIL_REPEAT=0x28, 1508c2ecf20Sopenharmony_ci CONTROL_KBD_TIME_BETWEEN_REPEATS=0x29, 1518c2ecf20Sopenharmony_ci CONTROL_TIMEZONE=0x2A, 1528c2ecf20Sopenharmony_ci CONTROL_MARK_SPACE_RATIO=0x2B, 1538c2ecf20Sopenharmony_ci CONTROL_DIAGNOSTIC_MODE=0x2E, 1548c2ecf20Sopenharmony_ci CONTROL_SCREEN_CONTRAST=0x2F, 1558c2ecf20Sopenharmony_ci RING_BELL=0x30, 1568c2ecf20Sopenharmony_ci SET_DIAGNOSTIC_STATUS=0x32, 1578c2ecf20Sopenharmony_ci CLEAR_KEY_COMBINATION_TABLE=0x33, 1588c2ecf20Sopenharmony_ci PERFORM_SOFTWARE_RESET=0x34, 1598c2ecf20Sopenharmony_ci SET_REAL_TIME_CLOCK=0x35, 1608c2ecf20Sopenharmony_ci RECALIBRATE_POINTING_STICK=0x36, 1618c2ecf20Sopenharmony_ci SET_BELL_FREQUENCY=0x37, 1628c2ecf20Sopenharmony_ci SET_INTERNAL_BATTERY_CHARGE_RATE=0x39, 1638c2ecf20Sopenharmony_ci SET_EXTERNAL_BATTERY_CHARGE_RATE=0x3A, 1648c2ecf20Sopenharmony_ci SET_REAL_TIME_CLOCK_ALARM=0x3B, 1658c2ecf20Sopenharmony_ci READ_EEPROM=0x40, 1668c2ecf20Sopenharmony_ci WRITE_EEPROM=0x41, 1678c2ecf20Sopenharmony_ci WRITE_TO_STATUS_DISPLAY=0x42, 1688c2ecf20Sopenharmony_ci DEFINE_SPECIAL_CHARACTER=0x43, 1698c2ecf20Sopenharmony_ci DEFINE_KEY_COMBINATION_ENTRY=0x50, 1708c2ecf20Sopenharmony_ci DEFINE_STRING_TABLE_ENTRY=0x51, 1718c2ecf20Sopenharmony_ci DEFINE_STATUS_SCREEN_DISPLAY=0x52, 1728c2ecf20Sopenharmony_ci PERFORM_EMU_COMMANDS=0x64, 1738c2ecf20Sopenharmony_ci READ_EMU_REGISTER=0x65, 1748c2ecf20Sopenharmony_ci WRITE_EMU_REGISTER=0x66, 1758c2ecf20Sopenharmony_ci READ_EMU_RAM=0x67, 1768c2ecf20Sopenharmony_ci WRITE_EMU_RAM=0x68, 1778c2ecf20Sopenharmony_ci READ_BQ_REGISTER=0x69, 1788c2ecf20Sopenharmony_ci WRITE_BQ_REGISTER=0x6A, 1798c2ecf20Sopenharmony_ci SET_USER_PASSWORD=0x70, 1808c2ecf20Sopenharmony_ci VERIFY_USER_PASSWORD=0x71, 1818c2ecf20Sopenharmony_ci GET_SYSTEM_PASSWORD_KEY=0x72, 1828c2ecf20Sopenharmony_ci VERIFY_SYSTEM_PASSWORD=0x73, 1838c2ecf20Sopenharmony_ci POWER_OFF=0x82, 1848c2ecf20Sopenharmony_ci POWER_RESTART=0x83, 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic struct uctrl_driver { 1888c2ecf20Sopenharmony_ci struct uctrl_regs __iomem *regs; 1898c2ecf20Sopenharmony_ci int irq; 1908c2ecf20Sopenharmony_ci int pending; 1918c2ecf20Sopenharmony_ci struct uctrl_status status; 1928c2ecf20Sopenharmony_ci} *global_driver; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void uctrl_get_event_status(struct uctrl_driver *); 1958c2ecf20Sopenharmony_cistatic void uctrl_get_external_status(struct uctrl_driver *); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic long 1988c2ecf20Sopenharmony_ciuctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci switch (cmd) { 2018c2ecf20Sopenharmony_ci default: 2028c2ecf20Sopenharmony_ci return -EINVAL; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int 2088c2ecf20Sopenharmony_ciuctrl_open(struct inode *inode, struct file *file) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci mutex_lock(&uctrl_mutex); 2118c2ecf20Sopenharmony_ci uctrl_get_event_status(global_driver); 2128c2ecf20Sopenharmony_ci uctrl_get_external_status(global_driver); 2138c2ecf20Sopenharmony_ci mutex_unlock(&uctrl_mutex); 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic irqreturn_t uctrl_interrupt(int irq, void *dev_id) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic const struct file_operations uctrl_fops = { 2238c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2248c2ecf20Sopenharmony_ci .llseek = no_llseek, 2258c2ecf20Sopenharmony_ci .unlocked_ioctl = uctrl_ioctl, 2268c2ecf20Sopenharmony_ci .open = uctrl_open, 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic struct miscdevice uctrl_dev = { 2308c2ecf20Sopenharmony_ci UCTRL_MINOR, 2318c2ecf20Sopenharmony_ci "uctrl", 2328c2ecf20Sopenharmony_ci &uctrl_fops 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* Wait for space to write, then write to it */ 2368c2ecf20Sopenharmony_ci#define WRITEUCTLDATA(value) \ 2378c2ecf20Sopenharmony_ci{ \ 2388c2ecf20Sopenharmony_ci unsigned int i; \ 2398c2ecf20Sopenharmony_ci for (i = 0; i < 10000; i++) { \ 2408c2ecf20Sopenharmony_ci if (UCTRL_STAT_TXNF_STA & sbus_readl(&driver->regs->uctrl_stat)) \ 2418c2ecf20Sopenharmony_ci break; \ 2428c2ecf20Sopenharmony_ci } \ 2438c2ecf20Sopenharmony_ci dprintk(("write data 0x%02x\n", value)); \ 2448c2ecf20Sopenharmony_ci sbus_writel(value, &driver->regs->uctrl_data); \ 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* Wait for something to read, read it, then clear the bit */ 2488c2ecf20Sopenharmony_ci#define READUCTLDATA(value) \ 2498c2ecf20Sopenharmony_ci{ \ 2508c2ecf20Sopenharmony_ci unsigned int i; \ 2518c2ecf20Sopenharmony_ci value = 0; \ 2528c2ecf20Sopenharmony_ci for (i = 0; i < 10000; i++) { \ 2538c2ecf20Sopenharmony_ci if ((UCTRL_STAT_RXNE_STA & sbus_readl(&driver->regs->uctrl_stat)) == 0) \ 2548c2ecf20Sopenharmony_ci break; \ 2558c2ecf20Sopenharmony_ci udelay(1); \ 2568c2ecf20Sopenharmony_ci } \ 2578c2ecf20Sopenharmony_ci value = sbus_readl(&driver->regs->uctrl_data); \ 2588c2ecf20Sopenharmony_ci dprintk(("read data 0x%02x\n", value)); \ 2598c2ecf20Sopenharmony_ci sbus_writel(UCTRL_STAT_RXNE_STA, &driver->regs->uctrl_stat); \ 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void uctrl_do_txn(struct uctrl_driver *driver, struct uctrl_txn *txn) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci int stat, incnt, outcnt, bytecnt, intr; 2658c2ecf20Sopenharmony_ci u32 byte; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci stat = sbus_readl(&driver->regs->uctrl_stat); 2688c2ecf20Sopenharmony_ci intr = sbus_readl(&driver->regs->uctrl_intr); 2698c2ecf20Sopenharmony_ci sbus_writel(stat, &driver->regs->uctrl_stat); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci dprintk(("interrupt stat 0x%x int 0x%x\n", stat, intr)); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci incnt = txn->inbits; 2748c2ecf20Sopenharmony_ci outcnt = txn->outbits; 2758c2ecf20Sopenharmony_ci byte = (txn->opcode << 8); 2768c2ecf20Sopenharmony_ci WRITEUCTLDATA(byte); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci bytecnt = 0; 2798c2ecf20Sopenharmony_ci while (incnt > 0) { 2808c2ecf20Sopenharmony_ci byte = (txn->inbuf[bytecnt] << 8); 2818c2ecf20Sopenharmony_ci WRITEUCTLDATA(byte); 2828c2ecf20Sopenharmony_ci incnt--; 2838c2ecf20Sopenharmony_ci bytecnt++; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Get the ack */ 2878c2ecf20Sopenharmony_ci READUCTLDATA(byte); 2888c2ecf20Sopenharmony_ci dprintk(("ack was %x\n", (byte >> 8))); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci bytecnt = 0; 2918c2ecf20Sopenharmony_ci while (outcnt > 0) { 2928c2ecf20Sopenharmony_ci READUCTLDATA(byte); 2938c2ecf20Sopenharmony_ci txn->outbuf[bytecnt] = (byte >> 8); 2948c2ecf20Sopenharmony_ci dprintk(("set byte to %02x\n", byte)); 2958c2ecf20Sopenharmony_ci outcnt--; 2968c2ecf20Sopenharmony_ci bytecnt++; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic void uctrl_get_event_status(struct uctrl_driver *driver) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct uctrl_txn txn; 3038c2ecf20Sopenharmony_ci u8 outbits[2]; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci txn.opcode = READ_EVENT_STATUS; 3068c2ecf20Sopenharmony_ci txn.inbits = 0; 3078c2ecf20Sopenharmony_ci txn.outbits = 2; 3088c2ecf20Sopenharmony_ci txn.inbuf = NULL; 3098c2ecf20Sopenharmony_ci txn.outbuf = outbits; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci uctrl_do_txn(driver, &txn); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); 3148c2ecf20Sopenharmony_ci driver->status.event_status = 3158c2ecf20Sopenharmony_ci ((outbits[0] & 0xff) << 8) | (outbits[1] & 0xff); 3168c2ecf20Sopenharmony_ci dprintk(("ev is %x\n", driver->status.event_status)); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void uctrl_get_external_status(struct uctrl_driver *driver) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct uctrl_txn txn; 3228c2ecf20Sopenharmony_ci u8 outbits[2]; 3238c2ecf20Sopenharmony_ci int i, v; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci txn.opcode = READ_EXTERNAL_STATUS; 3268c2ecf20Sopenharmony_ci txn.inbits = 0; 3278c2ecf20Sopenharmony_ci txn.outbits = 2; 3288c2ecf20Sopenharmony_ci txn.inbuf = NULL; 3298c2ecf20Sopenharmony_ci txn.outbuf = outbits; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci uctrl_do_txn(driver, &txn); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); 3348c2ecf20Sopenharmony_ci driver->status.external_status = 3358c2ecf20Sopenharmony_ci ((outbits[0] * 256) + (outbits[1])); 3368c2ecf20Sopenharmony_ci dprintk(("ex is %x\n", driver->status.external_status)); 3378c2ecf20Sopenharmony_ci v = driver->status.external_status; 3388c2ecf20Sopenharmony_ci for (i = 0; v != 0; i++, v >>= 1) { 3398c2ecf20Sopenharmony_ci if (v & 1) { 3408c2ecf20Sopenharmony_ci dprintk(("%s%s", " ", uctrl_extstatus[i])); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci dprintk(("\n")); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int uctrl_probe(struct platform_device *op) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct uctrl_driver *p; 3508c2ecf20Sopenharmony_ci int err = -ENOMEM; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci p = kzalloc(sizeof(*p), GFP_KERNEL); 3538c2ecf20Sopenharmony_ci if (!p) { 3548c2ecf20Sopenharmony_ci printk(KERN_ERR "uctrl: Unable to allocate device struct.\n"); 3558c2ecf20Sopenharmony_ci goto out; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci p->regs = of_ioremap(&op->resource[0], 0, 3598c2ecf20Sopenharmony_ci resource_size(&op->resource[0]), 3608c2ecf20Sopenharmony_ci "uctrl"); 3618c2ecf20Sopenharmony_ci if (!p->regs) { 3628c2ecf20Sopenharmony_ci printk(KERN_ERR "uctrl: Unable to map registers.\n"); 3638c2ecf20Sopenharmony_ci goto out_free; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci p->irq = op->archdata.irqs[0]; 3678c2ecf20Sopenharmony_ci err = request_irq(p->irq, uctrl_interrupt, 0, "uctrl", p); 3688c2ecf20Sopenharmony_ci if (err) { 3698c2ecf20Sopenharmony_ci printk(KERN_ERR "uctrl: Unable to register irq.\n"); 3708c2ecf20Sopenharmony_ci goto out_iounmap; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci err = misc_register(&uctrl_dev); 3748c2ecf20Sopenharmony_ci if (err) { 3758c2ecf20Sopenharmony_ci printk(KERN_ERR "uctrl: Unable to register misc device.\n"); 3768c2ecf20Sopenharmony_ci goto out_free_irq; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci sbus_writel(UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK, &p->regs->uctrl_intr); 3808c2ecf20Sopenharmony_ci printk(KERN_INFO "%pOF: uctrl regs[0x%p] (irq %d)\n", 3818c2ecf20Sopenharmony_ci op->dev.of_node, p->regs, p->irq); 3828c2ecf20Sopenharmony_ci uctrl_get_event_status(p); 3838c2ecf20Sopenharmony_ci uctrl_get_external_status(p); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci dev_set_drvdata(&op->dev, p); 3868c2ecf20Sopenharmony_ci global_driver = p; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ciout: 3898c2ecf20Sopenharmony_ci return err; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ciout_free_irq: 3928c2ecf20Sopenharmony_ci free_irq(p->irq, p); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ciout_iounmap: 3958c2ecf20Sopenharmony_ci of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0])); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ciout_free: 3988c2ecf20Sopenharmony_ci kfree(p); 3998c2ecf20Sopenharmony_ci goto out; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int uctrl_remove(struct platform_device *op) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct uctrl_driver *p = dev_get_drvdata(&op->dev); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (p) { 4078c2ecf20Sopenharmony_ci misc_deregister(&uctrl_dev); 4088c2ecf20Sopenharmony_ci free_irq(p->irq, p); 4098c2ecf20Sopenharmony_ci of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0])); 4108c2ecf20Sopenharmony_ci kfree(p); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic const struct of_device_id uctrl_match[] = { 4168c2ecf20Sopenharmony_ci { 4178c2ecf20Sopenharmony_ci .name = "uctrl", 4188c2ecf20Sopenharmony_ci }, 4198c2ecf20Sopenharmony_ci {}, 4208c2ecf20Sopenharmony_ci}; 4218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, uctrl_match); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic struct platform_driver uctrl_driver = { 4248c2ecf20Sopenharmony_ci .driver = { 4258c2ecf20Sopenharmony_ci .name = "uctrl", 4268c2ecf20Sopenharmony_ci .of_match_table = uctrl_match, 4278c2ecf20Sopenharmony_ci }, 4288c2ecf20Sopenharmony_ci .probe = uctrl_probe, 4298c2ecf20Sopenharmony_ci .remove = uctrl_remove, 4308c2ecf20Sopenharmony_ci}; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cimodule_platform_driver(uctrl_driver); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 436