109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2e443631dSVishnu Patekar /* 3e443631dSVishnu Patekar * Driver for Allwinner A10 PS2 host controller 4e443631dSVishnu Patekar * 5e443631dSVishnu Patekar * Author: Vishnu Patekar <vishnupatekar0510@gmail.com> 6e443631dSVishnu Patekar * Aaron.maoye <leafy.myeh@newbietech.com> 7e443631dSVishnu Patekar */ 8e443631dSVishnu Patekar 9e443631dSVishnu Patekar #include <linux/module.h> 10e443631dSVishnu Patekar #include <linux/serio.h> 11e443631dSVishnu Patekar #include <linux/interrupt.h> 12e443631dSVishnu Patekar #include <linux/errno.h> 13e443631dSVishnu Patekar #include <linux/slab.h> 14e443631dSVishnu Patekar #include <linux/io.h> 15e443631dSVishnu Patekar #include <linux/clk.h> 16e443631dSVishnu Patekar #include <linux/mod_devicetable.h> 17e443631dSVishnu Patekar #include <linux/platform_device.h> 18e443631dSVishnu Patekar 19e443631dSVishnu Patekar #define DRIVER_NAME "sun4i-ps2" 20e443631dSVishnu Patekar 21e443631dSVishnu Patekar /* register offset definitions */ 22e443631dSVishnu Patekar #define PS2_REG_GCTL 0x00 /* PS2 Module Global Control Reg */ 23e443631dSVishnu Patekar #define PS2_REG_DATA 0x04 /* PS2 Module Data Reg */ 24e443631dSVishnu Patekar #define PS2_REG_LCTL 0x08 /* PS2 Module Line Control Reg */ 25e443631dSVishnu Patekar #define PS2_REG_LSTS 0x0C /* PS2 Module Line Status Reg */ 26e443631dSVishnu Patekar #define PS2_REG_FCTL 0x10 /* PS2 Module FIFO Control Reg */ 27e443631dSVishnu Patekar #define PS2_REG_FSTS 0x14 /* PS2 Module FIFO Status Reg */ 28e443631dSVishnu Patekar #define PS2_REG_CLKDR 0x18 /* PS2 Module Clock Divider Reg*/ 29e443631dSVishnu Patekar 30e443631dSVishnu Patekar /* PS2 GLOBAL CONTROL REGISTER PS2_GCTL */ 31e443631dSVishnu Patekar #define PS2_GCTL_INTFLAG BIT(4) 32e443631dSVishnu Patekar #define PS2_GCTL_INTEN BIT(3) 33e443631dSVishnu Patekar #define PS2_GCTL_RESET BIT(2) 34e443631dSVishnu Patekar #define PS2_GCTL_MASTER BIT(1) 35e443631dSVishnu Patekar #define PS2_GCTL_BUSEN BIT(0) 36e443631dSVishnu Patekar 37e443631dSVishnu Patekar /* PS2 LINE CONTROL REGISTER */ 38e443631dSVishnu Patekar #define PS2_LCTL_NOACK BIT(18) 39e443631dSVishnu Patekar #define PS2_LCTL_TXDTOEN BIT(8) 40e443631dSVishnu Patekar #define PS2_LCTL_STOPERREN BIT(3) 41e443631dSVishnu Patekar #define PS2_LCTL_ACKERREN BIT(2) 42e443631dSVishnu Patekar #define PS2_LCTL_PARERREN BIT(1) 43e443631dSVishnu Patekar #define PS2_LCTL_RXDTOEN BIT(0) 44e443631dSVishnu Patekar 45e443631dSVishnu Patekar /* PS2 LINE STATUS REGISTER */ 46e443631dSVishnu Patekar #define PS2_LSTS_TXTDO BIT(8) 47e443631dSVishnu Patekar #define PS2_LSTS_STOPERR BIT(3) 48e443631dSVishnu Patekar #define PS2_LSTS_ACKERR BIT(2) 49e443631dSVishnu Patekar #define PS2_LSTS_PARERR BIT(1) 50e443631dSVishnu Patekar #define PS2_LSTS_RXTDO BIT(0) 51e443631dSVishnu Patekar 52e443631dSVishnu Patekar #define PS2_LINE_ERROR_BIT \ 53e443631dSVishnu Patekar (PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR | \ 54e443631dSVishnu Patekar PS2_LSTS_PARERR | PS2_LSTS_RXTDO) 55e443631dSVishnu Patekar 56e443631dSVishnu Patekar /* PS2 FIFO CONTROL REGISTER */ 57e443631dSVishnu Patekar #define PS2_FCTL_TXRST BIT(17) 58e443631dSVishnu Patekar #define PS2_FCTL_RXRST BIT(16) 59e443631dSVishnu Patekar #define PS2_FCTL_TXUFIEN BIT(10) 60e443631dSVishnu Patekar #define PS2_FCTL_TXOFIEN BIT(9) 61e443631dSVishnu Patekar #define PS2_FCTL_TXRDYIEN BIT(8) 62e443631dSVishnu Patekar #define PS2_FCTL_RXUFIEN BIT(2) 63e443631dSVishnu Patekar #define PS2_FCTL_RXOFIEN BIT(1) 64e443631dSVishnu Patekar #define PS2_FCTL_RXRDYIEN BIT(0) 65e443631dSVishnu Patekar 66e443631dSVishnu Patekar /* PS2 FIFO STATUS REGISTER */ 67e443631dSVishnu Patekar #define PS2_FSTS_TXUF BIT(10) 68e443631dSVishnu Patekar #define PS2_FSTS_TXOF BIT(9) 69e443631dSVishnu Patekar #define PS2_FSTS_TXRDY BIT(8) 70e443631dSVishnu Patekar #define PS2_FSTS_RXUF BIT(2) 71e443631dSVishnu Patekar #define PS2_FSTS_RXOF BIT(1) 72e443631dSVishnu Patekar #define PS2_FSTS_RXRDY BIT(0) 73e443631dSVishnu Patekar 74e443631dSVishnu Patekar #define PS2_FIFO_ERROR_BIT \ 75e443631dSVishnu Patekar (PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_RXUF | PS2_FSTS_RXOF) 76e443631dSVishnu Patekar 77e443631dSVishnu Patekar #define PS2_SAMPLE_CLK 1000000 78e443631dSVishnu Patekar #define PS2_SCLK 125000 79e443631dSVishnu Patekar 80e443631dSVishnu Patekar struct sun4i_ps2data { 81e443631dSVishnu Patekar struct serio *serio; 82e443631dSVishnu Patekar struct device *dev; 83e443631dSVishnu Patekar 84e443631dSVishnu Patekar /* IO mapping base */ 85e443631dSVishnu Patekar void __iomem *reg_base; 86e443631dSVishnu Patekar 87e443631dSVishnu Patekar /* clock management */ 88e443631dSVishnu Patekar struct clk *clk; 89e443631dSVishnu Patekar 90e443631dSVishnu Patekar /* irq */ 91e443631dSVishnu Patekar spinlock_t lock; 92e443631dSVishnu Patekar int irq; 93e443631dSVishnu Patekar }; 94e443631dSVishnu Patekar 95e443631dSVishnu Patekar static irqreturn_t sun4i_ps2_interrupt(int irq, void *dev_id) 96e443631dSVishnu Patekar { 97e443631dSVishnu Patekar struct sun4i_ps2data *drvdata = dev_id; 98e443631dSVishnu Patekar u32 intr_status; 99e443631dSVishnu Patekar u32 fifo_status; 100e443631dSVishnu Patekar unsigned char byte; 101e443631dSVishnu Patekar unsigned int rxflags = 0; 102e443631dSVishnu Patekar u32 rval; 103e443631dSVishnu Patekar 104e443631dSVishnu Patekar spin_lock(&drvdata->lock); 105e443631dSVishnu Patekar 106e443631dSVishnu Patekar /* Get the PS/2 interrupts and clear them */ 107e443631dSVishnu Patekar intr_status = readl(drvdata->reg_base + PS2_REG_LSTS); 108e443631dSVishnu Patekar fifo_status = readl(drvdata->reg_base + PS2_REG_FSTS); 109e443631dSVishnu Patekar 110e443631dSVishnu Patekar /* Check line status register */ 111e443631dSVishnu Patekar if (intr_status & PS2_LINE_ERROR_BIT) { 112e443631dSVishnu Patekar rxflags = (intr_status & PS2_LINE_ERROR_BIT) ? SERIO_FRAME : 0; 113e443631dSVishnu Patekar rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_PARITY : 0; 114e443631dSVishnu Patekar rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_TIMEOUT : 0; 115e443631dSVishnu Patekar 116e443631dSVishnu Patekar rval = PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR | 117e443631dSVishnu Patekar PS2_LSTS_PARERR | PS2_LSTS_RXTDO; 118e443631dSVishnu Patekar writel(rval, drvdata->reg_base + PS2_REG_LSTS); 119e443631dSVishnu Patekar } 120e443631dSVishnu Patekar 121e443631dSVishnu Patekar /* Check FIFO status register */ 122e443631dSVishnu Patekar if (fifo_status & PS2_FIFO_ERROR_BIT) { 123e443631dSVishnu Patekar rval = PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_TXRDY | 124e443631dSVishnu Patekar PS2_FSTS_RXUF | PS2_FSTS_RXOF | PS2_FSTS_RXRDY; 125e443631dSVishnu Patekar writel(rval, drvdata->reg_base + PS2_REG_FSTS); 126e443631dSVishnu Patekar } 127e443631dSVishnu Patekar 128e443631dSVishnu Patekar rval = (fifo_status >> 16) & 0x3; 129e443631dSVishnu Patekar while (rval--) { 130e443631dSVishnu Patekar byte = readl(drvdata->reg_base + PS2_REG_DATA) & 0xff; 131e443631dSVishnu Patekar serio_interrupt(drvdata->serio, byte, rxflags); 132e443631dSVishnu Patekar } 133e443631dSVishnu Patekar 134e443631dSVishnu Patekar writel(intr_status, drvdata->reg_base + PS2_REG_LSTS); 135e443631dSVishnu Patekar writel(fifo_status, drvdata->reg_base + PS2_REG_FSTS); 136e443631dSVishnu Patekar 137e443631dSVishnu Patekar spin_unlock(&drvdata->lock); 138e443631dSVishnu Patekar 139e443631dSVishnu Patekar return IRQ_HANDLED; 140e443631dSVishnu Patekar } 141e443631dSVishnu Patekar 142e443631dSVishnu Patekar static int sun4i_ps2_open(struct serio *serio) 143e443631dSVishnu Patekar { 144e443631dSVishnu Patekar struct sun4i_ps2data *drvdata = serio->port_data; 145e443631dSVishnu Patekar u32 src_clk = 0; 146e443631dSVishnu Patekar u32 clk_scdf; 147e443631dSVishnu Patekar u32 clk_pcdf; 148e443631dSVishnu Patekar u32 rval; 149e443631dSVishnu Patekar unsigned long flags; 150e443631dSVishnu Patekar 151e443631dSVishnu Patekar /* Set line control and enable interrupt */ 152e443631dSVishnu Patekar rval = PS2_LCTL_STOPERREN | PS2_LCTL_ACKERREN 153e443631dSVishnu Patekar | PS2_LCTL_PARERREN | PS2_LCTL_RXDTOEN; 154e443631dSVishnu Patekar writel(rval, drvdata->reg_base + PS2_REG_LCTL); 155e443631dSVishnu Patekar 156e443631dSVishnu Patekar /* Reset FIFO */ 157e443631dSVishnu Patekar rval = PS2_FCTL_TXRST | PS2_FCTL_RXRST | PS2_FCTL_TXUFIEN 158e443631dSVishnu Patekar | PS2_FCTL_TXOFIEN | PS2_FCTL_RXUFIEN 159e443631dSVishnu Patekar | PS2_FCTL_RXOFIEN | PS2_FCTL_RXRDYIEN; 160e443631dSVishnu Patekar 161e443631dSVishnu Patekar writel(rval, drvdata->reg_base + PS2_REG_FCTL); 162e443631dSVishnu Patekar 163e443631dSVishnu Patekar src_clk = clk_get_rate(drvdata->clk); 164e443631dSVishnu Patekar /* Set clock divider register */ 165e443631dSVishnu Patekar clk_scdf = src_clk / PS2_SAMPLE_CLK - 1; 166e443631dSVishnu Patekar clk_pcdf = PS2_SAMPLE_CLK / PS2_SCLK - 1; 167e443631dSVishnu Patekar rval = (clk_scdf << 8) | clk_pcdf; 168e443631dSVishnu Patekar writel(rval, drvdata->reg_base + PS2_REG_CLKDR); 169e443631dSVishnu Patekar 170e443631dSVishnu Patekar /* Set global control register */ 171e443631dSVishnu Patekar rval = PS2_GCTL_RESET | PS2_GCTL_INTEN | PS2_GCTL_MASTER 172e443631dSVishnu Patekar | PS2_GCTL_BUSEN; 173e443631dSVishnu Patekar 174e443631dSVishnu Patekar spin_lock_irqsave(&drvdata->lock, flags); 175e443631dSVishnu Patekar writel(rval, drvdata->reg_base + PS2_REG_GCTL); 176e443631dSVishnu Patekar spin_unlock_irqrestore(&drvdata->lock, flags); 177e443631dSVishnu Patekar 178e443631dSVishnu Patekar return 0; 179e443631dSVishnu Patekar } 180e443631dSVishnu Patekar 181e443631dSVishnu Patekar static void sun4i_ps2_close(struct serio *serio) 182e443631dSVishnu Patekar { 183e443631dSVishnu Patekar struct sun4i_ps2data *drvdata = serio->port_data; 184e443631dSVishnu Patekar u32 rval; 185e443631dSVishnu Patekar 186e443631dSVishnu Patekar /* Shut off the interrupt */ 187e443631dSVishnu Patekar rval = readl(drvdata->reg_base + PS2_REG_GCTL); 188e443631dSVishnu Patekar writel(rval & ~(PS2_GCTL_INTEN), drvdata->reg_base + PS2_REG_GCTL); 189e443631dSVishnu Patekar 190e443631dSVishnu Patekar synchronize_irq(drvdata->irq); 191e443631dSVishnu Patekar } 192e443631dSVishnu Patekar 193e443631dSVishnu Patekar static int sun4i_ps2_write(struct serio *serio, unsigned char val) 194e443631dSVishnu Patekar { 195e443631dSVishnu Patekar unsigned long expire = jiffies + msecs_to_jiffies(10000); 196e443631dSVishnu Patekar struct sun4i_ps2data *drvdata = serio->port_data; 197e443631dSVishnu Patekar 198e443631dSVishnu Patekar do { 199e443631dSVishnu Patekar if (readl(drvdata->reg_base + PS2_REG_FSTS) & PS2_FSTS_TXRDY) { 200e443631dSVishnu Patekar writel(val, drvdata->reg_base + PS2_REG_DATA); 201e443631dSVishnu Patekar return 0; 202e443631dSVishnu Patekar } 203e443631dSVishnu Patekar } while (time_before(jiffies, expire)); 204e443631dSVishnu Patekar 205e443631dSVishnu Patekar return SERIO_TIMEOUT; 206e443631dSVishnu Patekar } 207e443631dSVishnu Patekar 208e443631dSVishnu Patekar static int sun4i_ps2_probe(struct platform_device *pdev) 209e443631dSVishnu Patekar { 210e443631dSVishnu Patekar struct resource *res; /* IO mem resources */ 211e443631dSVishnu Patekar struct sun4i_ps2data *drvdata; 212e443631dSVishnu Patekar struct serio *serio; 213e443631dSVishnu Patekar struct device *dev = &pdev->dev; 214e443631dSVishnu Patekar int error; 215e443631dSVishnu Patekar 216e443631dSVishnu Patekar drvdata = kzalloc(sizeof(struct sun4i_ps2data), GFP_KERNEL); 217e443631dSVishnu Patekar serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 218e443631dSVishnu Patekar if (!drvdata || !serio) { 219e443631dSVishnu Patekar error = -ENOMEM; 220e443631dSVishnu Patekar goto err_free_mem; 221e443631dSVishnu Patekar } 222e443631dSVishnu Patekar 223e443631dSVishnu Patekar spin_lock_init(&drvdata->lock); 224e443631dSVishnu Patekar 225e443631dSVishnu Patekar /* IO */ 226e443631dSVishnu Patekar res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 227e443631dSVishnu Patekar if (!res) { 228e443631dSVishnu Patekar dev_err(dev, "failed to locate registers\n"); 229e443631dSVishnu Patekar error = -ENXIO; 230e443631dSVishnu Patekar goto err_free_mem; 231e443631dSVishnu Patekar } 232e443631dSVishnu Patekar 233e443631dSVishnu Patekar drvdata->reg_base = ioremap(res->start, resource_size(res)); 234e443631dSVishnu Patekar if (!drvdata->reg_base) { 235e443631dSVishnu Patekar dev_err(dev, "failed to map registers\n"); 236e443631dSVishnu Patekar error = -ENOMEM; 237e443631dSVishnu Patekar goto err_free_mem; 238e443631dSVishnu Patekar } 239e443631dSVishnu Patekar 240e443631dSVishnu Patekar drvdata->clk = clk_get(dev, NULL); 241e443631dSVishnu Patekar if (IS_ERR(drvdata->clk)) { 242e443631dSVishnu Patekar error = PTR_ERR(drvdata->clk); 243e443631dSVishnu Patekar dev_err(dev, "couldn't get clock %d\n", error); 244e443631dSVishnu Patekar goto err_ioremap; 245e443631dSVishnu Patekar } 246e443631dSVishnu Patekar 247e443631dSVishnu Patekar error = clk_prepare_enable(drvdata->clk); 248e443631dSVishnu Patekar if (error) { 249e443631dSVishnu Patekar dev_err(dev, "failed to enable clock %d\n", error); 250e443631dSVishnu Patekar goto err_clk; 251e443631dSVishnu Patekar } 252e443631dSVishnu Patekar 253e443631dSVishnu Patekar serio->id.type = SERIO_8042; 254e443631dSVishnu Patekar serio->write = sun4i_ps2_write; 255e443631dSVishnu Patekar serio->open = sun4i_ps2_open; 256e443631dSVishnu Patekar serio->close = sun4i_ps2_close; 257e443631dSVishnu Patekar serio->port_data = drvdata; 258e443631dSVishnu Patekar serio->dev.parent = dev; 259*a9f08ad7SWolfram Sang strscpy(serio->name, dev_name(dev), sizeof(serio->name)); 260*a9f08ad7SWolfram Sang strscpy(serio->phys, dev_name(dev), sizeof(serio->phys)); 261e443631dSVishnu Patekar 262e443631dSVishnu Patekar /* shutoff interrupt */ 263e443631dSVishnu Patekar writel(0, drvdata->reg_base + PS2_REG_GCTL); 264e443631dSVishnu Patekar 265e443631dSVishnu Patekar /* Get IRQ for the device */ 266cafb3abeSKrzysztof Kozlowski drvdata->irq = platform_get_irq(pdev, 0); 267cafb3abeSKrzysztof Kozlowski if (drvdata->irq < 0) { 268cafb3abeSKrzysztof Kozlowski error = drvdata->irq; 269e443631dSVishnu Patekar goto err_disable_clk; 270e443631dSVishnu Patekar } 271e443631dSVishnu Patekar 272e443631dSVishnu Patekar drvdata->serio = serio; 273e443631dSVishnu Patekar drvdata->dev = dev; 274e443631dSVishnu Patekar 275e443631dSVishnu Patekar error = request_irq(drvdata->irq, sun4i_ps2_interrupt, 0, 276e443631dSVishnu Patekar DRIVER_NAME, drvdata); 277e443631dSVishnu Patekar if (error) { 278e443631dSVishnu Patekar dev_err(drvdata->dev, "failed to allocate interrupt %d: %d\n", 279e443631dSVishnu Patekar drvdata->irq, error); 280e443631dSVishnu Patekar goto err_disable_clk; 281e443631dSVishnu Patekar } 282e443631dSVishnu Patekar 283e443631dSVishnu Patekar serio_register_port(serio); 284e443631dSVishnu Patekar platform_set_drvdata(pdev, drvdata); 285e443631dSVishnu Patekar 286e443631dSVishnu Patekar return 0; /* success */ 287e443631dSVishnu Patekar 288e443631dSVishnu Patekar err_disable_clk: 289e443631dSVishnu Patekar clk_disable_unprepare(drvdata->clk); 290e443631dSVishnu Patekar err_clk: 291e443631dSVishnu Patekar clk_put(drvdata->clk); 292e443631dSVishnu Patekar err_ioremap: 293e443631dSVishnu Patekar iounmap(drvdata->reg_base); 294e443631dSVishnu Patekar err_free_mem: 295e443631dSVishnu Patekar kfree(serio); 296e443631dSVishnu Patekar kfree(drvdata); 297e443631dSVishnu Patekar return error; 298e443631dSVishnu Patekar } 299e443631dSVishnu Patekar 300e443631dSVishnu Patekar static int sun4i_ps2_remove(struct platform_device *pdev) 301e443631dSVishnu Patekar { 302e443631dSVishnu Patekar struct sun4i_ps2data *drvdata = platform_get_drvdata(pdev); 303e443631dSVishnu Patekar 304e443631dSVishnu Patekar serio_unregister_port(drvdata->serio); 305e443631dSVishnu Patekar 306e443631dSVishnu Patekar free_irq(drvdata->irq, drvdata); 307e443631dSVishnu Patekar 308e443631dSVishnu Patekar clk_disable_unprepare(drvdata->clk); 309e443631dSVishnu Patekar clk_put(drvdata->clk); 310e443631dSVishnu Patekar 311e443631dSVishnu Patekar iounmap(drvdata->reg_base); 312e443631dSVishnu Patekar 313e443631dSVishnu Patekar kfree(drvdata); 314e443631dSVishnu Patekar 315e443631dSVishnu Patekar return 0; 316e443631dSVishnu Patekar } 317e443631dSVishnu Patekar 318e443631dSVishnu Patekar static const struct of_device_id sun4i_ps2_match[] = { 319e443631dSVishnu Patekar { .compatible = "allwinner,sun4i-a10-ps2", }, 320e443631dSVishnu Patekar { }, 321e443631dSVishnu Patekar }; 322e443631dSVishnu Patekar 323e443631dSVishnu Patekar MODULE_DEVICE_TABLE(of, sun4i_ps2_match); 324e443631dSVishnu Patekar 325e443631dSVishnu Patekar static struct platform_driver sun4i_ps2_driver = { 326e443631dSVishnu Patekar .probe = sun4i_ps2_probe, 327e443631dSVishnu Patekar .remove = sun4i_ps2_remove, 328e443631dSVishnu Patekar .driver = { 329e443631dSVishnu Patekar .name = DRIVER_NAME, 330e443631dSVishnu Patekar .of_match_table = sun4i_ps2_match, 331e443631dSVishnu Patekar }, 332e443631dSVishnu Patekar }; 333e443631dSVishnu Patekar module_platform_driver(sun4i_ps2_driver); 334e443631dSVishnu Patekar 335e443631dSVishnu Patekar MODULE_AUTHOR("Vishnu Patekar <vishnupatekar0510@gmail.com>"); 336e443631dSVishnu Patekar MODULE_AUTHOR("Aaron.maoye <leafy.myeh@newbietech.com>"); 337e443631dSVishnu Patekar MODULE_DESCRIPTION("Allwinner A10/Sun4i PS/2 driver"); 338e443631dSVishnu Patekar MODULE_LICENSE("GPL v2"); 339