> 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@xxxxxxxxxxxxxxxx> > Cc: Dmitry Eremin-Solenikov <dbaryshkov@xxxxxxxxx> > Signed-off-by: Alessandro Gardich <gremlin@xxxxxxxxxx> > Signed-off-by: Dmitry Artamonow <mad_soft@xxxxxxxx> > Signed-off-by: Linus Walleij <linus.walleij@xxxxxxxxxx> > --- > drivers/mfd/Kconfig | 10 + > drivers/mfd/Makefile | 1 + > drivers/mfd/ipaq-micro.c | 488 +++++++++++++++++++++++++++++++++++++++++ > include/linux/mfd/ipaq-micro.h | 149 +++++++++++++ > 4 files changed, 648 insertions(+) > create mode 100644 drivers/mfd/ipaq-micro.c > create mode 100644 include/linux/mfd/ipaq-micro.h <snip> > +++ b/drivers/mfd/ipaq-micro.c > @@ -0,0 +1,488 @@ > +/* > + * 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. Can you put this at the bottom of the header? > + * 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@xxxxxxxxxx> > + * Author : Dmitry Artamonow <mad_soft@xxxxxxxx> > + * Author : Linus Walleij <linus.walleij@xxxxxxxxxx> > + */ > + > +#include <linux/module.h> Does it matter that you're using: module_platform_driver(); ... yet you can't build this as a module? <snip> > +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, j; > + u8 checksum; > + u32 val; > + > + j = 0; Tiny nit: The naming convention of 'j' could be more indicative of its function. > + tx->buf[j++] = (u8) CHAR_SOF; Is this cast required? > + checksum = ((msg->id & 0x0f) << 4) | (msg->tx_len & 0x0f); > + tx->buf[j++] = checksum; > + > + for (i = 0; i < msg->tx_len; i++) { > + tx->buf[j++] = msg->tx_data[i]; > + checksum += msg->tx_data[i]; > + } > + > + tx->buf[j++] = checksum; > + tx->len = j; > + tx->index = 0; > + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1, > + tx->buf, tx->len, true); > + > + Too many new lines. > + val = readl(micro->base + UTCR3); > + val |= UTCR3_TIE; /* enable interrupt */ > + writel(val, micro->base + UTCR3); The comment should encompass all three lines of code. > +} > + > + Too many new lines. > +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 != NULL) { if (!micro->msg) is preferred. > + 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; > + <snip> > + case MSG_KEYBOARD: > + if (micro->key != NULL) !micro->key > + micro->key(micro->key_data, len, data); > + else > + dev_dbg(micro->dev, "ipaq micro : key message ignored, " > + "no handle \n"); Log print spread over two lines. Doesn't Checkpatch complain about this? > + break; > + case MSG_TOUCHSCREEN: > + if (micro->ts != NULL) !micro->ts ... etc etc > + micro->ts(micro->ts_data, len, data); > + else > + dev_dbg(micro->dev, "ipaq micro : touchscreen message " > + "ignored, no handle \n"); Print spill. > +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]; It's common practice to space out the [i*2] for readability. <snip> > +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); This is pretty messy. Better broken out? Your call. <snip> > + /* Stop interrupts */ > + val = readl(micro->base + UTCR3); > + val &= ~UTCR3_TIE; > + writel(val, micro->base + UTCR3); Ah, so it's correct here. Please see my comment surrounding "start interrupts" above. > +} > + > +static void micro_reset_comm(struct ipaq_micro *micro) > +{ > + struct ipaq_micro_rxdev *rx = µ->rx; > + u32 val; New line here please. > + 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); Any reason for squishing these together? > + /* Clear SR0 */ > + writel(0xff, micro->base + UTSR0); > + /* Enable RX int */ > + writel(UTCR3_TXE | UTCR3_RXE | UTCR3_RIE, micro->base + UTCR3); > + val = readl(micro->base + UTCR3); > + val &= ~UTCR3_TIE; /* Disable TX int */ > + writel(val, micro->base + UTCR3); > +} <snip> > +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", > + }, > +}; Is Device Tree ever going to be supported on the iPAC? If not, I think it's okay for you to make these single line entries. > +static int micro_suspend(struct device *dev) > +{ > + return 0; > +} Doesn't the PM framework check for NULL .suspend? > +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; Can you space all of these out a bit? > + ret = devm_request_irq(&pdev->dev, irq, micro_serial_isr, > + IRQF_SHARED, "ipaq-micro", > + micro); > + if (ret) { > + dev_err(&pdev->dev, "%s: unable to grab serial port IRQ\n", > + __func__); Any need to print the function name if you know the name of the device? > + return ret; > + } else > + dev_info(&pdev->dev, "grabbed serial port IRQ\n"); > + > + Too many new lines. > + 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; > + } New line here. > + /* 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; Break these up please. > +} > + > +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); It may seem silly for these piece of h/w, but don't we have a 'no new device drivers without DT support rule'? > +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..9466e97ff236 > --- /dev/null > +++ b/include/linux/mfd/ipaq-micro.h > @@ -0,0 +1,149 @@ > +/* > + * 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> > +struct device; What's this for? <snip> > +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); > +} Why are these in here? Where else are they called from? -- Lee Jones Linaro STMicroelectronics Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html