162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 1999 Derrick J Brashear (shadow@dementia.org) 562306a36Sopenharmony_ci * Copyright 2008 David S. Miller (davem@davemloft.net) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/mutex.h> 1462306a36Sopenharmony_ci#include <linux/ioport.h> 1562306a36Sopenharmony_ci#include <linux/miscdevice.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/openprom.h> 2162306a36Sopenharmony_ci#include <asm/oplib.h> 2262306a36Sopenharmony_ci#include <asm/irq.h> 2362306a36Sopenharmony_ci#include <asm/io.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define DEBUG 1 2662306a36Sopenharmony_ci#ifdef DEBUG 2762306a36Sopenharmony_ci#define dprintk(x) printk x 2862306a36Sopenharmony_ci#else 2962306a36Sopenharmony_ci#define dprintk(x) 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct uctrl_regs { 3362306a36Sopenharmony_ci u32 uctrl_intr; 3462306a36Sopenharmony_ci u32 uctrl_data; 3562306a36Sopenharmony_ci u32 uctrl_stat; 3662306a36Sopenharmony_ci u32 uctrl_xxx[5]; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct ts102_regs { 4062306a36Sopenharmony_ci u32 card_a_intr; 4162306a36Sopenharmony_ci u32 card_a_stat; 4262306a36Sopenharmony_ci u32 card_a_ctrl; 4362306a36Sopenharmony_ci u32 card_a_xxx; 4462306a36Sopenharmony_ci u32 card_b_intr; 4562306a36Sopenharmony_ci u32 card_b_stat; 4662306a36Sopenharmony_ci u32 card_b_ctrl; 4762306a36Sopenharmony_ci u32 card_b_xxx; 4862306a36Sopenharmony_ci u32 uctrl_intr; 4962306a36Sopenharmony_ci u32 uctrl_data; 5062306a36Sopenharmony_ci u32 uctrl_stat; 5162306a36Sopenharmony_ci u32 uctrl_xxx; 5262306a36Sopenharmony_ci u32 ts102_xxx[4]; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Bits for uctrl_intr register */ 5662306a36Sopenharmony_ci#define UCTRL_INTR_TXE_REQ 0x01 /* transmit FIFO empty int req */ 5762306a36Sopenharmony_ci#define UCTRL_INTR_TXNF_REQ 0x02 /* transmit FIFO not full int req */ 5862306a36Sopenharmony_ci#define UCTRL_INTR_RXNE_REQ 0x04 /* receive FIFO not empty int req */ 5962306a36Sopenharmony_ci#define UCTRL_INTR_RXO_REQ 0x08 /* receive FIFO overflow int req */ 6062306a36Sopenharmony_ci#define UCTRL_INTR_TXE_MSK 0x10 /* transmit FIFO empty mask */ 6162306a36Sopenharmony_ci#define UCTRL_INTR_TXNF_MSK 0x20 /* transmit FIFO not full mask */ 6262306a36Sopenharmony_ci#define UCTRL_INTR_RXNE_MSK 0x40 /* receive FIFO not empty mask */ 6362306a36Sopenharmony_ci#define UCTRL_INTR_RXO_MSK 0x80 /* receive FIFO overflow mask */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Bits for uctrl_stat register */ 6662306a36Sopenharmony_ci#define UCTRL_STAT_TXE_STA 0x01 /* transmit FIFO empty status */ 6762306a36Sopenharmony_ci#define UCTRL_STAT_TXNF_STA 0x02 /* transmit FIFO not full status */ 6862306a36Sopenharmony_ci#define UCTRL_STAT_RXNE_STA 0x04 /* receive FIFO not empty status */ 6962306a36Sopenharmony_ci#define UCTRL_STAT_RXO_STA 0x08 /* receive FIFO overflow status */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic DEFINE_MUTEX(uctrl_mutex); 7262306a36Sopenharmony_cistatic const char *uctrl_extstatus[16] = { 7362306a36Sopenharmony_ci "main power available", 7462306a36Sopenharmony_ci "internal battery attached", 7562306a36Sopenharmony_ci "external battery attached", 7662306a36Sopenharmony_ci "external VGA attached", 7762306a36Sopenharmony_ci "external keyboard attached", 7862306a36Sopenharmony_ci "external mouse attached", 7962306a36Sopenharmony_ci "lid down", 8062306a36Sopenharmony_ci "internal battery currently charging", 8162306a36Sopenharmony_ci "external battery currently charging", 8262306a36Sopenharmony_ci "internal battery currently discharging", 8362306a36Sopenharmony_ci "external battery currently discharging", 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* Everything required for one transaction with the uctrl */ 8762306a36Sopenharmony_cistruct uctrl_txn { 8862306a36Sopenharmony_ci u8 opcode; 8962306a36Sopenharmony_ci u8 inbits; 9062306a36Sopenharmony_ci u8 outbits; 9162306a36Sopenharmony_ci u8 *inbuf; 9262306a36Sopenharmony_ci u8 *outbuf; 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistruct uctrl_status { 9662306a36Sopenharmony_ci u8 current_temp; /* 0x07 */ 9762306a36Sopenharmony_ci u8 reset_status; /* 0x0b */ 9862306a36Sopenharmony_ci u16 event_status; /* 0x0c */ 9962306a36Sopenharmony_ci u16 error_status; /* 0x10 */ 10062306a36Sopenharmony_ci u16 external_status; /* 0x11, 0x1b */ 10162306a36Sopenharmony_ci u8 internal_charge; /* 0x18 */ 10262306a36Sopenharmony_ci u8 external_charge; /* 0x19 */ 10362306a36Sopenharmony_ci u16 control_lcd; /* 0x20 */ 10462306a36Sopenharmony_ci u8 control_bitport; /* 0x21 */ 10562306a36Sopenharmony_ci u8 speaker_volume; /* 0x23 */ 10662306a36Sopenharmony_ci u8 control_tft_brightness; /* 0x24 */ 10762306a36Sopenharmony_ci u8 control_kbd_repeat_delay; /* 0x28 */ 10862306a36Sopenharmony_ci u8 control_kbd_repeat_period; /* 0x29 */ 10962306a36Sopenharmony_ci u8 control_screen_contrast; /* 0x2F */ 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cienum uctrl_opcode { 11362306a36Sopenharmony_ci READ_SERIAL_NUMBER=0x1, 11462306a36Sopenharmony_ci READ_ETHERNET_ADDRESS=0x2, 11562306a36Sopenharmony_ci READ_HARDWARE_VERSION=0x3, 11662306a36Sopenharmony_ci READ_MICROCONTROLLER_VERSION=0x4, 11762306a36Sopenharmony_ci READ_MAX_TEMPERATURE=0x5, 11862306a36Sopenharmony_ci READ_MIN_TEMPERATURE=0x6, 11962306a36Sopenharmony_ci READ_CURRENT_TEMPERATURE=0x7, 12062306a36Sopenharmony_ci READ_SYSTEM_VARIANT=0x8, 12162306a36Sopenharmony_ci READ_POWERON_CYCLES=0x9, 12262306a36Sopenharmony_ci READ_POWERON_SECONDS=0xA, 12362306a36Sopenharmony_ci READ_RESET_STATUS=0xB, 12462306a36Sopenharmony_ci READ_EVENT_STATUS=0xC, 12562306a36Sopenharmony_ci READ_REAL_TIME_CLOCK=0xD, 12662306a36Sopenharmony_ci READ_EXTERNAL_VGA_PORT=0xE, 12762306a36Sopenharmony_ci READ_MICROCONTROLLER_ROM_CHECKSUM=0xF, 12862306a36Sopenharmony_ci READ_ERROR_STATUS=0x10, 12962306a36Sopenharmony_ci READ_EXTERNAL_STATUS=0x11, 13062306a36Sopenharmony_ci READ_USER_CONFIGURATION_AREA=0x12, 13162306a36Sopenharmony_ci READ_MICROCONTROLLER_VOLTAGE=0x13, 13262306a36Sopenharmony_ci READ_INTERNAL_BATTERY_VOLTAGE=0x14, 13362306a36Sopenharmony_ci READ_DCIN_VOLTAGE=0x15, 13462306a36Sopenharmony_ci READ_HORIZONTAL_POINTER_VOLTAGE=0x16, 13562306a36Sopenharmony_ci READ_VERTICAL_POINTER_VOLTAGE=0x17, 13662306a36Sopenharmony_ci READ_INTERNAL_BATTERY_CHARGE_LEVEL=0x18, 13762306a36Sopenharmony_ci READ_EXTERNAL_BATTERY_CHARGE_LEVEL=0x19, 13862306a36Sopenharmony_ci READ_REAL_TIME_CLOCK_ALARM=0x1A, 13962306a36Sopenharmony_ci READ_EVENT_STATUS_NO_RESET=0x1B, 14062306a36Sopenharmony_ci READ_INTERNAL_KEYBOARD_LAYOUT=0x1C, 14162306a36Sopenharmony_ci READ_EXTERNAL_KEYBOARD_LAYOUT=0x1D, 14262306a36Sopenharmony_ci READ_EEPROM_STATUS=0x1E, 14362306a36Sopenharmony_ci CONTROL_LCD=0x20, 14462306a36Sopenharmony_ci CONTROL_BITPORT=0x21, 14562306a36Sopenharmony_ci SPEAKER_VOLUME=0x23, 14662306a36Sopenharmony_ci CONTROL_TFT_BRIGHTNESS=0x24, 14762306a36Sopenharmony_ci CONTROL_WATCHDOG=0x25, 14862306a36Sopenharmony_ci CONTROL_FACTORY_EEPROM_AREA=0x26, 14962306a36Sopenharmony_ci CONTROL_KBD_TIME_UNTIL_REPEAT=0x28, 15062306a36Sopenharmony_ci CONTROL_KBD_TIME_BETWEEN_REPEATS=0x29, 15162306a36Sopenharmony_ci CONTROL_TIMEZONE=0x2A, 15262306a36Sopenharmony_ci CONTROL_MARK_SPACE_RATIO=0x2B, 15362306a36Sopenharmony_ci CONTROL_DIAGNOSTIC_MODE=0x2E, 15462306a36Sopenharmony_ci CONTROL_SCREEN_CONTRAST=0x2F, 15562306a36Sopenharmony_ci RING_BELL=0x30, 15662306a36Sopenharmony_ci SET_DIAGNOSTIC_STATUS=0x32, 15762306a36Sopenharmony_ci CLEAR_KEY_COMBINATION_TABLE=0x33, 15862306a36Sopenharmony_ci PERFORM_SOFTWARE_RESET=0x34, 15962306a36Sopenharmony_ci SET_REAL_TIME_CLOCK=0x35, 16062306a36Sopenharmony_ci RECALIBRATE_POINTING_STICK=0x36, 16162306a36Sopenharmony_ci SET_BELL_FREQUENCY=0x37, 16262306a36Sopenharmony_ci SET_INTERNAL_BATTERY_CHARGE_RATE=0x39, 16362306a36Sopenharmony_ci SET_EXTERNAL_BATTERY_CHARGE_RATE=0x3A, 16462306a36Sopenharmony_ci SET_REAL_TIME_CLOCK_ALARM=0x3B, 16562306a36Sopenharmony_ci READ_EEPROM=0x40, 16662306a36Sopenharmony_ci WRITE_EEPROM=0x41, 16762306a36Sopenharmony_ci WRITE_TO_STATUS_DISPLAY=0x42, 16862306a36Sopenharmony_ci DEFINE_SPECIAL_CHARACTER=0x43, 16962306a36Sopenharmony_ci DEFINE_KEY_COMBINATION_ENTRY=0x50, 17062306a36Sopenharmony_ci DEFINE_STRING_TABLE_ENTRY=0x51, 17162306a36Sopenharmony_ci DEFINE_STATUS_SCREEN_DISPLAY=0x52, 17262306a36Sopenharmony_ci PERFORM_EMU_COMMANDS=0x64, 17362306a36Sopenharmony_ci READ_EMU_REGISTER=0x65, 17462306a36Sopenharmony_ci WRITE_EMU_REGISTER=0x66, 17562306a36Sopenharmony_ci READ_EMU_RAM=0x67, 17662306a36Sopenharmony_ci WRITE_EMU_RAM=0x68, 17762306a36Sopenharmony_ci READ_BQ_REGISTER=0x69, 17862306a36Sopenharmony_ci WRITE_BQ_REGISTER=0x6A, 17962306a36Sopenharmony_ci SET_USER_PASSWORD=0x70, 18062306a36Sopenharmony_ci VERIFY_USER_PASSWORD=0x71, 18162306a36Sopenharmony_ci GET_SYSTEM_PASSWORD_KEY=0x72, 18262306a36Sopenharmony_ci VERIFY_SYSTEM_PASSWORD=0x73, 18362306a36Sopenharmony_ci POWER_OFF=0x82, 18462306a36Sopenharmony_ci POWER_RESTART=0x83, 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic struct uctrl_driver { 18862306a36Sopenharmony_ci struct uctrl_regs __iomem *regs; 18962306a36Sopenharmony_ci int irq; 19062306a36Sopenharmony_ci int pending; 19162306a36Sopenharmony_ci struct uctrl_status status; 19262306a36Sopenharmony_ci} *global_driver; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void uctrl_get_event_status(struct uctrl_driver *); 19562306a36Sopenharmony_cistatic void uctrl_get_external_status(struct uctrl_driver *); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic long 19862306a36Sopenharmony_ciuctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci switch (cmd) { 20162306a36Sopenharmony_ci default: 20262306a36Sopenharmony_ci return -EINVAL; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int 20862306a36Sopenharmony_ciuctrl_open(struct inode *inode, struct file *file) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci mutex_lock(&uctrl_mutex); 21162306a36Sopenharmony_ci uctrl_get_event_status(global_driver); 21262306a36Sopenharmony_ci uctrl_get_external_status(global_driver); 21362306a36Sopenharmony_ci mutex_unlock(&uctrl_mutex); 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic irqreturn_t uctrl_interrupt(int irq, void *dev_id) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci return IRQ_HANDLED; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic const struct file_operations uctrl_fops = { 22362306a36Sopenharmony_ci .owner = THIS_MODULE, 22462306a36Sopenharmony_ci .llseek = no_llseek, 22562306a36Sopenharmony_ci .unlocked_ioctl = uctrl_ioctl, 22662306a36Sopenharmony_ci .open = uctrl_open, 22762306a36Sopenharmony_ci}; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic struct miscdevice uctrl_dev = { 23062306a36Sopenharmony_ci UCTRL_MINOR, 23162306a36Sopenharmony_ci "uctrl", 23262306a36Sopenharmony_ci &uctrl_fops 23362306a36Sopenharmony_ci}; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* Wait for space to write, then write to it */ 23662306a36Sopenharmony_ci#define WRITEUCTLDATA(value) \ 23762306a36Sopenharmony_ci{ \ 23862306a36Sopenharmony_ci unsigned int i; \ 23962306a36Sopenharmony_ci for (i = 0; i < 10000; i++) { \ 24062306a36Sopenharmony_ci if (UCTRL_STAT_TXNF_STA & sbus_readl(&driver->regs->uctrl_stat)) \ 24162306a36Sopenharmony_ci break; \ 24262306a36Sopenharmony_ci } \ 24362306a36Sopenharmony_ci dprintk(("write data 0x%02x\n", value)); \ 24462306a36Sopenharmony_ci sbus_writel(value, &driver->regs->uctrl_data); \ 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* Wait for something to read, read it, then clear the bit */ 24862306a36Sopenharmony_ci#define READUCTLDATA(value) \ 24962306a36Sopenharmony_ci{ \ 25062306a36Sopenharmony_ci unsigned int i; \ 25162306a36Sopenharmony_ci value = 0; \ 25262306a36Sopenharmony_ci for (i = 0; i < 10000; i++) { \ 25362306a36Sopenharmony_ci if ((UCTRL_STAT_RXNE_STA & sbus_readl(&driver->regs->uctrl_stat)) == 0) \ 25462306a36Sopenharmony_ci break; \ 25562306a36Sopenharmony_ci udelay(1); \ 25662306a36Sopenharmony_ci } \ 25762306a36Sopenharmony_ci value = sbus_readl(&driver->regs->uctrl_data); \ 25862306a36Sopenharmony_ci dprintk(("read data 0x%02x\n", value)); \ 25962306a36Sopenharmony_ci sbus_writel(UCTRL_STAT_RXNE_STA, &driver->regs->uctrl_stat); \ 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void uctrl_do_txn(struct uctrl_driver *driver, struct uctrl_txn *txn) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci int stat, incnt, outcnt, bytecnt, intr; 26562306a36Sopenharmony_ci u32 byte; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci stat = sbus_readl(&driver->regs->uctrl_stat); 26862306a36Sopenharmony_ci intr = sbus_readl(&driver->regs->uctrl_intr); 26962306a36Sopenharmony_ci sbus_writel(stat, &driver->regs->uctrl_stat); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci dprintk(("interrupt stat 0x%x int 0x%x\n", stat, intr)); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci incnt = txn->inbits; 27462306a36Sopenharmony_ci outcnt = txn->outbits; 27562306a36Sopenharmony_ci byte = (txn->opcode << 8); 27662306a36Sopenharmony_ci WRITEUCTLDATA(byte); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci bytecnt = 0; 27962306a36Sopenharmony_ci while (incnt > 0) { 28062306a36Sopenharmony_ci byte = (txn->inbuf[bytecnt] << 8); 28162306a36Sopenharmony_ci WRITEUCTLDATA(byte); 28262306a36Sopenharmony_ci incnt--; 28362306a36Sopenharmony_ci bytecnt++; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Get the ack */ 28762306a36Sopenharmony_ci READUCTLDATA(byte); 28862306a36Sopenharmony_ci dprintk(("ack was %x\n", (byte >> 8))); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci bytecnt = 0; 29162306a36Sopenharmony_ci while (outcnt > 0) { 29262306a36Sopenharmony_ci READUCTLDATA(byte); 29362306a36Sopenharmony_ci txn->outbuf[bytecnt] = (byte >> 8); 29462306a36Sopenharmony_ci dprintk(("set byte to %02x\n", byte)); 29562306a36Sopenharmony_ci outcnt--; 29662306a36Sopenharmony_ci bytecnt++; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic void uctrl_get_event_status(struct uctrl_driver *driver) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct uctrl_txn txn; 30362306a36Sopenharmony_ci u8 outbits[2]; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci txn.opcode = READ_EVENT_STATUS; 30662306a36Sopenharmony_ci txn.inbits = 0; 30762306a36Sopenharmony_ci txn.outbits = 2; 30862306a36Sopenharmony_ci txn.inbuf = NULL; 30962306a36Sopenharmony_ci txn.outbuf = outbits; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci uctrl_do_txn(driver, &txn); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); 31462306a36Sopenharmony_ci driver->status.event_status = 31562306a36Sopenharmony_ci ((outbits[0] & 0xff) << 8) | (outbits[1] & 0xff); 31662306a36Sopenharmony_ci dprintk(("ev is %x\n", driver->status.event_status)); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void uctrl_get_external_status(struct uctrl_driver *driver) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct uctrl_txn txn; 32262306a36Sopenharmony_ci u8 outbits[2]; 32362306a36Sopenharmony_ci int i, v; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci txn.opcode = READ_EXTERNAL_STATUS; 32662306a36Sopenharmony_ci txn.inbits = 0; 32762306a36Sopenharmony_ci txn.outbits = 2; 32862306a36Sopenharmony_ci txn.inbuf = NULL; 32962306a36Sopenharmony_ci txn.outbuf = outbits; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci uctrl_do_txn(driver, &txn); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); 33462306a36Sopenharmony_ci driver->status.external_status = 33562306a36Sopenharmony_ci ((outbits[0] * 256) + (outbits[1])); 33662306a36Sopenharmony_ci dprintk(("ex is %x\n", driver->status.external_status)); 33762306a36Sopenharmony_ci v = driver->status.external_status; 33862306a36Sopenharmony_ci for (i = 0; v != 0; i++, v >>= 1) { 33962306a36Sopenharmony_ci if (v & 1) { 34062306a36Sopenharmony_ci dprintk(("%s%s", " ", uctrl_extstatus[i])); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci dprintk(("\n")); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int uctrl_probe(struct platform_device *op) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct uctrl_driver *p; 35062306a36Sopenharmony_ci int err = -ENOMEM; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci p = kzalloc(sizeof(*p), GFP_KERNEL); 35362306a36Sopenharmony_ci if (!p) { 35462306a36Sopenharmony_ci printk(KERN_ERR "uctrl: Unable to allocate device struct.\n"); 35562306a36Sopenharmony_ci goto out; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci p->regs = of_ioremap(&op->resource[0], 0, 35962306a36Sopenharmony_ci resource_size(&op->resource[0]), 36062306a36Sopenharmony_ci "uctrl"); 36162306a36Sopenharmony_ci if (!p->regs) { 36262306a36Sopenharmony_ci printk(KERN_ERR "uctrl: Unable to map registers.\n"); 36362306a36Sopenharmony_ci goto out_free; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci p->irq = op->archdata.irqs[0]; 36762306a36Sopenharmony_ci err = request_irq(p->irq, uctrl_interrupt, 0, "uctrl", p); 36862306a36Sopenharmony_ci if (err) { 36962306a36Sopenharmony_ci printk(KERN_ERR "uctrl: Unable to register irq.\n"); 37062306a36Sopenharmony_ci goto out_iounmap; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci err = misc_register(&uctrl_dev); 37462306a36Sopenharmony_ci if (err) { 37562306a36Sopenharmony_ci printk(KERN_ERR "uctrl: Unable to register misc device.\n"); 37662306a36Sopenharmony_ci goto out_free_irq; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci sbus_writel(UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK, &p->regs->uctrl_intr); 38062306a36Sopenharmony_ci printk(KERN_INFO "%pOF: uctrl regs[0x%p] (irq %d)\n", 38162306a36Sopenharmony_ci op->dev.of_node, p->regs, p->irq); 38262306a36Sopenharmony_ci uctrl_get_event_status(p); 38362306a36Sopenharmony_ci uctrl_get_external_status(p); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci dev_set_drvdata(&op->dev, p); 38662306a36Sopenharmony_ci global_driver = p; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ciout: 38962306a36Sopenharmony_ci return err; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ciout_free_irq: 39262306a36Sopenharmony_ci free_irq(p->irq, p); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ciout_iounmap: 39562306a36Sopenharmony_ci of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0])); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ciout_free: 39862306a36Sopenharmony_ci kfree(p); 39962306a36Sopenharmony_ci goto out; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int uctrl_remove(struct platform_device *op) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct uctrl_driver *p = dev_get_drvdata(&op->dev); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (p) { 40762306a36Sopenharmony_ci misc_deregister(&uctrl_dev); 40862306a36Sopenharmony_ci free_irq(p->irq, p); 40962306a36Sopenharmony_ci of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0])); 41062306a36Sopenharmony_ci kfree(p); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic const struct of_device_id uctrl_match[] = { 41662306a36Sopenharmony_ci { 41762306a36Sopenharmony_ci .name = "uctrl", 41862306a36Sopenharmony_ci }, 41962306a36Sopenharmony_ci {}, 42062306a36Sopenharmony_ci}; 42162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, uctrl_match); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic struct platform_driver uctrl_driver = { 42462306a36Sopenharmony_ci .driver = { 42562306a36Sopenharmony_ci .name = "uctrl", 42662306a36Sopenharmony_ci .of_match_table = uctrl_match, 42762306a36Sopenharmony_ci }, 42862306a36Sopenharmony_ci .probe = uctrl_probe, 42962306a36Sopenharmony_ci .remove = uctrl_remove, 43062306a36Sopenharmony_ci}; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cimodule_platform_driver(uctrl_driver); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 436