diff options
Diffstat (limited to 'recipes-kernel/linux/linux-yocto-dev/h3600/0001-MFD-driver-for-Atmel-Microcontroller-on-iPaq-h3xxx.patch')
-rw-r--r-- | recipes-kernel/linux/linux-yocto-dev/h3600/0001-MFD-driver-for-Atmel-Microcontroller-on-iPaq-h3xxx.patch | 712 |
1 files changed, 712 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-yocto-dev/h3600/0001-MFD-driver-for-Atmel-Microcontroller-on-iPaq-h3xxx.patch b/recipes-kernel/linux/linux-yocto-dev/h3600/0001-MFD-driver-for-Atmel-Microcontroller-on-iPaq-h3xxx.patch new file mode 100644 index 0000000..2470cdf --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-dev/h3600/0001-MFD-driver-for-Atmel-Microcontroller-on-iPaq-h3xxx.patch @@ -0,0 +1,712 @@ +From a2073bd47b86b1ad3ba0600e772cd0f14ba73838 Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Fri, 31 Jan 2014 14:34:35 +0100 +Subject: [PATCH 1/7] MFD: driver for Atmel Microcontroller on iPaq h3xxx + +This adds a driver for the Atmel Microcontroller found on the +iPAQ h3xxx series. This device handles some keys, the +touchscreen, and the battery monitoring. + +This is a port of a driver from handhelds.org 2.6.21 kernel, +written by Alessandro Gardich based on Andrew Christians +original HAL-driver. It has been heavily cleaned and +converted to mfd-core by Dmitry Artamonow and rewritten +again for the v3.x series kernels by Linus Walleij, +bringing back some of the functionality lost from Andrew's +original driver. + +Cc: Russell King <linux@arm.linux.org.uk> +Cc: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> +Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Alessandro Gardich <gremlin@gremlin.it> +Signed-off-by: Dmitry Artamonow <mad_soft@inbox.ru> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +--- + drivers/mfd/Kconfig | 10 + + drivers/mfd/Makefile | 1 + + drivers/mfd/ipaq-micro.c | 487 +++++++++++++++++++++++++++++++++++++++++ + include/linux/mfd/ipaq-micro.h | 148 +++++++++++++ + 4 files changed, 646 insertions(+) + create mode 100644 drivers/mfd/ipaq-micro.c + create mode 100644 include/linux/mfd/ipaq-micro.h + +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 49bb445d846a..255807afc582 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -242,6 +242,16 @@ config MFD_INTEL_MSIC + Passage) chip. This chip embeds audio, battery, GPIO, etc. + devices used in Intel Medfield platforms. + ++config MFD_IPAQ_MICRO ++ bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" ++ depends on SA1100_H3100 || SA1100_H3600 ++ select MFD_CORE ++ help ++ Select this to get support for the Microcontroller found in ++ the Compaq iPAQ handheld computers. This is an Atmel ++ AT90LS8535 microcontroller flashed with a special iPAQ ++ firmware using the custom protocol implemented in this driver. ++ + config MFD_JANZ_CMODIO + tristate "Janz CMOD-IO PCI MODULbus Carrier Board" + select MFD_CORE +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 5aea5ef0a62f..cf8081fcb3ac 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -166,3 +166,4 @@ obj-$(CONFIG_MFD_RETU) += retu-mfd.o + obj-$(CONFIG_MFD_AS3711) += as3711.o + obj-$(CONFIG_MFD_AS3722) += as3722.o + obj-$(CONFIG_MFD_STW481X) += stw481x.o ++obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o +diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c +new file mode 100644 +index 000000000000..f71dffdd3f0f +--- /dev/null ++++ b/drivers/mfd/ipaq-micro.c +@@ -0,0 +1,487 @@ ++/* ++ * Compaq iPAQ h3xxx Atmel microcontroller companion support ++ * ++ * This is an Atmel AT90LS8535 with a special flashed-in firmware that ++ * implements the special protocol used by this driver. ++ * ++ * based on previous kernel 2.4 version by Andrew Christian ++ * Author : Alessandro Gardich <gremlin@gremlin.it> ++ * Author : Dmitry Artamonow <mad_soft@inbox.ru> ++ * Author : Linus Walleij <linus.walleij@linaro.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/pm.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/io.h> ++#include <linux/mfd/core.h> ++#include <linux/mfd/ipaq-micro.h> ++#include <linux/string.h> ++#include <linux/random.h> ++#include <linux/slab.h> ++#include <linux/list.h> ++ ++#include <mach/hardware.h> ++ ++static void ipaq_micro_trigger_tx(struct ipaq_micro *micro) ++{ ++ struct ipaq_micro_txdev *tx = µ->tx; ++ struct ipaq_micro_msg *msg = micro->msg; ++ int i, bp; ++ u8 checksum; ++ u32 val; ++ ++ bp = 0; ++ tx->buf[bp++] = CHAR_SOF; ++ ++ checksum = ((msg->id & 0x0f) << 4) | (msg->tx_len & 0x0f); ++ tx->buf[bp++] = checksum; ++ ++ for (i = 0; i < msg->tx_len; i++) { ++ tx->buf[bp++] = msg->tx_data[i]; ++ checksum += msg->tx_data[i]; ++ } ++ ++ tx->buf[bp++] = checksum; ++ tx->len = bp; ++ tx->index = 0; ++ print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1, ++ tx->buf, tx->len, true); ++ ++ /* Enable interrupt */ ++ val = readl(micro->base + UTCR3); ++ val |= UTCR3_TIE; ++ writel(val, micro->base + UTCR3); ++} ++ ++int ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg) ++{ ++ unsigned long flags; ++ ++ dev_dbg(micro->dev, "TX msg: %02x, %d bytes\n", msg->id, msg->tx_len); ++ ++ spin_lock_irqsave(µ->lock, flags); ++ if (micro->msg) { ++ list_add_tail(&msg->node, µ->queue); ++ spin_unlock_irqrestore(µ->lock, flags); ++ return 0; ++ } ++ micro->msg = msg; ++ ipaq_micro_trigger_tx(micro); ++ spin_unlock_irqrestore(µ->lock, flags); ++ return 0; ++} ++EXPORT_SYMBOL(ipaq_micro_tx_msg); ++ ++static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data) ++{ ++ int i; ++ ++ dev_dbg(micro->dev, "RX msg: %02x, %d bytes\n", id, len); ++ ++ spin_lock(µ->lock); ++ switch (id) { ++ case MSG_VERSION: ++ case MSG_EEPROM_READ: ++ case MSG_EEPROM_WRITE: ++ case MSG_BACKLIGHT: ++ case MSG_NOTIFY_LED: ++ case MSG_THERMAL_SENSOR: ++ case MSG_BATTERY: ++ /* Handle synchronous messages */ ++ if (micro->msg && micro->msg->id == id) { ++ struct ipaq_micro_msg *msg = micro->msg; ++ ++ memcpy(msg->rx_data, data, len); ++ msg->rx_len = len; ++ complete(µ->msg->ack); ++ if (!list_empty(µ->queue)) { ++ micro->msg = list_entry(micro->queue.next, ++ struct ipaq_micro_msg, ++ node); ++ list_del_init(µ->msg->node); ++ ipaq_micro_trigger_tx(micro); ++ } else ++ micro->msg = NULL; ++ dev_dbg(micro->dev, "OK RX message 0x%02x\n", id); ++ } else { ++ dev_err(micro->dev, ++ "out of band RX message 0x%02x\n", id); ++ if(!micro->msg) ++ dev_info(micro->dev, "no message queued\n"); ++ else ++ dev_info(micro->dev, "expected message %02x\n", ++ micro->msg->id); ++ } ++ break; ++ case MSG_KEYBOARD: ++ if (micro->key) ++ micro->key(micro->key_data, len, data); ++ else ++ dev_dbg(micro->dev, "key message ignored, no handle \n"); ++ break; ++ case MSG_TOUCHSCREEN: ++ if (micro->ts) ++ micro->ts(micro->ts_data, len, data); ++ else ++ dev_dbg(micro->dev, "touchscreen message ignored, no handle \n"); ++ break; ++ default: ++ dev_err(micro->dev, ++ "unknown msg %d [%d] ", id, len); ++ for (i = 0; i < len; ++i) ++ pr_cont("0x%02x ", data[i]); ++ pr_cont("\n"); ++ } ++ spin_unlock(µ->lock); ++} ++ ++static void micro_process_char(struct ipaq_micro *micro, u8 ch) ++{ ++ struct ipaq_micro_rxdev *rx = µ->rx; ++ ++ switch (rx->state) { ++ case STATE_SOF: /* Looking for SOF */ ++ if (ch == CHAR_SOF) ++ rx->state = STATE_ID; /* Next byte is the id and len */ ++ break; ++ case STATE_ID: /* Looking for id and len byte */ ++ rx->id = (ch & 0xf0) >> 4 ; ++ rx->len = (ch & 0x0f); ++ rx->index = 0; ++ rx->chksum = ch; ++ rx->state = (rx->len > 0) ? STATE_DATA : STATE_CHKSUM; ++ break; ++ case STATE_DATA: /* Looking for 'len' data bytes */ ++ rx->chksum += ch; ++ rx->buf[rx->index] = ch; ++ if (++rx->index == rx->len) ++ rx->state = STATE_CHKSUM; ++ break; ++ case STATE_CHKSUM: /* Looking for the checksum */ ++ if (ch == rx->chksum) ++ micro_rx_msg(micro, rx->id, rx->len, rx->buf); ++ rx->state = STATE_SOF; ++ break; ++ } ++} ++ ++static void micro_rx_chars(struct ipaq_micro *micro) ++{ ++ u32 status, ch; ++ ++ while ((status = readl(micro->base + UTSR1)) & UTSR1_RNE) { ++ ch = readl(micro->base + UTDR); ++ if (status & UTSR1_PRE) ++ dev_err(micro->dev, "rx: parity error\n"); ++ else if (status & UTSR1_FRE) ++ dev_err(micro->dev, "rx: framing error\n"); ++ else if (status & UTSR1_ROR) ++ dev_err(micro->dev, "rx: overrun error\n"); ++ micro_process_char(micro, ch); ++ } ++} ++ ++static void ipaq_micro_get_version(struct ipaq_micro *micro) ++{ ++ struct ipaq_micro_msg msg = { ++ .id = MSG_VERSION, ++ }; ++ ++ ipaq_micro_tx_msg_sync(micro, &msg); ++ if (msg.rx_len == 4) { ++ memcpy(micro->version, msg.rx_data, 4); ++ micro->version[4] = '\0'; ++ } else if (msg.rx_len == 9) { ++ memcpy(micro->version, msg.rx_data, 4); ++ micro->version[4] = '\0'; ++ /* Bytes 4-7 are "pack", byte 8 is "boot type" */ ++ } else { ++ dev_err(micro->dev, ++ "illegal version message %d bytes\n", msg.rx_len); ++ } ++} ++ ++static void ipaq_micro_eeprom_read(struct ipaq_micro *micro, ++ u8 address, u8 len, u8 *data) ++{ ++ struct ipaq_micro_msg msg = { ++ .id = MSG_EEPROM_READ, ++ }; ++ u8 i; ++ ++ for (i = 0; i < len; i++) { ++ msg.tx_data[0] = address + i; ++ msg.tx_data[1] = 1; ++ msg.tx_len = 2; ++ ipaq_micro_tx_msg_sync(micro, &msg); ++ memcpy(data + (i * 2), msg.rx_data, 2); ++ } ++} ++ ++static char *ipaq_micro_str(u8 *wchar, u8 len) ++{ ++ char retstr[256]; ++ u8 i; ++ ++ for (i = 0; i < len / 2; i++) ++ retstr[i] = wchar[i * 2]; ++ return kstrdup(retstr, GFP_KERNEL); ++} ++ ++static u16 ipaq_micro_to_u16(u8 *data) ++{ ++ return data[1] << 8 | data[0]; ++} ++ ++static void ipaq_micro_eeprom_dump(struct ipaq_micro *micro) ++{ ++ u8 dump[256]; ++ char *str; ++ ++ ipaq_micro_eeprom_read(micro, 0, 128, dump); ++ str = ipaq_micro_str(dump, 10); ++ if (str) { ++ dev_info(micro->dev, "HM version %s\n", str); ++ kfree(str); ++ } ++ str = ipaq_micro_str(dump+10, 40); ++ if (str) { ++ dev_info(micro->dev, "serial number: %s\n", str); ++ /* Feed the random pool with this */ ++ add_device_randomness(str, strlen(str)); ++ kfree(str); ++ } ++ str = ipaq_micro_str(dump+50, 20); ++ if (str) { ++ dev_info(micro->dev, "module ID: %s\n", str); ++ kfree(str); ++ } ++ str = ipaq_micro_str(dump+70, 10); ++ if (str) { ++ dev_info(micro->dev, "product revision: %s\n", str); ++ kfree(str); ++ } ++ dev_info(micro->dev, "product ID: %u\n", ipaq_micro_to_u16(dump+80)); ++ dev_info(micro->dev, "frame rate: %u fps\n", ++ ipaq_micro_to_u16(dump+82)); ++ dev_info(micro->dev, "page mode: %u\n", ipaq_micro_to_u16(dump+84)); ++ dev_info(micro->dev, "country ID: %u\n", ipaq_micro_to_u16(dump+86)); ++ dev_info(micro->dev, "color display: %s\n", ++ ipaq_micro_to_u16(dump+88) ? "yes" : "no"); ++ dev_info(micro->dev, "ROM size: %u MiB\n", ipaq_micro_to_u16(dump+90)); ++ dev_info(micro->dev, "RAM size: %u KiB\n", ipaq_micro_to_u16(dump+92)); ++ dev_info(micro->dev, "screen: %u x %u\n", ++ ipaq_micro_to_u16(dump+94), ipaq_micro_to_u16(dump+96)); ++ print_hex_dump(KERN_DEBUG, "eeprom: ", DUMP_PREFIX_OFFSET, 16, 1, ++ dump, 256, true); ++ ++} ++ ++static void micro_tx_chars(struct ipaq_micro *micro) ++{ ++ struct ipaq_micro_txdev *tx = µ->tx; ++ u32 val; ++ ++ while ((tx->index < tx->len) && ++ (readl(micro->base + UTSR1) & UTSR1_TNF)) { ++ writel(tx->buf[tx->index], micro->base + UTDR); ++ tx->index++; ++ } ++ ++ /* Stop interrupts */ ++ val = readl(micro->base + UTCR3); ++ val &= ~UTCR3_TIE; ++ writel(val, micro->base + UTCR3); ++} ++ ++static void micro_reset_comm(struct ipaq_micro *micro) ++{ ++ struct ipaq_micro_rxdev *rx = µ->rx; ++ u32 val; ++ ++ if (micro->msg) ++ complete(µ->msg->ack); ++ ++ /* Initialize Serial channel protocol frame */ ++ rx->state = STATE_SOF; /* Reset the state machine */ ++ ++ /* Set up interrupts */ ++ writel(0x01, micro->sdlc + 0x0); /* Select UART mode */ ++ ++ /* Clean up CR3 */ ++ writel(0x0, micro->base + UTCR3); ++ ++ /* Format: 8N1 */ ++ writel(UTCR0_8BitData | UTCR0_1StpBit, micro->base + UTCR0); ++ ++ /* Baud rate: 115200 */ ++ writel(0x0, micro->base + UTCR1); ++ writel(0x1, micro->base + UTCR2); ++ ++ /* Clear SR0 */ ++ writel(0xff, micro->base + UTSR0); ++ ++ /* Enable RX int, disable TX int */ ++ writel(UTCR3_TXE | UTCR3_RXE | UTCR3_RIE, micro->base + UTCR3); ++ val = readl(micro->base + UTCR3); ++ val &= ~UTCR3_TIE; ++ writel(val, micro->base + UTCR3); ++} ++ ++static irqreturn_t micro_serial_isr(int irq, void *dev_id) ++{ ++ struct ipaq_micro *micro = dev_id; ++ struct ipaq_micro_txdev *tx = µ->tx; ++ u32 status; ++ ++ status = readl(micro->base + UTSR0); ++ do { ++ if (status & (UTSR0_RID | UTSR0_RFS)) { ++ if (status & UTSR0_RID) ++ /* Clear the Receiver IDLE bit */ ++ writel(UTSR0_RID, micro->base + UTSR0); ++ micro_rx_chars(micro); ++ } ++ ++ /* Clear break bits */ ++ if (status & (UTSR0_RBB | UTSR0_REB)) ++ writel(status & (UTSR0_RBB | UTSR0_REB), ++ micro->base + UTSR0); ++ ++ if (status & UTSR0_TFS) ++ micro_tx_chars(micro); ++ ++ status = readl(micro->base + UTSR0); ++ ++ } while (((tx->index < tx->len) && (status & UTSR0_TFS)) || ++ (status & (UTSR0_RFS | UTSR0_RID))); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct mfd_cell micro_cells[] = { ++ { .name = "ipaq-micro-backlight", }, ++ { .name = "ipaq-micro-battery", }, ++ { .name = "ipaq-micro-keys", }, ++ { .name = "ipaq-micro-ts", }, ++ { .name = "ipaq-micro-leds", }, ++}; ++ ++static int micro_suspend(struct device *dev) ++{ ++ return 0; ++} ++ ++static int micro_resume(struct device *dev) ++{ ++ struct ipaq_micro *micro = dev_get_drvdata(dev); ++ ++ micro_reset_comm(micro); ++ mdelay(10); ++ ++ return 0; ++} ++ ++static int micro_probe(struct platform_device *pdev) ++{ ++ struct ipaq_micro *micro; ++ struct resource *res; ++ int ret; ++ int irq; ++ ++ micro = devm_kzalloc(&pdev->dev, sizeof(*micro), GFP_KERNEL); ++ if (!micro) ++ return -ENOMEM; ++ ++ micro->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -EINVAL; ++ ++ micro->base = devm_request_and_ioremap(&pdev->dev, res); ++ if (!micro->base) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!res) ++ return -EINVAL; ++ ++ micro->sdlc = devm_request_and_ioremap(&pdev->dev, res); ++ if (!micro->sdlc) ++ return -ENOMEM; ++ ++ micro_reset_comm(micro); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (!irq) ++ return -EINVAL; ++ ret = devm_request_irq(&pdev->dev, irq, micro_serial_isr, ++ IRQF_SHARED, "ipaq-micro", ++ micro); ++ if (ret) { ++ dev_err(&pdev->dev, "unable to grab serial port IRQ\n"); ++ return ret; ++ } else ++ dev_info(&pdev->dev, "grabbed serial port IRQ\n"); ++ ++ spin_lock_init(µ->lock); ++ INIT_LIST_HEAD(µ->queue); ++ platform_set_drvdata(pdev, micro); ++ ++ ret = mfd_add_devices(&pdev->dev, pdev->id, micro_cells, ++ ARRAY_SIZE(micro_cells), NULL, 0, NULL); ++ if (ret) { ++ dev_err(&pdev->dev, "error adding MFD cells"); ++ return ret; ++ } ++ ++ /* Check version */ ++ ipaq_micro_get_version(micro); ++ dev_info(&pdev->dev, "Atmel micro ASIC version %s\n", micro->version); ++ ipaq_micro_eeprom_dump(micro); ++ ++ return 0; ++} ++ ++static int micro_remove(struct platform_device *pdev) ++{ ++ struct ipaq_micro *micro = platform_get_drvdata(pdev); ++ u32 val; ++ ++ mfd_remove_devices(&pdev->dev); ++ ++ val = readl(micro->base + UTCR3); ++ val &= ~(UTCR3_RXE | UTCR3_RIE); /* disable receive interrupt */ ++ val &= ~(UTCR3_TXE | UTCR3_TIE); /* disable transmit interrupt */ ++ writel(val, micro->base + UTCR3); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops micro_dev_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(micro_suspend, micro_resume) ++}; ++ ++static struct platform_driver micro_device_driver = { ++ .driver = { ++ .name = "ipaq-h3xxx-micro", ++ .pm = µ_dev_pm_ops, ++ }, ++ .probe = micro_probe, ++ .remove = micro_remove, ++ /* .shutdown = micro_suspend, // FIXME */ ++}; ++module_platform_driver(micro_device_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("driver for iPAQ Atmel micro core and backlight"); +diff --git a/include/linux/mfd/ipaq-micro.h b/include/linux/mfd/ipaq-micro.h +new file mode 100644 +index 000000000000..5c4d29f6674f +--- /dev/null ++++ b/include/linux/mfd/ipaq-micro.h +@@ -0,0 +1,148 @@ ++/* ++ * Header file for the compaq Micro MFD ++ */ ++ ++#ifndef _MFD_IPAQ_MICRO_H_ ++#define _MFD_IPAQ_MICRO_H_ ++ ++#include <linux/spinlock.h> ++#include <linux/completion.h> ++#include <linux/list.h> ++ ++#define TX_BUF_SIZE 32 ++#define RX_BUF_SIZE 16 ++#define CHAR_SOF 0x02 ++ ++/* ++ * These are the different messages that can be sent to the microcontroller ++ * to control various aspects. ++ */ ++#define MSG_VERSION 0x0 ++#define MSG_KEYBOARD 0x2 ++#define MSG_TOUCHSCREEN 0x3 ++#define MSG_EEPROM_READ 0x4 ++#define MSG_EEPROM_WRITE 0x5 ++#define MSG_THERMAL_SENSOR 0x6 ++#define MSG_NOTIFY_LED 0x8 ++#define MSG_BATTERY 0x9 ++#define MSG_SPI_READ 0xb ++#define MSG_SPI_WRITE 0xc ++#define MSG_BACKLIGHT 0xd /* H3600 only */ ++#define MSG_CODEC_CTRL 0xe /* H3100 only */ ++#define MSG_DISPLAY_CTRL 0xf /* H3100 only */ ++ ++/* state of receiver parser */ ++enum rx_state { ++ STATE_SOF = 0, /* Next byte should be start of frame */ ++ STATE_ID, /* Next byte is ID & message length */ ++ STATE_DATA, /* Next byte is a data byte */ ++ STATE_CHKSUM /* Next byte should be checksum */ ++}; ++ ++/** ++ * struct ipaq_micro_txdev - TX state ++ * @len: length of message in TX buffer ++ * @index: current index into TX buffer ++ * @buf: TX buffer ++ */ ++struct ipaq_micro_txdev { ++ u8 len; ++ u8 index; ++ u8 buf[TX_BUF_SIZE]; ++}; ++ ++/** ++ * struct ipaq_micro_rxdev - RX state ++ * @state: context of RX state machine ++ * @chksum: calculated checksum ++ * @id: message ID from packet ++ * @len: RX buffer length ++ * @index: RX buffer index ++ * @buf: RX buffer ++ */ ++struct ipaq_micro_rxdev { ++ enum rx_state state; ++ unsigned char chksum; ++ u8 id; ++ unsigned int len; ++ unsigned int index; ++ u8 buf[RX_BUF_SIZE]; ++}; ++ ++/** ++ * struct ipaq_micro_msg - message to the iPAQ microcontroller ++ * @id: 4-bit ID of the message ++ * @tx_len: length of TX data ++ * @tx_data: TX data to send ++ * @rx_len: length of receieved RX data ++ * @rx_data: RX data to recieve ++ * @ack: a completion that will be completed when RX is complete ++ * @node: list node if message gets queued ++ */ ++struct ipaq_micro_msg { ++ u8 id; ++ u8 tx_len; ++ u8 tx_data[TX_BUF_SIZE]; ++ u8 rx_len; ++ u8 rx_data[RX_BUF_SIZE]; ++ struct completion ack; ++ struct list_head node; ++}; ++ ++/** ++ * struct ipaq_micro - iPAQ microcontroller state ++ * @dev: corresponding platform device ++ * @base: virtual memory base for underlying serial device ++ * @sdlc: virtual memory base for Synchronous Data Link Controller ++ * @version: version string ++ * @tx: TX state ++ * @rx: RX state ++ * @lock: lock for this state container ++ * @msg: current message ++ * @queue: message queue ++ * @key: callback for asynchronous key events ++ * @key_data: data to pass along with key events ++ * @ts: callback for asynchronous touchscreen events ++ * @ts_data: data to pass along with key events ++ */ ++struct ipaq_micro { ++ struct device *dev; ++ void __iomem *base; ++ void __iomem *sdlc; ++ char version[5]; ++ struct ipaq_micro_txdev tx; /* transmit ISR state */ ++ struct ipaq_micro_rxdev rx; /* receive ISR state */ ++ spinlock_t lock; ++ struct ipaq_micro_msg *msg; ++ struct list_head queue; ++ void (*key) (void *data, int len, unsigned char *rxdata); ++ void *key_data; ++ void (*ts) (void *data, int len, unsigned char *rxdata); ++ void *ts_data; ++}; ++ ++extern int ++ipaq_micro_tx_msg(struct ipaq_micro *micro, struct ipaq_micro_msg *msg); ++ ++static inline int ++ipaq_micro_tx_msg_sync(struct ipaq_micro *micro, ++ struct ipaq_micro_msg *msg) ++{ ++ int ret; ++ ++ init_completion(&msg->ack); ++ ret = ipaq_micro_tx_msg(micro, msg); ++ wait_for_completion(&msg->ack); ++ ++ return ret; ++} ++ ++static inline int ++ipaq_micro_tx_msg_async(struct ipaq_micro *micro, ++ struct ipaq_micro_msg *msg) ++{ ++ init_completion(&msg->ack); ++ return ipaq_micro_tx_msg(micro, msg); ++} ++ ++#endif /* _MFD_IPAQ_MICRO_H_ */ +-- +1.9.0 + |