xref: /openbmc/linux/drivers/sbus/char/uctrl.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2237f8aafSDavid S. Miller /* uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright 1999 Derrick J Brashear (shadow@dementia.org)
5237f8aafSDavid S. Miller  * Copyright 2008 David S. Miller (davem@davemloft.net)
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds #include <linux/module.h>
91da177e4SLinus Torvalds #include <linux/errno.h>
101da177e4SLinus Torvalds #include <linux/delay.h>
111da177e4SLinus Torvalds #include <linux/interrupt.h>
121da177e4SLinus Torvalds #include <linux/slab.h>
13a3108ca2SArnd Bergmann #include <linux/mutex.h>
141da177e4SLinus Torvalds #include <linux/ioport.h>
151da177e4SLinus Torvalds #include <linux/miscdevice.h>
161da177e4SLinus Torvalds #include <linux/mm.h>
17237f8aafSDavid S. Miller #include <linux/of.h>
18*878f2774SRob Herring #include <linux/platform_device.h>
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #include <asm/openprom.h>
211da177e4SLinus Torvalds #include <asm/oplib.h>
221da177e4SLinus Torvalds #include <asm/irq.h>
231da177e4SLinus Torvalds #include <asm/io.h>
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #define DEBUG 1
261da177e4SLinus Torvalds #ifdef DEBUG
271da177e4SLinus Torvalds #define dprintk(x) printk x
281da177e4SLinus Torvalds #else
291da177e4SLinus Torvalds #define dprintk(x)
301da177e4SLinus Torvalds #endif
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds struct uctrl_regs {
33237f8aafSDavid S. Miller 	u32 uctrl_intr;
34237f8aafSDavid S. Miller 	u32 uctrl_data;
35237f8aafSDavid S. Miller 	u32 uctrl_stat;
36237f8aafSDavid S. Miller 	u32 uctrl_xxx[5];
371da177e4SLinus Torvalds };
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds struct ts102_regs {
40237f8aafSDavid S. Miller 	u32 card_a_intr;
41237f8aafSDavid S. Miller 	u32 card_a_stat;
42237f8aafSDavid S. Miller 	u32 card_a_ctrl;
43237f8aafSDavid S. Miller 	u32 card_a_xxx;
44237f8aafSDavid S. Miller 	u32 card_b_intr;
45237f8aafSDavid S. Miller 	u32 card_b_stat;
46237f8aafSDavid S. Miller 	u32 card_b_ctrl;
47237f8aafSDavid S. Miller 	u32 card_b_xxx;
48237f8aafSDavid S. Miller 	u32 uctrl_intr;
49237f8aafSDavid S. Miller 	u32 uctrl_data;
50237f8aafSDavid S. Miller 	u32 uctrl_stat;
51237f8aafSDavid S. Miller 	u32 uctrl_xxx;
52237f8aafSDavid S. Miller 	u32 ts102_xxx[4];
531da177e4SLinus Torvalds };
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds /* Bits for uctrl_intr register */
561da177e4SLinus Torvalds #define UCTRL_INTR_TXE_REQ         0x01    /* transmit FIFO empty int req */
571da177e4SLinus Torvalds #define UCTRL_INTR_TXNF_REQ        0x02    /* transmit FIFO not full int req */
581da177e4SLinus Torvalds #define UCTRL_INTR_RXNE_REQ        0x04    /* receive FIFO not empty int req */
591da177e4SLinus Torvalds #define UCTRL_INTR_RXO_REQ         0x08    /* receive FIFO overflow int req */
601da177e4SLinus Torvalds #define UCTRL_INTR_TXE_MSK         0x10    /* transmit FIFO empty mask */
611da177e4SLinus Torvalds #define UCTRL_INTR_TXNF_MSK        0x20    /* transmit FIFO not full mask */
621da177e4SLinus Torvalds #define UCTRL_INTR_RXNE_MSK        0x40    /* receive FIFO not empty mask */
631da177e4SLinus Torvalds #define UCTRL_INTR_RXO_MSK         0x80    /* receive FIFO overflow mask */
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds /* Bits for uctrl_stat register */
661da177e4SLinus Torvalds #define UCTRL_STAT_TXE_STA         0x01    /* transmit FIFO empty status */
671da177e4SLinus Torvalds #define UCTRL_STAT_TXNF_STA        0x02    /* transmit FIFO not full status */
681da177e4SLinus Torvalds #define UCTRL_STAT_RXNE_STA        0x04    /* receive FIFO not empty status */
691da177e4SLinus Torvalds #define UCTRL_STAT_RXO_STA         0x08    /* receive FIFO overflow status */
701da177e4SLinus Torvalds 
71a3108ca2SArnd Bergmann static DEFINE_MUTEX(uctrl_mutex);
721da177e4SLinus Torvalds static const char *uctrl_extstatus[16] = {
731da177e4SLinus Torvalds         "main power available",
741da177e4SLinus Torvalds         "internal battery attached",
751da177e4SLinus Torvalds         "external battery attached",
761da177e4SLinus Torvalds         "external VGA attached",
771da177e4SLinus Torvalds         "external keyboard attached",
781da177e4SLinus Torvalds         "external mouse attached",
791da177e4SLinus Torvalds         "lid down",
801da177e4SLinus Torvalds         "internal battery currently charging",
811da177e4SLinus Torvalds         "external battery currently charging",
821da177e4SLinus Torvalds         "internal battery currently discharging",
831da177e4SLinus Torvalds         "external battery currently discharging",
841da177e4SLinus Torvalds };
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds /* Everything required for one transaction with the uctrl */
871da177e4SLinus Torvalds struct uctrl_txn {
881da177e4SLinus Torvalds 	u8 opcode;
891da177e4SLinus Torvalds 	u8 inbits;
901da177e4SLinus Torvalds 	u8 outbits;
911da177e4SLinus Torvalds 	u8 *inbuf;
921da177e4SLinus Torvalds 	u8 *outbuf;
931da177e4SLinus Torvalds };
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds struct uctrl_status {
961da177e4SLinus Torvalds 	u8 current_temp; /* 0x07 */
971da177e4SLinus Torvalds 	u8 reset_status; /* 0x0b */
981da177e4SLinus Torvalds 	u16 event_status; /* 0x0c */
991da177e4SLinus Torvalds 	u16 error_status; /* 0x10 */
1001da177e4SLinus Torvalds 	u16 external_status; /* 0x11, 0x1b */
1011da177e4SLinus Torvalds 	u8 internal_charge; /* 0x18 */
1021da177e4SLinus Torvalds 	u8 external_charge; /* 0x19 */
1031da177e4SLinus Torvalds 	u16 control_lcd; /* 0x20 */
1041da177e4SLinus Torvalds 	u8 control_bitport; /* 0x21 */
1051da177e4SLinus Torvalds 	u8 speaker_volume; /* 0x23 */
1061da177e4SLinus Torvalds 	u8 control_tft_brightness; /* 0x24 */
1071da177e4SLinus Torvalds 	u8 control_kbd_repeat_delay; /* 0x28 */
1081da177e4SLinus Torvalds 	u8 control_kbd_repeat_period; /* 0x29 */
1091da177e4SLinus Torvalds 	u8 control_screen_contrast; /* 0x2F */
1101da177e4SLinus Torvalds };
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds enum uctrl_opcode {
1131da177e4SLinus Torvalds   READ_SERIAL_NUMBER=0x1,
1141da177e4SLinus Torvalds   READ_ETHERNET_ADDRESS=0x2,
1151da177e4SLinus Torvalds   READ_HARDWARE_VERSION=0x3,
1161da177e4SLinus Torvalds   READ_MICROCONTROLLER_VERSION=0x4,
1171da177e4SLinus Torvalds   READ_MAX_TEMPERATURE=0x5,
1181da177e4SLinus Torvalds   READ_MIN_TEMPERATURE=0x6,
1191da177e4SLinus Torvalds   READ_CURRENT_TEMPERATURE=0x7,
1201da177e4SLinus Torvalds   READ_SYSTEM_VARIANT=0x8,
1211da177e4SLinus Torvalds   READ_POWERON_CYCLES=0x9,
1221da177e4SLinus Torvalds   READ_POWERON_SECONDS=0xA,
1231da177e4SLinus Torvalds   READ_RESET_STATUS=0xB,
1241da177e4SLinus Torvalds   READ_EVENT_STATUS=0xC,
1251da177e4SLinus Torvalds   READ_REAL_TIME_CLOCK=0xD,
1261da177e4SLinus Torvalds   READ_EXTERNAL_VGA_PORT=0xE,
1271da177e4SLinus Torvalds   READ_MICROCONTROLLER_ROM_CHECKSUM=0xF,
1281da177e4SLinus Torvalds   READ_ERROR_STATUS=0x10,
1291da177e4SLinus Torvalds   READ_EXTERNAL_STATUS=0x11,
1301da177e4SLinus Torvalds   READ_USER_CONFIGURATION_AREA=0x12,
1311da177e4SLinus Torvalds   READ_MICROCONTROLLER_VOLTAGE=0x13,
1321da177e4SLinus Torvalds   READ_INTERNAL_BATTERY_VOLTAGE=0x14,
1331da177e4SLinus Torvalds   READ_DCIN_VOLTAGE=0x15,
1341da177e4SLinus Torvalds   READ_HORIZONTAL_POINTER_VOLTAGE=0x16,
1351da177e4SLinus Torvalds   READ_VERTICAL_POINTER_VOLTAGE=0x17,
1361da177e4SLinus Torvalds   READ_INTERNAL_BATTERY_CHARGE_LEVEL=0x18,
1371da177e4SLinus Torvalds   READ_EXTERNAL_BATTERY_CHARGE_LEVEL=0x19,
1381da177e4SLinus Torvalds   READ_REAL_TIME_CLOCK_ALARM=0x1A,
1391da177e4SLinus Torvalds   READ_EVENT_STATUS_NO_RESET=0x1B,
1401da177e4SLinus Torvalds   READ_INTERNAL_KEYBOARD_LAYOUT=0x1C,
1411da177e4SLinus Torvalds   READ_EXTERNAL_KEYBOARD_LAYOUT=0x1D,
1421da177e4SLinus Torvalds   READ_EEPROM_STATUS=0x1E,
1431da177e4SLinus Torvalds   CONTROL_LCD=0x20,
1441da177e4SLinus Torvalds   CONTROL_BITPORT=0x21,
1451da177e4SLinus Torvalds   SPEAKER_VOLUME=0x23,
1461da177e4SLinus Torvalds   CONTROL_TFT_BRIGHTNESS=0x24,
1471da177e4SLinus Torvalds   CONTROL_WATCHDOG=0x25,
1481da177e4SLinus Torvalds   CONTROL_FACTORY_EEPROM_AREA=0x26,
1491da177e4SLinus Torvalds   CONTROL_KBD_TIME_UNTIL_REPEAT=0x28,
1501da177e4SLinus Torvalds   CONTROL_KBD_TIME_BETWEEN_REPEATS=0x29,
1511da177e4SLinus Torvalds   CONTROL_TIMEZONE=0x2A,
1521da177e4SLinus Torvalds   CONTROL_MARK_SPACE_RATIO=0x2B,
1531da177e4SLinus Torvalds   CONTROL_DIAGNOSTIC_MODE=0x2E,
1541da177e4SLinus Torvalds   CONTROL_SCREEN_CONTRAST=0x2F,
1551da177e4SLinus Torvalds   RING_BELL=0x30,
1561da177e4SLinus Torvalds   SET_DIAGNOSTIC_STATUS=0x32,
1571da177e4SLinus Torvalds   CLEAR_KEY_COMBINATION_TABLE=0x33,
1581da177e4SLinus Torvalds   PERFORM_SOFTWARE_RESET=0x34,
1591da177e4SLinus Torvalds   SET_REAL_TIME_CLOCK=0x35,
1601da177e4SLinus Torvalds   RECALIBRATE_POINTING_STICK=0x36,
1611da177e4SLinus Torvalds   SET_BELL_FREQUENCY=0x37,
1621da177e4SLinus Torvalds   SET_INTERNAL_BATTERY_CHARGE_RATE=0x39,
1631da177e4SLinus Torvalds   SET_EXTERNAL_BATTERY_CHARGE_RATE=0x3A,
1641da177e4SLinus Torvalds   SET_REAL_TIME_CLOCK_ALARM=0x3B,
1651da177e4SLinus Torvalds   READ_EEPROM=0x40,
1661da177e4SLinus Torvalds   WRITE_EEPROM=0x41,
1671da177e4SLinus Torvalds   WRITE_TO_STATUS_DISPLAY=0x42,
1681da177e4SLinus Torvalds   DEFINE_SPECIAL_CHARACTER=0x43,
1691da177e4SLinus Torvalds   DEFINE_KEY_COMBINATION_ENTRY=0x50,
1701da177e4SLinus Torvalds   DEFINE_STRING_TABLE_ENTRY=0x51,
1711da177e4SLinus Torvalds   DEFINE_STATUS_SCREEN_DISPLAY=0x52,
1721da177e4SLinus Torvalds   PERFORM_EMU_COMMANDS=0x64,
1731da177e4SLinus Torvalds   READ_EMU_REGISTER=0x65,
1741da177e4SLinus Torvalds   WRITE_EMU_REGISTER=0x66,
1751da177e4SLinus Torvalds   READ_EMU_RAM=0x67,
1761da177e4SLinus Torvalds   WRITE_EMU_RAM=0x68,
1771da177e4SLinus Torvalds   READ_BQ_REGISTER=0x69,
1781da177e4SLinus Torvalds   WRITE_BQ_REGISTER=0x6A,
1791da177e4SLinus Torvalds   SET_USER_PASSWORD=0x70,
1801da177e4SLinus Torvalds   VERIFY_USER_PASSWORD=0x71,
1811da177e4SLinus Torvalds   GET_SYSTEM_PASSWORD_KEY=0x72,
1821da177e4SLinus Torvalds   VERIFY_SYSTEM_PASSWORD=0x73,
1831da177e4SLinus Torvalds   POWER_OFF=0x82,
1841da177e4SLinus Torvalds   POWER_RESTART=0x83,
1851da177e4SLinus Torvalds };
1861da177e4SLinus Torvalds 
187237f8aafSDavid S. Miller static struct uctrl_driver {
188237f8aafSDavid S. Miller 	struct uctrl_regs __iomem *regs;
1891da177e4SLinus Torvalds 	int irq;
1901da177e4SLinus Torvalds 	int pending;
1911da177e4SLinus Torvalds 	struct uctrl_status status;
192237f8aafSDavid S. Miller } *global_driver;
1931da177e4SLinus Torvalds 
194237f8aafSDavid S. Miller static void uctrl_get_event_status(struct uctrl_driver *);
195237f8aafSDavid S. Miller static void uctrl_get_external_status(struct uctrl_driver *);
1961da177e4SLinus Torvalds 
1976c0f8bc7SStoyan Gaydarov static long
uctrl_ioctl(struct file * file,unsigned int cmd,unsigned long arg)1986c0f8bc7SStoyan Gaydarov uctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1991da177e4SLinus Torvalds {
2001da177e4SLinus Torvalds 	switch (cmd) {
2011da177e4SLinus Torvalds 		default:
2021da177e4SLinus Torvalds 			return -EINVAL;
2031da177e4SLinus Torvalds 	}
2041da177e4SLinus Torvalds 	return 0;
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds static int
uctrl_open(struct inode * inode,struct file * file)2081da177e4SLinus Torvalds uctrl_open(struct inode *inode, struct file *file)
2091da177e4SLinus Torvalds {
210a3108ca2SArnd Bergmann 	mutex_lock(&uctrl_mutex);
211237f8aafSDavid S. Miller 	uctrl_get_event_status(global_driver);
212237f8aafSDavid S. Miller 	uctrl_get_external_status(global_driver);
213a3108ca2SArnd Bergmann 	mutex_unlock(&uctrl_mutex);
2141da177e4SLinus Torvalds 	return 0;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
uctrl_interrupt(int irq,void * dev_id)2177d12e780SDavid Howells static irqreturn_t uctrl_interrupt(int irq, void *dev_id)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	return IRQ_HANDLED;
2201da177e4SLinus Torvalds }
2211da177e4SLinus Torvalds 
22200977a59SArjan van de Ven static const struct file_operations uctrl_fops = {
2231da177e4SLinus Torvalds 	.owner =	THIS_MODULE,
2241da177e4SLinus Torvalds 	.llseek =	no_llseek,
2256c0f8bc7SStoyan Gaydarov 	.unlocked_ioctl =	uctrl_ioctl,
2261da177e4SLinus Torvalds 	.open =		uctrl_open,
2271da177e4SLinus Torvalds };
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds static struct miscdevice uctrl_dev = {
2301da177e4SLinus Torvalds 	UCTRL_MINOR,
2311da177e4SLinus Torvalds 	"uctrl",
2321da177e4SLinus Torvalds 	&uctrl_fops
2331da177e4SLinus Torvalds };
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds /* Wait for space to write, then write to it */
2361da177e4SLinus Torvalds #define WRITEUCTLDATA(value) \
2371da177e4SLinus Torvalds { \
2381da177e4SLinus Torvalds   unsigned int i; \
2391da177e4SLinus Torvalds   for (i = 0; i < 10000; i++) { \
240237f8aafSDavid S. Miller       if (UCTRL_STAT_TXNF_STA & sbus_readl(&driver->regs->uctrl_stat)) \
2411da177e4SLinus Torvalds       break; \
2421da177e4SLinus Torvalds   } \
2431da177e4SLinus Torvalds   dprintk(("write data 0x%02x\n", value)); \
244237f8aafSDavid S. Miller   sbus_writel(value, &driver->regs->uctrl_data); \
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds /* Wait for something to read, read it, then clear the bit */
2481da177e4SLinus Torvalds #define READUCTLDATA(value) \
2491da177e4SLinus Torvalds { \
2501da177e4SLinus Torvalds   unsigned int i; \
2511da177e4SLinus Torvalds   value = 0; \
2521da177e4SLinus Torvalds   for (i = 0; i < 10000; i++) { \
253237f8aafSDavid S. Miller       if ((UCTRL_STAT_RXNE_STA & sbus_readl(&driver->regs->uctrl_stat)) == 0) \
2541da177e4SLinus Torvalds       break; \
2551da177e4SLinus Torvalds     udelay(1); \
2561da177e4SLinus Torvalds   } \
257237f8aafSDavid S. Miller   value = sbus_readl(&driver->regs->uctrl_data); \
2581da177e4SLinus Torvalds   dprintk(("read data 0x%02x\n", value)); \
259237f8aafSDavid S. Miller   sbus_writel(UCTRL_STAT_RXNE_STA, &driver->regs->uctrl_stat); \
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds 
uctrl_do_txn(struct uctrl_driver * driver,struct uctrl_txn * txn)262237f8aafSDavid S. Miller static void uctrl_do_txn(struct uctrl_driver *driver, struct uctrl_txn *txn)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds 	int stat, incnt, outcnt, bytecnt, intr;
2651da177e4SLinus Torvalds 	u32 byte;
2661da177e4SLinus Torvalds 
267237f8aafSDavid S. Miller 	stat = sbus_readl(&driver->regs->uctrl_stat);
268237f8aafSDavid S. Miller 	intr = sbus_readl(&driver->regs->uctrl_intr);
269237f8aafSDavid S. Miller 	sbus_writel(stat, &driver->regs->uctrl_stat);
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	dprintk(("interrupt stat 0x%x int 0x%x\n", stat, intr));
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	incnt = txn->inbits;
2741da177e4SLinus Torvalds 	outcnt = txn->outbits;
2751da177e4SLinus Torvalds 	byte = (txn->opcode << 8);
2761da177e4SLinus Torvalds 	WRITEUCTLDATA(byte);
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	bytecnt = 0;
2791da177e4SLinus Torvalds 	while (incnt > 0) {
2801da177e4SLinus Torvalds 		byte = (txn->inbuf[bytecnt] << 8);
2811da177e4SLinus Torvalds 		WRITEUCTLDATA(byte);
2821da177e4SLinus Torvalds 		incnt--;
2831da177e4SLinus Torvalds 		bytecnt++;
2841da177e4SLinus Torvalds 	}
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds 	/* Get the ack */
2871da177e4SLinus Torvalds 	READUCTLDATA(byte);
2881da177e4SLinus Torvalds 	dprintk(("ack was %x\n", (byte >> 8)));
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 	bytecnt = 0;
2911da177e4SLinus Torvalds 	while (outcnt > 0) {
2921da177e4SLinus Torvalds 		READUCTLDATA(byte);
2931da177e4SLinus Torvalds 		txn->outbuf[bytecnt] = (byte >> 8);
2941da177e4SLinus Torvalds 		dprintk(("set byte to %02x\n", byte));
2951da177e4SLinus Torvalds 		outcnt--;
2961da177e4SLinus Torvalds 		bytecnt++;
2971da177e4SLinus Torvalds 	}
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
uctrl_get_event_status(struct uctrl_driver * driver)300237f8aafSDavid S. Miller static void uctrl_get_event_status(struct uctrl_driver *driver)
3011da177e4SLinus Torvalds {
3021da177e4SLinus Torvalds 	struct uctrl_txn txn;
3031da177e4SLinus Torvalds 	u8 outbits[2];
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	txn.opcode = READ_EVENT_STATUS;
3061da177e4SLinus Torvalds 	txn.inbits = 0;
3071da177e4SLinus Torvalds 	txn.outbits = 2;
308fec607ffSAl Viro 	txn.inbuf = NULL;
3091da177e4SLinus Torvalds 	txn.outbuf = outbits;
3101da177e4SLinus Torvalds 
311237f8aafSDavid S. Miller 	uctrl_do_txn(driver, &txn);
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff)));
3141da177e4SLinus Torvalds 	driver->status.event_status =
3151da177e4SLinus Torvalds 		((outbits[0] & 0xff) << 8) | (outbits[1] & 0xff);
3161da177e4SLinus Torvalds 	dprintk(("ev is %x\n", driver->status.event_status));
3171da177e4SLinus Torvalds }
3181da177e4SLinus Torvalds 
uctrl_get_external_status(struct uctrl_driver * driver)319237f8aafSDavid S. Miller static void uctrl_get_external_status(struct uctrl_driver *driver)
3201da177e4SLinus Torvalds {
3211da177e4SLinus Torvalds 	struct uctrl_txn txn;
3221da177e4SLinus Torvalds 	u8 outbits[2];
3231da177e4SLinus Torvalds 	int i, v;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	txn.opcode = READ_EXTERNAL_STATUS;
3261da177e4SLinus Torvalds 	txn.inbits = 0;
3271da177e4SLinus Torvalds 	txn.outbits = 2;
328fec607ffSAl Viro 	txn.inbuf = NULL;
3291da177e4SLinus Torvalds 	txn.outbuf = outbits;
3301da177e4SLinus Torvalds 
331237f8aafSDavid S. Miller 	uctrl_do_txn(driver, &txn);
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 	dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff)));
3341da177e4SLinus Torvalds 	driver->status.external_status =
3351da177e4SLinus Torvalds 		((outbits[0] * 256) + (outbits[1]));
3361da177e4SLinus Torvalds 	dprintk(("ex is %x\n", driver->status.external_status));
3371da177e4SLinus Torvalds 	v = driver->status.external_status;
3381da177e4SLinus Torvalds 	for (i = 0; v != 0; i++, v >>= 1) {
3391da177e4SLinus Torvalds 		if (v & 1) {
3401da177e4SLinus Torvalds 			dprintk(("%s%s", " ", uctrl_extstatus[i]));
3411da177e4SLinus Torvalds 		}
3421da177e4SLinus Torvalds 	}
3431da177e4SLinus Torvalds 	dprintk(("\n"));
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds }
3461da177e4SLinus Torvalds 
uctrl_probe(struct platform_device * op)347082a2004SGreg Kroah-Hartman static int uctrl_probe(struct platform_device *op)
3481da177e4SLinus Torvalds {
349237f8aafSDavid S. Miller 	struct uctrl_driver *p;
350237f8aafSDavid S. Miller 	int err = -ENOMEM;
3511da177e4SLinus Torvalds 
352237f8aafSDavid S. Miller 	p = kzalloc(sizeof(*p), GFP_KERNEL);
353237f8aafSDavid S. Miller 	if (!p) {
354237f8aafSDavid S. Miller 		printk(KERN_ERR "uctrl: Unable to allocate device struct.\n");
355237f8aafSDavid S. Miller 		goto out;
356237f8aafSDavid S. Miller 	}
3571da177e4SLinus Torvalds 
358237f8aafSDavid S. Miller 	p->regs = of_ioremap(&op->resource[0], 0,
359237f8aafSDavid S. Miller 			     resource_size(&op->resource[0]),
360237f8aafSDavid S. Miller 			     "uctrl");
361237f8aafSDavid S. Miller 	if (!p->regs) {
362237f8aafSDavid S. Miller 		printk(KERN_ERR "uctrl: Unable to map registers.\n");
363237f8aafSDavid S. Miller 		goto out_free;
364237f8aafSDavid S. Miller 	}
3651da177e4SLinus Torvalds 
3661636f8acSGrant Likely 	p->irq = op->archdata.irqs[0];
367237f8aafSDavid S. Miller 	err = request_irq(p->irq, uctrl_interrupt, 0, "uctrl", p);
36819ba1b19SDavid S. Miller 	if (err) {
369237f8aafSDavid S. Miller 		printk(KERN_ERR "uctrl: Unable to register irq.\n");
370237f8aafSDavid S. Miller 		goto out_iounmap;
371237f8aafSDavid S. Miller 	}
372237f8aafSDavid S. Miller 
373237f8aafSDavid S. Miller 	err = misc_register(&uctrl_dev);
374237f8aafSDavid S. Miller 	if (err) {
375237f8aafSDavid S. Miller 		printk(KERN_ERR "uctrl: Unable to register misc device.\n");
376237f8aafSDavid S. Miller 		goto out_free_irq;
377237f8aafSDavid S. Miller 	}
378237f8aafSDavid S. Miller 
379237f8aafSDavid S. Miller 	sbus_writel(UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK, &p->regs->uctrl_intr);
3808cd3ec51SRob Herring 	printk(KERN_INFO "%pOF: uctrl regs[0x%p] (irq %d)\n",
3818cd3ec51SRob Herring 	       op->dev.of_node, p->regs, p->irq);
382237f8aafSDavid S. Miller 	uctrl_get_event_status(p);
383237f8aafSDavid S. Miller 	uctrl_get_external_status(p);
384237f8aafSDavid S. Miller 
385237f8aafSDavid S. Miller 	dev_set_drvdata(&op->dev, p);
386237f8aafSDavid S. Miller 	global_driver = p;
387237f8aafSDavid S. Miller 
388237f8aafSDavid S. Miller out:
38919ba1b19SDavid S. Miller 	return err;
390237f8aafSDavid S. Miller 
391237f8aafSDavid S. Miller out_free_irq:
392237f8aafSDavid S. Miller 	free_irq(p->irq, p);
393237f8aafSDavid S. Miller 
394237f8aafSDavid S. Miller out_iounmap:
395237f8aafSDavid S. Miller 	of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0]));
396237f8aafSDavid S. Miller 
397237f8aafSDavid S. Miller out_free:
398237f8aafSDavid S. Miller 	kfree(p);
399237f8aafSDavid S. Miller 	goto out;
40019ba1b19SDavid S. Miller }
4011da177e4SLinus Torvalds 
uctrl_remove(struct platform_device * op)402082a2004SGreg Kroah-Hartman static int uctrl_remove(struct platform_device *op)
403237f8aafSDavid S. Miller {
404237f8aafSDavid S. Miller 	struct uctrl_driver *p = dev_get_drvdata(&op->dev);
4051da177e4SLinus Torvalds 
406237f8aafSDavid S. Miller 	if (p) {
407237f8aafSDavid S. Miller 		misc_deregister(&uctrl_dev);
408237f8aafSDavid S. Miller 		free_irq(p->irq, p);
409237f8aafSDavid S. Miller 		of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0]));
410237f8aafSDavid S. Miller 		kfree(p);
411237f8aafSDavid S. Miller 	}
4121da177e4SLinus Torvalds 	return 0;
4131da177e4SLinus Torvalds }
4141da177e4SLinus Torvalds 
415fd098316SDavid S. Miller static const struct of_device_id uctrl_match[] = {
4161da177e4SLinus Torvalds 	{
417237f8aafSDavid S. Miller 		.name = "uctrl",
418237f8aafSDavid S. Miller 	},
419237f8aafSDavid S. Miller 	{},
420237f8aafSDavid S. Miller };
421237f8aafSDavid S. Miller MODULE_DEVICE_TABLE(of, uctrl_match);
4221da177e4SLinus Torvalds 
4234ebb24f7SGrant Likely static struct platform_driver uctrl_driver = {
4244018294bSGrant Likely 	.driver = {
425237f8aafSDavid S. Miller 		.name = "uctrl",
4264018294bSGrant Likely 		.of_match_table = uctrl_match,
4274018294bSGrant Likely 	},
428237f8aafSDavid S. Miller 	.probe		= uctrl_probe,
429082a2004SGreg Kroah-Hartman 	.remove		= uctrl_remove,
430237f8aafSDavid S. Miller };
431237f8aafSDavid S. Miller 
432237f8aafSDavid S. Miller 
433dbf2b92dSAxel Lin module_platform_driver(uctrl_driver);
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds MODULE_LICENSE("GPL");
436