diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index b88569e..2df47ed 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..228ab15 --- /dev/null +++ b/drivers/input/serio/at32psif.c @@ -0,0 +1,342 @@ +/* + * 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 + +#include "at32psif.h" + +#define PSIF_BUF_SIZE 16 + +#define ring_is_empty(_psif) (_psif->head == _psif->tail) +#define ring_next_head(_psif) ((_psif->head + 1) & (PSIF_BUF_SIZE - 1)) +#define ring_next_tail(_psif) ((_psif->tail + 1) & (PSIF_BUF_SIZE - 1)) + +struct psif { + struct platform_device *pdev; + struct clk *pclk; + struct serio *io; + void __iomem *regs; + unsigned int irq; + unsigned int open; + /* Prevent concurrent writes to circular buffer. */ + spinlock_t lock; + unsigned int head; + unsigned int tail; + unsigned char buffer[PSIF_BUF_SIZE]; +}; + +static irqreturn_t psif_interrupt(int irq, void *_ptr) +{ + struct psif *psif = _ptr; + int retval = IRQ_NONE; + unsigned int io_flags = 0; + unsigned long lock_flags; + unsigned long status; + + status = psif_readl(psif, SR); + + if (status & PSIF_BIT(RXRDY)) { + unsigned char val = (unsigned char) psif_readl(psif, RHR); + + if (status & PSIF_BIT(PARITY)) + io_flags |= SERIO_PARITY; + if (status & PSIF_BIT(OVRUN)) + dev_err(&psif->pdev->dev, "overrun read error\n"); + + /* TODO: why do we have to wait? Are we too fast for serio? */ + udelay(100); + + serio_interrupt(psif->io, val, io_flags); + + retval = IRQ_HANDLED; + } + + spin_lock_irqsave(&psif->lock, lock_flags); + + if (status & PSIF_BIT(TXEMPTY)) { + if (status & PSIF_BIT(NACK)) + dev_err(&psif->pdev->dev, "NACK error\n"); + if (ring_is_empty(psif)) { + psif_writel(psif, IDR, PSIF_BIT(TXEMPTY)); + } else { + psif_writel(psif, THR, psif->buffer[psif->tail]); + psif->tail = ring_next_tail(psif); + } + + retval = IRQ_HANDLED; + } + + spin_unlock_irqrestore(&psif->lock, lock_flags); + + 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(TXEMPTY)) { + psif_writel(psif, THR, val); + } else { + head = ring_next_head(psif); + + if (head != psif->tail) { + psif->buffer[psif->head] = val; + psif->head = head; + } else { + dev_err(&psif->pdev->dev, "underrun write error\n"); + } + + /* Make sure TXEMPTY interrupt is enabled. */ + psif_writel(psif, IER, PSIF_BIT(TXEMPTY)); + } + + 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(RXRDY)); + + psif->open = 1; +out: + return retval; +} + +static void psif_close(struct serio *io) +{ + struct psif *psif = io->port_data; + + psif->open = 0; + + psif_writel(psif, IDR, ~0UL); + psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); + + clk_disable(psif->pclk); +} + +static void 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 > ((1<pdev->dev, "pclk too fast, " + "prescaler set to max\n"); + } + + clk_enable(psif->pclk); + psif_writel(psif, PSR, prscv); + clk_disable(psif->pclk); +} + +static int __init 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; + } + + 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_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_iounmap; + } + psif->pclk = pclk; + + /* Reset the PSIF to enter at a known state. */ + ret = clk_enable(pclk); + if (ret) { + dev_dbg(&pdev->dev, "could not enable pclk\n"); + goto out_put_clk; + } + psif_writel(psif, CR, PSIF_BIT(CR_SWRST)); + clk_disable(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, "at32psif", psif); + if (ret) { + dev_dbg(&pdev->dev, "could not request irq %d\n", irq); + goto out_put_clk; + } + psif->irq = 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; + + psif_set_prescaler(psif); + + 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_put_clk: + clk_put(psif->pclk); +out_iounmap: + iounmap(psif->regs); +out_free_io: + kfree(io); +out_free_psif: + kfree(psif); +out: + return ret; +} + +static int __exit psif_remove(struct platform_device *pdev) +{ + struct psif *psif = platform_get_drvdata(pdev); + + psif_writel(psif, IDR, ~0UL); + psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); + + serio_unregister_port(psif->io); + iounmap(psif->regs); + free_irq(psif->irq, psif); + clk_put(psif->pclk); + 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); + + if (psif->open) { + 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); + + if (psif->open) { + clk_enable(psif->pclk); + psif_set_prescaler(psif); + psif_writel(psif, CR, PSIF_BIT(CR_RXEN) | PSIF_BIT(CR_TXEN)); + } + + return 0; +} +#else +#define psif_suspend NULL +#define psif_resume NULL +#endif + +static struct platform_driver psif_driver = { + .remove = __exit_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..b0cc5e4 --- /dev/null +++ b/drivers/input/serio/at32psif.h @@ -0,0 +1,82 @@ +/* + * 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 _AT32PSIF_H +#define _AT32PSIF_H + +/* PSIF register offsets */ +#define PSIF_CR 0x00 +#define PSIF_RHR 0x04 +#define PSIF_THR 0x08 +#define PSIF_SR 0x10 +#define PSIF_IER 0x14 +#define PSIF_IDR 0x18 +#define PSIF_IMR 0x1c +#define PSIF_PSR 0x20 + +/* 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, enable, mask and status register. */ +#define PSIF_NACK_OFFSET 8 +#define PSIF_NACK_SIZE 1 +#define PSIF_OVRUN_OFFSET 5 +#define PSIF_OVRUN_SIZE 1 +#define PSIF_PARITY_OFFSET 9 +#define PSIF_PARITY_SIZE 1 +#define PSIF_RXRDY_OFFSET 4 +#define PSIF_RXRDY_SIZE 1 +#define PSIF_TXEMPTY_OFFSET 1 +#define PSIF_TXEMPTY_SIZE 1 +#define PSIF_TXRDY_OFFSET 0 +#define PSIF_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 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 /* _AT32PSIF_H */ diff --git a/arch/avr32/boards/atstk1000/Kconfig b/arch/avr32/boards/atstk1000/Kconfig index 56a8d8e..e125205 100644 --- a/arch/avr32/boards/atstk1000/Kconfig +++ b/arch/avr32/boards/atstk1000/Kconfig @@ -145,4 +145,17 @@ config BOARD_ATSTK1000_CF_DETECT_PIN The default is 0x3e, which is pin 30 on PIOB, aka GPIO15. +config BOARD_ATSTK100X_ENABLE_PSIF + bool "Enable PSIF peripheral (PS/2 support)" + default n + help + Select this if you want to use the PSIF peripheral to hook up PS/2 + devices to your STK1000. This will require a hardware modification to + work correctly, since PS/2 devices require 5 volt power and signals, + while the STK1000 only provides 3.3 volt. + + Say N if you have not modified the hardware to boost the voltage, say + Y if you have level convertion hardware or a PS/2 device capable of + operating on 3.3 volt. + endif # stk 1000 diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index f19f54d..2ba37d5 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -261,6 +261,10 @@ static int __init atstk1002_init(void) at32_add_device_ssc(0, ATMEL_SSC_TX); #endif at32_add_device_cf(0, 2, &cf0_data); +#ifdef CONFIG_BOARD_ATSTK100X_ENABLE_PSIF + at32_add_device_psif(0); + at32_add_device_psif(1); +#endif atstk1000_setup_j2_leds(); atstk1002_setup_extdac(); diff --git a/arch/avr32/boards/atstk1000/atstk1003.c b/arch/avr32/boards/atstk1000/atstk1003.c index 768d204..2cc0bbc 100644 --- a/arch/avr32/boards/atstk1000/atstk1003.c +++ b/arch/avr32/boards/atstk1000/atstk1003.c @@ -172,6 +172,10 @@ static int __init atstk1003_init(void) at32_add_device_ssc(0, ATMEL_SSC_TX); #endif at32_add_device_cf(0, 2, &cf0_data); +#ifdef CONFIG_BOARD_ATSTK100X_ENABLE_PSIF + at32_add_device_psif(0); + at32_add_device_psif(1); +#endif atstk1000_setup_j2_leds(); atstk1003_setup_extdac(); diff --git a/arch/avr32/boards/atstk1000/atstk1004.c b/arch/avr32/boards/atstk1000/atstk1004.c index 96015dd..9e8c293 100644 --- a/arch/avr32/boards/atstk1000/atstk1004.c +++ b/arch/avr32/boards/atstk1000/atstk1004.c @@ -143,6 +143,10 @@ static int __init atstk1004_init(void) #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM at32_add_device_ssc(0, ATMEL_SSC_TX); #endif +#ifdef CONFIG_BOARD_ATSTK100X_ENABLE_PSIF + at32_add_device_psif(0); + at32_add_device_psif(1); +#endif atstk1000_setup_j2_leds(); atstk1004_setup_extdac(); diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 3300944..cfec920 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -679,6 +679,81 @@ void __init at32_add_system_devices(void) } /* -------------------------------------------------------------------- + * PSIF + * -------------------------------------------------------------------- */ +static struct resource atmel_psif0_resource[] __initdata = { + { + .start = 0xffe03c00, + .end = 0xffe03cff, + .flags = IORESOURCE_MEM, + }, + IRQ(18), +}; +static struct clk atmel_psif0_pclk = { + .name = "pclk", + .parent = &pba_clk, + .mode = pba_clk_mode, + .get_rate = pba_clk_get_rate, + .index = 15, +}; + +static struct resource atmel_psif1_resource[] __initdata = { + { + .start = 0xffe03d00, + .end = 0xffe03dff, + .flags = IORESOURCE_MEM, + }, + IRQ(18), +}; +static struct clk atmel_psif1_pclk = { + .name = "pclk", + .parent = &pba_clk, + .mode = pba_clk_mode, + .get_rate = pba_clk_get_rate, + .index = 15, +}; + +struct platform_device *__init at32_add_device_psif(unsigned int id) +{ + struct platform_device *pdev; + + if (!(id == 0 || id == 1)) + return NULL; + + pdev = platform_device_alloc("atmel_psif", id); + if (!pdev) + return NULL; + + switch (id) { + case 0: + if (platform_device_add_resources(pdev, atmel_psif0_resource, + ARRAY_SIZE(atmel_psif0_resource))) + goto err_add_resources; + atmel_psif0_pclk.dev = &pdev->dev; + select_peripheral(PA(8), PERIPH_A, 0); /* CLOCK */ + select_peripheral(PA(9), PERIPH_A, 0); /* DATA */ + break; + case 1: + if (platform_device_add_resources(pdev, atmel_psif1_resource, + ARRAY_SIZE(atmel_psif1_resource))) + goto err_add_resources; + atmel_psif1_pclk.dev = &pdev->dev; + select_peripheral(PB(11), PERIPH_A, 0); /* CLOCK */ + select_peripheral(PB(12), PERIPH_A, 0); /* DATA */ + break; + default: + return NULL; + } + + platform_device_add(pdev); + return pdev; + +err_add_resources: + platform_device_put(pdev); + return NULL; +} + +/* -------------------------------------------------------------------- * USART * -------------------------------------------------------------------- */ @@ -1712,6 +1787,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 1a6e02c..19cfec7 100644 --- a/include/asm-avr32/arch-at32ap/board.h +++ b/include/asm-avr32/arch-at32ap/board.h @@ -93,4 +93,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/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c --- 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);