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