diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index d30de89..a2dacee 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -268,6 +268,8 @@ static int __init atstk1002_init(void) atstk1000_setup_j2_leds(); atstk1002_setup_extdac(); + at32_add_device_psif(0); + at32_add_device_psif(1); return 0; } --- a/arch/avr32/boards/atngw100/setup.c 2008-01-31 13:38:32.000000000 -0500 +++ b/arch/avr32/boards/atngw100/setup.c 2008-01-31 13:44:09.000000000 -0500 @@ -224,6 +224,9 @@ static int __init atngw100_init(void) at32_add_device_usba(0, NULL); at32_add_device_ac97c(0); + at32_add_device_psif(0); + at32_add_device_psif(1); + for (i = 0; i < ARRAY_SIZE(ngw_leds); i++) { at32_select_gpio(ngw_leds[i].gpio, AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 5e3fae0..4ee9c51 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -678,6 +678,55 @@ void __init at32_add_system_devices(void) } /* -------------------------------------------------------------------- + * PSIF + * -------------------------------------------------------------------- */ +static struct resource atmel_psif0_resource[] = { + { + .start = 0xffe03c00, + .end = 0xffe03cff, + .flags = IORESOURCE_MEM, + }, + IRQ(18), +}; +DEFINE_DEV(atmel_psif, 0); +DEV_CLK(pclk, atmel_psif0, pba, 15); + +static struct resource atmel_psif1_resource[] = { + { + .start = 0xffe03d00, + .end = 0xffe03dff, + .flags = IORESOURCE_MEM, + }, + IRQ(18), +}; +DEFINE_DEV(atmel_psif, 1); +DEV_CLK(pclk, atmel_psif1, pba, 15); + +struct platform_device *__init +at32_add_device_psif(unsigned int id) +{ + struct platform_device *pdev; + + switch (id) { + case 0: + pdev = &atmel_psif0_device; + select_peripheral(PA(8), PERIPH_A, 0); /* CLOCK */ + select_peripheral(PA(9), PERIPH_A, 0); /* DATA */ + break; + case 1: + pdev = &atmel_psif1_device; + select_peripheral(PB(11), PERIPH_A, 0); /* CLOCK */ + select_peripheral(PB(12), PERIPH_A, 0); /* DATA */ + break; + default: + return NULL; + } + + platform_device_register(pdev); + return pdev; +} + +/* -------------------------------------------------------------------- * USART * -------------------------------------------------------------------- */ @@ -1653,6 +1702,8 @@ struct clk *at32_clock_list[] = { &pio3_mck, &pio4_mck, &at32_systc0_pclk, + &atmel_psif0_pclk, + &atmel_psif1_pclk, &atmel_usart0_usart, &atmel_usart1_usart, &atmel_usart2_usart, diff --git a/include/asm-avr32/arch-at32ap/board.h b/include/asm-avr32/arch-at32ap/board.h index 7aa1c29..0f31573 100644 --- a/include/asm-avr32/arch-at32ap/board.h +++ b/include/asm-avr32/arch-at32ap/board.h @@ -89,4 +89,7 @@ struct platform_device * at32_add_device_cf(unsigned int id, unsigned int extint, struct cf_platform_data *data); +struct platform_device * +at32_add_device_psif(unsigned int id); + #endif /* __ASM_ARCH_BOARD_H */ diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index d95f316..20a7193 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -1000,7 +1000,8 @@ DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); #if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(CONFIG_ALPHA) ||\ defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) ||\ defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\ - (defined(CONFIG_ARM) && defined(CONFIG_KEYBOARD_ATKBD) && !defined(CONFIG_ARCH_RPC)) + (defined(CONFIG_ARM) && defined(CONFIG_KEYBOARD_ATKBD) && !defined(CONFIG_ARCH_RPC)) ||\ + defined(CONFIG_AVR32) #define HW_RAW(dev) (test_bit(EV_MSC, dev->evbit) && test_bit(MSC_RAW, dev->mscbit) &&\ ((dev)->id.bustype == BUS_I8042) && ((dev)->id.vendor == 0x0001) && ((dev)->id.product == 0x0001)) diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 5ce632c..40731ff 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -88,6 +88,17 @@ config SERIO_RPCKBD To compile this driver as a module, choose M here: the module will be called rpckbd. +config SERIO_AT32PSIF + tristate "AVR32 PSIF PS/2 keyboard and mouse controller" + depends on AVR32 + default n + help + Say Y here if you want to use the PSIF peripheral on AVR32 devices + and connect a PS/2 keyboard and/or mouse to it. + + To compile this driver as a module, choose M here: the module will + be called at32psif. + config SERIO_AMBAKMI tristate "AMBA KMI keyboard controller" depends on ARM_AMBA diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 4155197..38b8868 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o +obj-$(CONFIG_SERIO_AT32PSIF) += at32psif.o obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o obj-$(CONFIG_HP_SDC) += hp_sdc.o diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c new file mode 100644 index 0000000..bc7e2cb --- /dev/null +++ b/drivers/input/serio/at32psif.c @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * Driver for the AT32AP700X PS/2 controller (PSIF). + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "at32psif.h" + +struct psif { + struct platform_device *pdev; + struct clk *pclk; + struct serio *io; + void __iomem *regs; + unsigned int irq; + spinlock_t lock; + unsigned int head; + unsigned int tail; + unsigned char buffer[8]; +}; + +static irqreturn_t psif_interrupt(int irq, void *_ptr) +{ + struct psif *psif = _ptr; + int retval = IRQ_NONE; + unsigned int flags = 0; + unsigned long status; + + status = psif_readl(psif, SR); + + if (status & PSIF_BIT(SR_RXRDY)) { + if (status & PSIF_BIT(SR_PARITY)) + flags |= SERIO_PARITY; + if (status & PSIF_BIT(SR_OVRUN)) + dev_err(&psif->pdev->dev, "overrun error\n"); + serio_interrupt(psif->io, psif_readl(psif, RHR), flags); + retval = IRQ_HANDLED; + } + + spin_lock(&psif->lock); + + if (status & PSIF_BIT(SR_TXEMPTY) && psif->head == psif->tail) { + psif_writel(psif, IDR, PSIF_BIT(IDR_TXEMPTY)); + } + else if (status & PSIF_BIT(SR_TXEMPTY)) { + psif_writel(psif, THR, (unsigned long)psif->buffer[psif->tail]); + psif->tail = (psif->tail + 1) & ((sizeof(psif->buffer) - 1)); + } + + spin_unlock(&psif->lock); + + return retval; +} + +static int psif_write(struct serio *io, unsigned char val) +{ + struct psif *psif = io->port_data; + unsigned long flags; + unsigned int head; + + spin_lock_irqsave(&psif->lock, flags); + + /* Write directly if TX is ready. */ + if (psif_readl(psif, SR) & PSIF_BIT(SR_TXEMPTY)) { + psif_writel(psif, THR, val); + } else { + if (psif->head == psif->tail) + psif_writel(psif, IER, PSIF_BIT(IER_TXEMPTY)); + head = (psif->head + 1) & ((sizeof(psif->buffer) - 1)); + if (head != psif->tail) { + psif->buffer[psif->head] = val; + psif->head = head; + } + } + + spin_unlock_irqrestore(&psif->lock, flags); + + return 0; +} + +static int psif_open(struct serio *io) +{ + struct psif *psif = io->port_data; + int retval; + + retval = clk_enable(psif->pclk); + if (retval) + goto out; + + psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN)); + psif_writel(psif, IER, PSIF_BIT(IER_RXRDY)); +out: + return retval; +} + +static void psif_close(struct serio *io) +{ + struct psif *psif = io->port_data; + + psif_writel(psif, IDR, ~0UL); + psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); + + clk_disable(psif->pclk); +} + +static int psif_set_prescaler(struct psif *psif) +{ + unsigned long prscv; + unsigned long rate = clk_get_rate(psif->pclk); + + /* PRSCV = Pulse length (100 uS) * PSIF module frequency. */ + prscv = 100 * (rate / 1000000); + + if (prscv > 0xfff) + prscv = 0xfff; + + psif_writel(psif, PSR, prscv); + + return prscv; +} + +static int __devinit psif_probe(struct platform_device *pdev) +{ + struct resource *regs; + struct psif *psif; + struct serio *io; + struct clk *pclk; + int irq; + int ret; + + psif = kzalloc(sizeof(struct psif), GFP_KERNEL); + if (!psif) { + dev_dbg(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto out; + } + psif->pdev = pdev; + + io = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!io) { + dev_dbg(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto out_free_psif; + } + psif->io = io; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_dbg(&pdev->dev, "no mmio resources defined\n"); + ret = -ENOMEM; + goto out_free_io; + } + + pclk = clk_get(&pdev->dev, "pclk"); + if (IS_ERR(pclk)) { + dev_dbg(&pdev->dev, "could not get peripheral clock\n"); + ret = PTR_ERR(pclk); + goto out_free_io; + } + psif->pclk = pclk; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_dbg(&pdev->dev, "could not get irq\n"); + ret = -ENXIO; + goto out_put_clk; + } + ret = request_irq(irq, psif_interrupt, IRQF_SHARED, "psif_kbd", psif); + if (ret) { + dev_dbg(&pdev->dev, "could not request irq %d\n", irq); + goto out_put_clk; + } + psif->irq = irq; + + psif->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!psif->regs) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "could not map I/O memory\n"); + goto out_free_irq; + } + + io->id.type = SERIO_8042; + io->write = psif_write; + io->open = psif_open; + io->close = psif_close; + strlcpy(io->name, pdev->dev.bus_id, sizeof(io->name)); + strlcpy(io->phys, pdev->dev.bus_id, sizeof(io->phys)); + io->port_data = psif; + io->dev.parent = &pdev->dev; + + ret = psif_set_prescaler(psif); + if (ret < 0) { + dev_dbg(&pdev->dev, "could not set prescaler\n"); + goto out_iounmap; + } + + spin_lock_init(&psif->lock); + serio_register_port(psif->io); + platform_set_drvdata(pdev, psif); + + dev_info(&pdev->dev, "Atmel AVR32 PSIF PS/2 driver on 0x%08x irq %d\n", + (int)psif->regs, psif->irq); + + return 0; + +out_iounmap: + iounmap(psif->regs); +out_free_irq: + free_irq(psif->irq, psif); +out_put_clk: + clk_put(psif->pclk); +out_free_io: + kfree(io); +out_free_psif: + kfree(psif); +out: + return ret; +} + +static int __devexit psif_remove(struct platform_device *pdev) +{ + struct psif *psif = platform_get_drvdata(pdev); + + if (psif) { + serio_unregister_port(psif->io); + iounmap(psif->regs); + free_irq(psif->irq, psif); + clk_put(psif->pclk); + kfree(psif->io); + kfree(psif); + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int psif_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct psif *psif = platform_get_drvdata(pdev); + + psif_writel(psif, CR, PSIF_BIT(CR_RXDIS) | PSIF_BIT(CR_TXDIS)); + clk_disable(psif->pclk); + + return 0; +} + +static int psif_resume(struct platform_device *pdev) +{ + struct psif *psif = platform_get_drvdata(pdev); + + clk_enable(psif->pclk); + psif_writel(psif, CR, PSIF_BIT(CR_RXEN) | PSIF_BIT(CR_TXEN)); + psif_set_prescaler(psif); + + return 0; +} +#else +#define psif_suspend NULL +#define psif_resume NULL +#endif + +static struct platform_driver psif_driver = { + .remove = __devexit_p(psif_remove), + .driver = { + .name = "atmel_psif", + }, + .suspend = psif_suspend, + .resume = psif_resume, +}; + +static int __init psif_init(void) +{ + return platform_driver_probe(&psif_driver, psif_probe); +} + +static void __exit psif_exit(void) +{ + platform_driver_unregister(&psif_driver); +} + +module_init(psif_init); +module_exit(psif_exit); + +MODULE_AUTHOR("Hans-Christian Egtvedt "); +MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/serio/at32psif.h b/drivers/input/serio/at32psif.h new file mode 100644 index 0000000..b0f19b7 --- /dev/null +++ b/drivers/input/serio/at32psif.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * Driver for the AT32AP700X PS/2 controller (PSIF). + * + * 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. + */ + +#ifndef _AT32AP700X_PSIF +#define _AT32AP700X_PSIF + +/* PSIF register offsets */ +#define PSIF_CR 0x0000 +#define PSIF_IDR 0x0018 +#define PSIF_IER 0x0014 +#define PSIF_IMR 0x001c +#define PSIF_PSR 0x0024 +#define PSIF_RHR 0x0004 +#define PSIF_SR 0x0010 +#define PSIF_THR 0x0008 + +/* Bitfields in control register. */ +#define PSIF_CR_RXDIS_OFFSET 1 +#define PSIF_CR_RXDIS_SIZE 1 +#define PSIF_CR_RXEN_OFFSET 0 +#define PSIF_CR_RXEN_SIZE 1 +#define PSIF_CR_SWRST_OFFSET 15 +#define PSIF_CR_SWRST_SIZE 1 +#define PSIF_CR_TXDIS_OFFSET 9 +#define PSIF_CR_TXDIS_SIZE 1 +#define PSIF_CR_TXEN_OFFSET 8 +#define PSIF_CR_TXEN_SIZE 1 + +/* Bitfields in interrupt disable register. */ +#define PSIF_IDR_NACK_OFFSET 8 +#define PSIF_IDR_NACK_SIZE 1 +#define PSIF_IDR_OVRUN_OFFSET 5 +#define PSIF_IDR_OVRUN_SIZE 1 +#define PSIF_IDR_PARITY_OFFSET 9 +#define PSIF_IDR_PARITY_SIZE 1 +#define PSIF_IDR_RXRDY_OFFSET 4 +#define PSIF_IDR_RXRDY_SIZE 1 +#define PSIF_IDR_TXEMPTY_OFFSET 1 +#define PSIF_IDR_TXEMPTY_SIZE 1 +#define PSIF_IDR_TXRDY_OFFSET 0 +#define PSIF_IDR_TXRDY_SIZE 1 + +/* Bitfields in interrupt enable register. */ +#define PSIF_IER_NACK_OFFSET 8 +#define PSIF_IER_NACK_SIZE 1 +#define PSIF_IER_OVRUN_OFFSET 5 +#define PSIF_IER_OVRUN_SIZE 1 +#define PSIF_IER_PARITY_OFFSET 9 +#define PSIF_IER_PARITY_SIZE 1 +#define PSIF_IER_RXRDY_OFFSET 4 +#define PSIF_IER_RXRDY_SIZE 1 +#define PSIF_IER_TXEMPTY_OFFSET 1 +#define PSIF_IER_TXEMPTY_SIZE 1 +#define PSIF_IER_TXRDY_OFFSET 0 +#define PSIF_IER_TXRDY_SIZE 1 + +/* Bitfields in interrupt mask register. */ +#define PSIF_IMR_NACK_OFFSET 8 +#define PSIF_IMR_NACK_SIZE 1 +#define PSIF_IMR_OVRUN_OFFSET 5 +#define PSIF_IMR_OVRUN_SIZE 1 +#define PSIF_IMR_PARITY_OFFSET 9 +#define PSIF_IMR_PARITY_SIZE 1 +#define PSIF_IMR_RXRDY_OFFSET 4 +#define PSIF_IMR_RXRDY_SIZE 1 +#define PSIF_IMR_TXEMPTY_OFFSET 1 +#define PSIF_IMR_TXEMPTY_SIZE 1 +#define PSIF_IMR_TXRDY_OFFSET 0 +#define PSIF_IMR_TXRDY_SIZE 1 + +/* Bitfields in prescale register. */ +#define PSIF_PSR_PRSCV_OFFSET 0 +#define PSIF_PSR_PRSCV_SIZE 13 + +/* Bitfields in receive hold register. */ +#define PSIF_RHR_RXDATA_OFFSET 0 +#define PSIF_RHR_RXDATA_SIZE 8 + +/* Bitfields in status register. */ +#define PSIF_SR_NACK_OFFSET 8 +#define PSIF_SR_NACK_SIZE 1 +#define PSIF_SR_OVRUN_OFFSET 5 +#define PSIF_SR_OVRUN_SIZE 1 +#define PSIF_SR_PARITY_OFFSET 9 +#define PSIF_SR_PARITY_SIZE 1 +#define PSIF_SR_RXRDY_OFFSET 4 +#define PSIF_SR_RXRDY_SIZE 1 +#define PSIF_SR_TXEMPTY_OFFSET 1 +#define PSIF_SR_TXEMPTY_SIZE 1 +#define PSIF_SR_TXRDY_OFFSET 0 +#define PSIF_SR_TXRDY_SIZE 1 + +/* Bitfields in transmit hold register. */ +#define PSIF_THR_TXDATA_OFFSET 0 +#define PSIF_THR_TXDATA_SIZE 8 + +/* Bit manipulation macros */ +#define PSIF_BIT(name) \ + (1 << PSIF_##name##_OFFSET) +#define PSIF_BF(name,value) \ + (((value) & ((1 << PSIF_##name##_SIZE) - 1)) \ + << PSIF_##name##_OFFSET) +#define PSIF_BFEXT(name,value)\ + (((value) >> PSIF_##name##_OFFSET) \ + & ((1 << PSIF_##name##_SIZE) - 1)) +#define PSIF_BFINS(name,value,old) \ + (((old) & ~(((1 << PSIF_##name##_SIZE) - 1) \ + << PSIF_##name##_OFFSET)) \ + | PSIF_BF(name,value)) + +/* Register access macros */ +#define psif_readl(port,reg) \ + __raw_readl((port)->regs + PSIF_##reg) +#define psif_writel(port,reg,value) \ + __raw_writel((value), (port)->regs + PSIF_##reg) + +#endif /* _AT32AP700X_PSIF */