1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * linux/drivers/input/serio/ambakmi.c |
4 | * |
5 | * Copyright (C) 2000-2003 Deep Blue Solutions Ltd. |
6 | * Copyright (C) 2002 Russell King. |
7 | */ |
8 | #include <linux/module.h> |
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> |
17 | #include <linux/amba/bus.h> |
18 | #include <linux/amba/kmi.h> |
19 | #include <linux/clk.h> |
20 | |
21 | #include <asm/io.h> |
22 | #include <asm/irq.h> |
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 | |
35 | static irqreturn_t amba_kmi_int(int irq, void *dev_id) |
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) { |
42 | serio_interrupt(serio: kmi->io, readb(KMIDATA), flags: 0); |
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 | |
55 | while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && --timeleft) |
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 | |
70 | ret = clk_prepare_enable(clk: kmi->clk); |
71 | if (ret) |
72 | goto out; |
73 | |
74 | divisor = clk_get_rate(clk: kmi->clk) / 8000000 - 1; |
75 | writeb(val: divisor, KMICLKDIV); |
76 | writeb(KMICR_EN, KMICR); |
77 | |
78 | ret = request_irq(irq: kmi->irq, handler: amba_kmi_int, IRQF_SHARED, name: "kmi-pl050" , |
79 | dev: kmi); |
80 | if (ret) { |
81 | printk(KERN_ERR "kmi: failed to claim IRQ%d\n" , kmi->irq); |
82 | writeb(val: 0, KMICR); |
83 | goto clk_disable; |
84 | } |
85 | |
86 | writeb(KMICR_EN | KMICR_RXINTREN, KMICR); |
87 | |
88 | return 0; |
89 | |
90 | clk_disable: |
91 | clk_disable_unprepare(clk: kmi->clk); |
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(val: 0, KMICR); |
101 | |
102 | free_irq(kmi->irq, kmi); |
103 | clk_disable_unprepare(clk: kmi->clk); |
104 | } |
105 | |
106 | static int amba_kmi_probe(struct amba_device *dev, |
107 | const struct amba_id *id) |
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 | |
117 | kmi = kzalloc(size: sizeof(struct amba_kmi_port), GFP_KERNEL); |
118 | io = kzalloc(size: sizeof(struct serio), GFP_KERNEL); |
119 | if (!kmi || !io) { |
120 | ret = -ENOMEM; |
121 | goto out; |
122 | } |
123 | |
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; |
129 | strscpy(p: io->name, q: dev_name(dev: &dev->dev), size: sizeof(io->name)); |
130 | strscpy(p: io->phys, q: dev_name(dev: &dev->dev), size: sizeof(io->phys)); |
131 | io->port_data = kmi; |
132 | io->dev.parent = &dev->dev; |
133 | |
134 | kmi->io = io; |
135 | kmi->base = ioremap(offset: dev->res.start, size: resource_size(res: &dev->res)); |
136 | if (!kmi->base) { |
137 | ret = -ENOMEM; |
138 | goto out; |
139 | } |
140 | |
141 | kmi->clk = clk_get(dev: &dev->dev, id: "KMIREFCLK" ); |
142 | if (IS_ERR(ptr: kmi->clk)) { |
143 | ret = PTR_ERR(ptr: 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(addr: kmi->base); |
155 | out: |
156 | kfree(objp: kmi); |
157 | kfree(objp: io); |
158 | amba_release_regions(dev); |
159 | return ret; |
160 | } |
161 | |
162 | static void amba_kmi_remove(struct amba_device *dev) |
163 | { |
164 | struct amba_kmi_port *kmi = amba_get_drvdata(dev); |
165 | |
166 | serio_unregister_port(serio: kmi->io); |
167 | clk_put(clk: kmi->clk); |
168 | iounmap(addr: kmi->base); |
169 | kfree(objp: kmi); |
170 | amba_release_regions(dev); |
171 | } |
172 | |
173 | static int amba_kmi_resume(struct device *dev) |
174 | { |
175 | struct amba_kmi_port *kmi = dev_get_drvdata(dev); |
176 | |
177 | /* kick the serio layer to rescan this port */ |
178 | serio_reconnect(serio: kmi->io); |
179 | |
180 | return 0; |
181 | } |
182 | |
183 | static DEFINE_SIMPLE_DEV_PM_OPS(amba_kmi_dev_pm_ops, NULL, amba_kmi_resume); |
184 | |
185 | static const struct amba_id amba_kmi_idtable[] = { |
186 | { |
187 | .id = 0x00041050, |
188 | .mask = 0x000fffff, |
189 | }, |
190 | { 0, 0 } |
191 | }; |
192 | |
193 | MODULE_DEVICE_TABLE(amba, amba_kmi_idtable); |
194 | |
195 | static struct amba_driver ambakmi_driver = { |
196 | .drv = { |
197 | .name = "kmi-pl050" , |
198 | .owner = THIS_MODULE, |
199 | .pm = pm_sleep_ptr(&amba_kmi_dev_pm_ops), |
200 | }, |
201 | .id_table = amba_kmi_idtable, |
202 | .probe = amba_kmi_probe, |
203 | .remove = amba_kmi_remove, |
204 | }; |
205 | |
206 | module_amba_driver(ambakmi_driver); |
207 | |
208 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>" ); |
209 | MODULE_DESCRIPTION("AMBA KMI controller driver" ); |
210 | MODULE_LICENSE("GPL" ); |
211 | |