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