]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * linux/drivers/input/serio/ambakmi.c | |
4 | * | |
5 | * Copyright (C) 2000-2003 Deep Blue Solutions Ltd. | |
6 | * Copyright (C) 2002 Russell King. | |
1da177e4 LT |
7 | */ |
8 | #include <linux/module.h> | |
1da177e4 LT |
9 | #include <linux/serio.h> |
10 | #include <linux/errno.h> | |
11 | #include <linux/interrupt.h> | |
12 | #include <linux/ioport.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/delay.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/err.h> | |
a62c80e5 RK |
17 | #include <linux/amba/bus.h> |
18 | #include <linux/amba/kmi.h> | |
f8ce2547 | 19 | #include <linux/clk.h> |
1da177e4 LT |
20 | |
21 | #include <asm/io.h> | |
22 | #include <asm/irq.h> | |
1da177e4 LT |
23 | |
24 | #define KMI_BASE (kmi->base) | |
25 | ||
26 | struct amba_kmi_port { | |
27 | struct serio *io; | |
28 | struct clk *clk; | |
29 | void __iomem *base; | |
30 | unsigned int irq; | |
31 | unsigned int divisor; | |
32 | unsigned int open; | |
33 | }; | |
34 | ||
7d12e780 | 35 | static irqreturn_t amba_kmi_int(int irq, void *dev_id) |
1da177e4 LT |
36 | { |
37 | struct amba_kmi_port *kmi = dev_id; | |
38 | unsigned int status = readb(KMIIR); | |
39 | int handled = IRQ_NONE; | |
40 | ||
41 | while (status & KMIIR_RXINTR) { | |
7d12e780 | 42 | serio_interrupt(kmi->io, readb(KMIDATA), 0); |
1da177e4 LT |
43 | status = readb(KMIIR); |
44 | handled = IRQ_HANDLED; | |
45 | } | |
46 | ||
47 | return handled; | |
48 | } | |
49 | ||
50 | static int amba_kmi_write(struct serio *io, unsigned char val) | |
51 | { | |
52 | struct amba_kmi_port *kmi = io->port_data; | |
53 | unsigned int timeleft = 10000; /* timeout in 100ms */ | |
54 | ||
4ab73761 | 55 | while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && --timeleft) |
1da177e4 LT |
56 | udelay(10); |
57 | ||
58 | if (timeleft) | |
59 | writeb(val, KMIDATA); | |
60 | ||
61 | return timeleft ? 0 : SERIO_TIMEOUT; | |
62 | } | |
63 | ||
64 | static int amba_kmi_open(struct serio *io) | |
65 | { | |
66 | struct amba_kmi_port *kmi = io->port_data; | |
67 | unsigned int divisor; | |
68 | int ret; | |
69 | ||
59d1f5c4 | 70 | ret = clk_prepare_enable(kmi->clk); |
1da177e4 | 71 | if (ret) |
a8d3584a | 72 | goto out; |
1da177e4 LT |
73 | |
74 | divisor = clk_get_rate(kmi->clk) / 8000000 - 1; | |
75 | writeb(divisor, KMICLKDIV); | |
76 | writeb(KMICR_EN, KMICR); | |
77 | ||
5d61b54f LD |
78 | ret = request_irq(kmi->irq, amba_kmi_int, IRQF_SHARED, "kmi-pl050", |
79 | kmi); | |
1da177e4 LT |
80 | if (ret) { |
81 | printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq); | |
82 | writeb(0, KMICR); | |
83 | goto clk_disable; | |
84 | } | |
85 | ||
86 | writeb(KMICR_EN | KMICR_RXINTREN, KMICR); | |
87 | ||
88 | return 0; | |
89 | ||
90 | clk_disable: | |
59d1f5c4 | 91 | clk_disable_unprepare(kmi->clk); |
1da177e4 LT |
92 | out: |
93 | return ret; | |
94 | } | |
95 | ||
96 | static void amba_kmi_close(struct serio *io) | |
97 | { | |
98 | struct amba_kmi_port *kmi = io->port_data; | |
99 | ||
100 | writeb(0, KMICR); | |
101 | ||
102 | free_irq(kmi->irq, kmi); | |
59d1f5c4 | 103 | clk_disable_unprepare(kmi->clk); |
1da177e4 LT |
104 | } |
105 | ||
5298cc4c | 106 | static int amba_kmi_probe(struct amba_device *dev, |
aa25afad | 107 | const struct amba_id *id) |
1da177e4 LT |
108 | { |
109 | struct amba_kmi_port *kmi; | |
110 | struct serio *io; | |
111 | int ret; | |
112 | ||
113 | ret = amba_request_regions(dev, NULL); | |
114 | if (ret) | |
115 | return ret; | |
116 | ||
dd00cc48 YP |
117 | kmi = kzalloc(sizeof(struct amba_kmi_port), GFP_KERNEL); |
118 | io = kzalloc(sizeof(struct serio), GFP_KERNEL); | |
1da177e4 LT |
119 | if (!kmi || !io) { |
120 | ret = -ENOMEM; | |
121 | goto out; | |
122 | } | |
123 | ||
1da177e4 LT |
124 | |
125 | io->id.type = SERIO_8042; | |
126 | io->write = amba_kmi_write; | |
127 | io->open = amba_kmi_open; | |
128 | io->close = amba_kmi_close; | |
4e8718a1 KS |
129 | strlcpy(io->name, dev_name(&dev->dev), sizeof(io->name)); |
130 | strlcpy(io->phys, dev_name(&dev->dev), sizeof(io->phys)); | |
1da177e4 LT |
131 | io->port_data = kmi; |
132 | io->dev.parent = &dev->dev; | |
133 | ||
266429df | 134 | kmi->io = io; |
dc890c2d | 135 | kmi->base = ioremap(dev->res.start, resource_size(&dev->res)); |
1da177e4 LT |
136 | if (!kmi->base) { |
137 | ret = -ENOMEM; | |
138 | goto out; | |
139 | } | |
140 | ||
141 | kmi->clk = clk_get(&dev->dev, "KMIREFCLK"); | |
142 | if (IS_ERR(kmi->clk)) { | |
143 | ret = PTR_ERR(kmi->clk); | |
144 | goto unmap; | |
145 | } | |
146 | ||
147 | kmi->irq = dev->irq[0]; | |
148 | amba_set_drvdata(dev, kmi); | |
149 | ||
150 | serio_register_port(kmi->io); | |
151 | return 0; | |
152 | ||
153 | unmap: | |
154 | iounmap(kmi->base); | |
155 | out: | |
156 | kfree(kmi); | |
157 | kfree(io); | |
158 | amba_release_regions(dev); | |
159 | return ret; | |
160 | } | |
161 | ||
e2619cf7 | 162 | static int amba_kmi_remove(struct amba_device *dev) |
1da177e4 LT |
163 | { |
164 | struct amba_kmi_port *kmi = amba_get_drvdata(dev); | |
165 | ||
1da177e4 LT |
166 | serio_unregister_port(kmi->io); |
167 | clk_put(kmi->clk); | |
168 | iounmap(kmi->base); | |
169 | kfree(kmi); | |
170 | amba_release_regions(dev); | |
171 | return 0; | |
172 | } | |
173 | ||
cee3d8cc | 174 | static int __maybe_unused amba_kmi_resume(struct device *dev) |
1da177e4 | 175 | { |
cee3d8cc | 176 | struct amba_kmi_port *kmi = dev_get_drvdata(dev); |
1da177e4 LT |
177 | |
178 | /* kick the serio layer to rescan this port */ | |
179 | serio_reconnect(kmi->io); | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
cee3d8cc UH |
184 | static SIMPLE_DEV_PM_OPS(amba_kmi_dev_pm_ops, NULL, amba_kmi_resume); |
185 | ||
b54bf2fd | 186 | static const struct amba_id amba_kmi_idtable[] = { |
1da177e4 LT |
187 | { |
188 | .id = 0x00041050, | |
189 | .mask = 0x000fffff, | |
190 | }, | |
191 | { 0, 0 } | |
192 | }; | |
193 | ||
2dfff235 DM |
194 | MODULE_DEVICE_TABLE(amba, amba_kmi_idtable); |
195 | ||
1da177e4 LT |
196 | static struct amba_driver ambakmi_driver = { |
197 | .drv = { | |
198 | .name = "kmi-pl050", | |
898d1053 | 199 | .owner = THIS_MODULE, |
cee3d8cc | 200 | .pm = &amba_kmi_dev_pm_ops, |
1da177e4 LT |
201 | }, |
202 | .id_table = amba_kmi_idtable, | |
203 | .probe = amba_kmi_probe, | |
1cb0aa88 | 204 | .remove = amba_kmi_remove, |
1da177e4 LT |
205 | }; |
206 | ||
9e5ed094 | 207 | module_amba_driver(ambakmi_driver); |
1da177e4 LT |
208 | |
209 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); | |
210 | MODULE_DESCRIPTION("AMBA KMI controller driver"); | |
211 | MODULE_LICENSE("GPL"); |