1a6e9118bSGanbold Tsagaankhuu /*-
2a6e9118bSGanbold Tsagaankhuu * Copyright (c) 2016 Ganbold Tsagaankhuu <ganbold@freebsd.org>
3a6e9118bSGanbold Tsagaankhuu * All rights reserved.
4a6e9118bSGanbold Tsagaankhuu *
5a6e9118bSGanbold Tsagaankhuu * Redistribution and use in source and binary forms, with or without
6a6e9118bSGanbold Tsagaankhuu * modification, are permitted provided that the following conditions
7a6e9118bSGanbold Tsagaankhuu * are met:
8a6e9118bSGanbold Tsagaankhuu * 1. Redistributions of source code must retain the above copyright
9a6e9118bSGanbold Tsagaankhuu * notice, this list of conditions and the following disclaimer.
10a6e9118bSGanbold Tsagaankhuu * 2. Redistributions in binary form must reproduce the above copyright
11a6e9118bSGanbold Tsagaankhuu * notice, this list of conditions and the following disclaimer in the
12a6e9118bSGanbold Tsagaankhuu * documentation and/or other materials provided with the distribution.
13a6e9118bSGanbold Tsagaankhuu *
14a6e9118bSGanbold Tsagaankhuu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15a6e9118bSGanbold Tsagaankhuu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16a6e9118bSGanbold Tsagaankhuu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17a6e9118bSGanbold Tsagaankhuu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18a6e9118bSGanbold Tsagaankhuu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19a6e9118bSGanbold Tsagaankhuu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20a6e9118bSGanbold Tsagaankhuu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21a6e9118bSGanbold Tsagaankhuu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22a6e9118bSGanbold Tsagaankhuu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23a6e9118bSGanbold Tsagaankhuu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24a6e9118bSGanbold Tsagaankhuu * SUCH DAMAGE.
25a6e9118bSGanbold Tsagaankhuu */
26a6e9118bSGanbold Tsagaankhuu
27a6e9118bSGanbold Tsagaankhuu /*
28a6e9118bSGanbold Tsagaankhuu * Allwinner Consumer IR controller
29a6e9118bSGanbold Tsagaankhuu */
30a6e9118bSGanbold Tsagaankhuu
31a6e9118bSGanbold Tsagaankhuu #include <sys/param.h>
32a6e9118bSGanbold Tsagaankhuu #include <sys/systm.h>
33a6e9118bSGanbold Tsagaankhuu #include <sys/bus.h>
34a6e9118bSGanbold Tsagaankhuu #include <sys/kernel.h>
35a6e9118bSGanbold Tsagaankhuu #include <sys/module.h>
36a6e9118bSGanbold Tsagaankhuu #include <sys/rman.h>
37a6e9118bSGanbold Tsagaankhuu #include <sys/sysctl.h>
38a6e9118bSGanbold Tsagaankhuu #include <machine/bus.h>
39a6e9118bSGanbold Tsagaankhuu
40a6e9118bSGanbold Tsagaankhuu #include <dev/ofw/openfirm.h>
41a6e9118bSGanbold Tsagaankhuu #include <dev/ofw/ofw_bus.h>
42a6e9118bSGanbold Tsagaankhuu #include <dev/ofw/ofw_bus_subr.h>
43be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
44*1f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
45a6e9118bSGanbold Tsagaankhuu
46a6e9118bSGanbold Tsagaankhuu #include <dev/evdev/input.h>
47a6e9118bSGanbold Tsagaankhuu #include <dev/evdev/evdev.h>
48a6e9118bSGanbold Tsagaankhuu
49a6e9118bSGanbold Tsagaankhuu #define READ(_sc, _r) bus_read_4((_sc)->res[0], (_r))
50a6e9118bSGanbold Tsagaankhuu #define WRITE(_sc, _r, _v) bus_write_4((_sc)->res[0], (_r), (_v))
51a6e9118bSGanbold Tsagaankhuu
52a6e9118bSGanbold Tsagaankhuu /* IR Control */
53a6e9118bSGanbold Tsagaankhuu #define AW_IR_CTL 0x00
54a6e9118bSGanbold Tsagaankhuu /* Global Enable */
55a6e9118bSGanbold Tsagaankhuu #define AW_IR_CTL_GEN (1 << 0)
56a6e9118bSGanbold Tsagaankhuu /* RX enable */
57a6e9118bSGanbold Tsagaankhuu #define AW_IR_CTL_RXEN (1 << 1)
58a6e9118bSGanbold Tsagaankhuu /* CIR mode enable */
59a6e9118bSGanbold Tsagaankhuu #define AW_IR_CTL_MD (1 << 4) | (1 << 5)
60a6e9118bSGanbold Tsagaankhuu
61a6e9118bSGanbold Tsagaankhuu /* RX Config Reg */
62a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXCTL 0x10
63a6e9118bSGanbold Tsagaankhuu /* Pulse Polarity Invert flag */
64a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXCTL_RPPI (1 << 2)
65a6e9118bSGanbold Tsagaankhuu
66a6e9118bSGanbold Tsagaankhuu /* RX Data */
67a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXFIFO 0x20
68a6e9118bSGanbold Tsagaankhuu
69a6e9118bSGanbold Tsagaankhuu /* RX Interrupt Control */
70a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXINT 0x2C
71a6e9118bSGanbold Tsagaankhuu /* RX FIFO Overflow */
72a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXINT_ROI_EN (1 << 0)
73a6e9118bSGanbold Tsagaankhuu /* RX Packet End */
74a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXINT_RPEI_EN (1 << 1)
75a6e9118bSGanbold Tsagaankhuu /* RX FIFO Data Available */
76a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXINT_RAI_EN (1 << 4)
77a6e9118bSGanbold Tsagaankhuu /* RX FIFO available byte level */
78a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXINT_RAL(val) ((val) << 8)
79a6e9118bSGanbold Tsagaankhuu
80a6e9118bSGanbold Tsagaankhuu /* RX Interrupt Status Reg */
81a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXSTA 0x30
82a6e9118bSGanbold Tsagaankhuu /* RX FIFO Get Available Counter */
83a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXSTA_COUNTER(val) (((val) >> 8) & (sc->fifo_size * 2 - 1))
84a6e9118bSGanbold Tsagaankhuu /* Clear all interrupt status */
85a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXSTA_CLEARALL 0xff
86a6e9118bSGanbold Tsagaankhuu
87a6e9118bSGanbold Tsagaankhuu /* IR Sample Configure Reg */
88a6e9118bSGanbold Tsagaankhuu #define AW_IR_CIR 0x34
89c8c3a334SGanbold Tsagaankhuu
90c8c3a334SGanbold Tsagaankhuu /*
91c8c3a334SGanbold Tsagaankhuu * Frequency sample: 23437.5Hz (Cycle: 42.7us)
92c8c3a334SGanbold Tsagaankhuu * Pulse of NEC Remote > 560us
93c8c3a334SGanbold Tsagaankhuu */
94c8c3a334SGanbold Tsagaankhuu /* Filter Threshold = 8 * 42.7 = ~341us < 500us */
95a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXFILT_VAL (((8) & 0x3f) << 2)
96a6e9118bSGanbold Tsagaankhuu /* Idle Threshold = (2 + 1) * 128 * 42.7 = ~16.4ms > 9ms */
97a6e9118bSGanbold Tsagaankhuu #define AW_IR_RXIDLE_VAL (((2) & 0xff) << 8)
98a6e9118bSGanbold Tsagaankhuu
99a6e9118bSGanbold Tsagaankhuu /* Bit 15 - value (pulse/space) */
100a6e9118bSGanbold Tsagaankhuu #define VAL_MASK 0x80
101a6e9118bSGanbold Tsagaankhuu /* Bits 0:14 - sample duration */
102a6e9118bSGanbold Tsagaankhuu #define PERIOD_MASK 0x7f
103a6e9118bSGanbold Tsagaankhuu
104a6e9118bSGanbold Tsagaankhuu /* Clock rate for IR0 or IR1 clock in CIR mode */
105a6e9118bSGanbold Tsagaankhuu #define AW_IR_BASE_CLK 3000000
106a6e9118bSGanbold Tsagaankhuu /* Frequency sample 3MHz/64 = 46875Hz (21.3us) */
107a6e9118bSGanbold Tsagaankhuu #define AW_IR_SAMPLE_64 (0 << 0)
108a6e9118bSGanbold Tsagaankhuu /* Frequency sample 3MHz/128 = 23437.5Hz (42.7us) */
109a6e9118bSGanbold Tsagaankhuu #define AW_IR_SAMPLE_128 (1 << 0)
110a6e9118bSGanbold Tsagaankhuu
111a6e9118bSGanbold Tsagaankhuu #define AW_IR_ERROR_CODE 0xffffffff
112a6e9118bSGanbold Tsagaankhuu #define AW_IR_REPEAT_CODE 0x0
113a6e9118bSGanbold Tsagaankhuu
114a6e9118bSGanbold Tsagaankhuu /* 80 * 42.7 = ~3.4ms, Lead1(4.5ms) > AW_IR_L1_MIN */
115a6e9118bSGanbold Tsagaankhuu #define AW_IR_L1_MIN 80
116a6e9118bSGanbold Tsagaankhuu /* 40 * 42.7 = ~1.7ms, Lead0(4.5ms) Lead0R(2.25ms) > AW_IR_L0_MIN */
117a6e9118bSGanbold Tsagaankhuu #define AW_IR_L0_MIN 40
118a6e9118bSGanbold Tsagaankhuu /* 26 * 42.7 = ~1109us ~= 561 * 2, Pulse < AW_IR_PMAX */
119a6e9118bSGanbold Tsagaankhuu #define AW_IR_PMAX 26
120a6e9118bSGanbold Tsagaankhuu /* 26 * 42.7 = ~1109us ~= 561 * 2, D1 > AW_IR_DMID, D0 <= AW_IR_DMID */
121a6e9118bSGanbold Tsagaankhuu #define AW_IR_DMID 26
122a6e9118bSGanbold Tsagaankhuu /* 53 * 42.7 = ~2263us ~= 561 * 4, D < AW_IR_DMAX */
123a6e9118bSGanbold Tsagaankhuu #define AW_IR_DMAX 53
124a6e9118bSGanbold Tsagaankhuu
125a6e9118bSGanbold Tsagaankhuu /* Active Thresholds */
126852d1357SAndriy Gapon #define AW_IR_ACTIVE_T_VAL AW_IR_L1_MIN
127852d1357SAndriy Gapon #define AW_IR_ACTIVE_T (((AW_IR_ACTIVE_T_VAL - 1) & 0xff) << 16)
128852d1357SAndriy Gapon #define AW_IR_ACTIVE_T_C_VAL 0
129852d1357SAndriy Gapon #define AW_IR_ACTIVE_T_C ((AW_IR_ACTIVE_T_C_VAL & 0xff) << 23)
130a6e9118bSGanbold Tsagaankhuu
131a6e9118bSGanbold Tsagaankhuu /* Code masks */
132a6e9118bSGanbold Tsagaankhuu #define CODE_MASK 0x00ff00ff
133a6e9118bSGanbold Tsagaankhuu #define INV_CODE_MASK 0xff00ff00
134a6e9118bSGanbold Tsagaankhuu #define VALID_CODE_MASK 0x00ff0000
135a6e9118bSGanbold Tsagaankhuu
136012fba46SAndriy Gapon enum {
137012fba46SAndriy Gapon A10_IR = 1,
138012fba46SAndriy Gapon A13_IR,
139012fba46SAndriy Gapon A31_IR,
140012fba46SAndriy Gapon };
141a6e9118bSGanbold Tsagaankhuu
142a6e9118bSGanbold Tsagaankhuu #define AW_IR_RAW_BUF_SIZE 128
143a6e9118bSGanbold Tsagaankhuu
144bce17068SEmmanuel Vadot SYSCTL_NODE(_hw, OID_AUTO, aw_cir, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
145bce17068SEmmanuel Vadot "aw_cir driver");
146bce17068SEmmanuel Vadot
147bce17068SEmmanuel Vadot static int aw_cir_debug = 0;
148bce17068SEmmanuel Vadot SYSCTL_INT(_hw_aw_cir, OID_AUTO, debug, CTLFLAG_RWTUN, &aw_cir_debug, 0,
149bce17068SEmmanuel Vadot "Debug 1=on 0=off");
150bce17068SEmmanuel Vadot
151a6e9118bSGanbold Tsagaankhuu struct aw_ir_softc {
152a6e9118bSGanbold Tsagaankhuu device_t dev;
153a6e9118bSGanbold Tsagaankhuu struct resource *res[2];
154a6e9118bSGanbold Tsagaankhuu void * intrhand;
155a6e9118bSGanbold Tsagaankhuu int fifo_size;
156a6e9118bSGanbold Tsagaankhuu int dcnt; /* Packet Count */
157a6e9118bSGanbold Tsagaankhuu unsigned char buf[AW_IR_RAW_BUF_SIZE];
158a6e9118bSGanbold Tsagaankhuu struct evdev_dev *sc_evdev;
159a6e9118bSGanbold Tsagaankhuu };
160a6e9118bSGanbold Tsagaankhuu
161a6e9118bSGanbold Tsagaankhuu static struct resource_spec aw_ir_spec[] = {
162a6e9118bSGanbold Tsagaankhuu { SYS_RES_MEMORY, 0, RF_ACTIVE },
163a6e9118bSGanbold Tsagaankhuu { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
164a6e9118bSGanbold Tsagaankhuu { -1, 0 }
165a6e9118bSGanbold Tsagaankhuu };
166a6e9118bSGanbold Tsagaankhuu
167a6e9118bSGanbold Tsagaankhuu static struct ofw_compat_data compat_data[] = {
168a6e9118bSGanbold Tsagaankhuu { "allwinner,sun4i-a10-ir", A10_IR },
169a6e9118bSGanbold Tsagaankhuu { "allwinner,sun5i-a13-ir", A13_IR },
170012fba46SAndriy Gapon { "allwinner,sun6i-a31-ir", A31_IR },
171a6e9118bSGanbold Tsagaankhuu { NULL, 0 }
172a6e9118bSGanbold Tsagaankhuu };
173a6e9118bSGanbold Tsagaankhuu
174a6e9118bSGanbold Tsagaankhuu static void
aw_ir_buf_reset(struct aw_ir_softc * sc)175a6e9118bSGanbold Tsagaankhuu aw_ir_buf_reset(struct aw_ir_softc *sc)
176a6e9118bSGanbold Tsagaankhuu {
177a6e9118bSGanbold Tsagaankhuu
178a6e9118bSGanbold Tsagaankhuu sc->dcnt = 0;
179a6e9118bSGanbold Tsagaankhuu }
180a6e9118bSGanbold Tsagaankhuu
181a6e9118bSGanbold Tsagaankhuu static void
aw_ir_buf_write(struct aw_ir_softc * sc,unsigned char data)182a6e9118bSGanbold Tsagaankhuu aw_ir_buf_write(struct aw_ir_softc *sc, unsigned char data)
183a6e9118bSGanbold Tsagaankhuu {
184a6e9118bSGanbold Tsagaankhuu
185a6e9118bSGanbold Tsagaankhuu if (sc->dcnt < AW_IR_RAW_BUF_SIZE)
186a6e9118bSGanbold Tsagaankhuu sc->buf[sc->dcnt++] = data;
187a6e9118bSGanbold Tsagaankhuu else
188a6e9118bSGanbold Tsagaankhuu if (bootverbose)
189a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev, "IR RX Buffer Full!\n");
190a6e9118bSGanbold Tsagaankhuu }
191a6e9118bSGanbold Tsagaankhuu
192a6e9118bSGanbold Tsagaankhuu static int
aw_ir_buf_full(struct aw_ir_softc * sc)193a6e9118bSGanbold Tsagaankhuu aw_ir_buf_full(struct aw_ir_softc *sc)
194a6e9118bSGanbold Tsagaankhuu {
195a6e9118bSGanbold Tsagaankhuu
196a6e9118bSGanbold Tsagaankhuu return (sc->dcnt >= AW_IR_RAW_BUF_SIZE);
197a6e9118bSGanbold Tsagaankhuu }
198a6e9118bSGanbold Tsagaankhuu
199a6e9118bSGanbold Tsagaankhuu static unsigned char
aw_ir_read_data(struct aw_ir_softc * sc)200a6e9118bSGanbold Tsagaankhuu aw_ir_read_data(struct aw_ir_softc *sc)
201a6e9118bSGanbold Tsagaankhuu {
202a6e9118bSGanbold Tsagaankhuu
203a6e9118bSGanbold Tsagaankhuu return (unsigned char)(READ(sc, AW_IR_RXFIFO) & 0xff);
204a6e9118bSGanbold Tsagaankhuu }
205a6e9118bSGanbold Tsagaankhuu
206a6e9118bSGanbold Tsagaankhuu static unsigned long
aw_ir_decode_packets(struct aw_ir_softc * sc)207a6e9118bSGanbold Tsagaankhuu aw_ir_decode_packets(struct aw_ir_softc *sc)
208a6e9118bSGanbold Tsagaankhuu {
209d9fe3aedSAndriy Gapon unsigned int len, code;
210a6e9118bSGanbold Tsagaankhuu unsigned int active_delay;
211d9fe3aedSAndriy Gapon unsigned char val, last;
212a6e9118bSGanbold Tsagaankhuu int i, bitcount;
213a6e9118bSGanbold Tsagaankhuu
214bce17068SEmmanuel Vadot if (bootverbose && __predict_false(aw_cir_debug) != 0)
215a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev, "sc->dcnt = %d\n", sc->dcnt);
216a6e9118bSGanbold Tsagaankhuu
217a6e9118bSGanbold Tsagaankhuu /* Find Lead 1 (bit separator) */
218852d1357SAndriy Gapon active_delay = AW_IR_ACTIVE_T_VAL *
219852d1357SAndriy Gapon (AW_IR_ACTIVE_T_C_VAL != 0 ? 128 : 1);
220852d1357SAndriy Gapon len = active_delay;
221bce17068SEmmanuel Vadot if (bootverbose && __predict_false(aw_cir_debug) != 0)
222d9fe3aedSAndriy Gapon device_printf(sc->dev, "Initial len: %d\n", len);
223a6e9118bSGanbold Tsagaankhuu for (i = 0; i < sc->dcnt; i++) {
224a6e9118bSGanbold Tsagaankhuu val = sc->buf[i];
225a6e9118bSGanbold Tsagaankhuu if (val & VAL_MASK)
226d9fe3aedSAndriy Gapon len += (val & PERIOD_MASK) + 1;
227a6e9118bSGanbold Tsagaankhuu else {
228a6e9118bSGanbold Tsagaankhuu if (len > AW_IR_L1_MIN)
229a6e9118bSGanbold Tsagaankhuu break;
230a6e9118bSGanbold Tsagaankhuu len = 0;
231a6e9118bSGanbold Tsagaankhuu }
232a6e9118bSGanbold Tsagaankhuu }
233bce17068SEmmanuel Vadot if (bootverbose && __predict_false(aw_cir_debug) != 0)
234d9fe3aedSAndriy Gapon device_printf(sc->dev, "len = %d\n", len);
235a6e9118bSGanbold Tsagaankhuu if ((val & VAL_MASK) || (len <= AW_IR_L1_MIN)) {
236bce17068SEmmanuel Vadot if (bootverbose && __predict_false(aw_cir_debug) != 0)
237a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev, "Bit separator error\n");
238a6e9118bSGanbold Tsagaankhuu goto error_code;
239a6e9118bSGanbold Tsagaankhuu }
240a6e9118bSGanbold Tsagaankhuu
241a6e9118bSGanbold Tsagaankhuu /* Find Lead 0 (bit length) */
242a6e9118bSGanbold Tsagaankhuu len = 0;
243a6e9118bSGanbold Tsagaankhuu for (; i < sc->dcnt; i++) {
244a6e9118bSGanbold Tsagaankhuu val = sc->buf[i];
245a6e9118bSGanbold Tsagaankhuu if (val & VAL_MASK) {
246a6e9118bSGanbold Tsagaankhuu if(len > AW_IR_L0_MIN)
247a6e9118bSGanbold Tsagaankhuu break;
248a6e9118bSGanbold Tsagaankhuu len = 0;
249a6e9118bSGanbold Tsagaankhuu } else
250d9fe3aedSAndriy Gapon len += (val & PERIOD_MASK) + 1;
251a6e9118bSGanbold Tsagaankhuu }
252a6e9118bSGanbold Tsagaankhuu if ((!(val & VAL_MASK)) || (len <= AW_IR_L0_MIN)) {
253bce17068SEmmanuel Vadot if (bootverbose && __predict_false(aw_cir_debug) != 0)
254a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev, "Bit length error\n");
255a6e9118bSGanbold Tsagaankhuu goto error_code;
256a6e9118bSGanbold Tsagaankhuu }
257a6e9118bSGanbold Tsagaankhuu
258a6e9118bSGanbold Tsagaankhuu /* Start decoding */
259a6e9118bSGanbold Tsagaankhuu code = 0;
260a6e9118bSGanbold Tsagaankhuu bitcount = 0;
261a6e9118bSGanbold Tsagaankhuu last = 1;
262a6e9118bSGanbold Tsagaankhuu len = 0;
263a6e9118bSGanbold Tsagaankhuu for (; i < sc->dcnt; i++) {
264a6e9118bSGanbold Tsagaankhuu val = sc->buf[i];
265a6e9118bSGanbold Tsagaankhuu if (last) {
266a6e9118bSGanbold Tsagaankhuu if (val & VAL_MASK)
267d9fe3aedSAndriy Gapon len += (val & PERIOD_MASK) + 1;
268a6e9118bSGanbold Tsagaankhuu else {
269a6e9118bSGanbold Tsagaankhuu if (len > AW_IR_PMAX) {
270a6e9118bSGanbold Tsagaankhuu if (bootverbose)
271a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev,
272d9fe3aedSAndriy Gapon "Pulse error, len=%d\n",
273d9fe3aedSAndriy Gapon len);
274a6e9118bSGanbold Tsagaankhuu goto error_code;
275a6e9118bSGanbold Tsagaankhuu }
276a6e9118bSGanbold Tsagaankhuu last = 0;
277d9fe3aedSAndriy Gapon len = (val & PERIOD_MASK) + 1;
278a6e9118bSGanbold Tsagaankhuu }
279a6e9118bSGanbold Tsagaankhuu } else {
280a6e9118bSGanbold Tsagaankhuu if (val & VAL_MASK) {
281a6e9118bSGanbold Tsagaankhuu if (len > AW_IR_DMAX) {
282a6e9118bSGanbold Tsagaankhuu if (bootverbose)
283a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev,
284d9fe3aedSAndriy Gapon "Distance error, len=%d\n",
285d9fe3aedSAndriy Gapon len);
286a6e9118bSGanbold Tsagaankhuu goto error_code;
287a6e9118bSGanbold Tsagaankhuu } else {
288a6e9118bSGanbold Tsagaankhuu if (len > AW_IR_DMID) {
289a6e9118bSGanbold Tsagaankhuu /* Decode */
290a6e9118bSGanbold Tsagaankhuu code |= 1 << bitcount;
291a6e9118bSGanbold Tsagaankhuu }
292a6e9118bSGanbold Tsagaankhuu bitcount++;
293a6e9118bSGanbold Tsagaankhuu if (bitcount == 32)
294a6e9118bSGanbold Tsagaankhuu break; /* Finish decoding */
295a6e9118bSGanbold Tsagaankhuu }
296a6e9118bSGanbold Tsagaankhuu last = 1;
297d9fe3aedSAndriy Gapon len = (val & PERIOD_MASK) + 1;
298a6e9118bSGanbold Tsagaankhuu } else
299d9fe3aedSAndriy Gapon len += (val & PERIOD_MASK) + 1;
300a6e9118bSGanbold Tsagaankhuu }
301a6e9118bSGanbold Tsagaankhuu }
302a6e9118bSGanbold Tsagaankhuu return (code);
303a6e9118bSGanbold Tsagaankhuu
304a6e9118bSGanbold Tsagaankhuu error_code:
305a6e9118bSGanbold Tsagaankhuu
306a6e9118bSGanbold Tsagaankhuu return (AW_IR_ERROR_CODE);
307a6e9118bSGanbold Tsagaankhuu }
308a6e9118bSGanbold Tsagaankhuu
309a6e9118bSGanbold Tsagaankhuu static int
aw_ir_validate_code(unsigned long code)310a6e9118bSGanbold Tsagaankhuu aw_ir_validate_code(unsigned long code)
311a6e9118bSGanbold Tsagaankhuu {
312a6e9118bSGanbold Tsagaankhuu unsigned long v1, v2;
313a6e9118bSGanbold Tsagaankhuu
314a6e9118bSGanbold Tsagaankhuu /* Don't check address */
315a6e9118bSGanbold Tsagaankhuu v1 = code & CODE_MASK;
316a6e9118bSGanbold Tsagaankhuu v2 = (code & INV_CODE_MASK) >> 8;
317a6e9118bSGanbold Tsagaankhuu
318a6e9118bSGanbold Tsagaankhuu if (((v1 ^ v2) & VALID_CODE_MASK) == VALID_CODE_MASK)
319a6e9118bSGanbold Tsagaankhuu return (0); /* valid */
320a6e9118bSGanbold Tsagaankhuu else
321a6e9118bSGanbold Tsagaankhuu return (1); /* invalid */
322a6e9118bSGanbold Tsagaankhuu }
323a6e9118bSGanbold Tsagaankhuu
324a6e9118bSGanbold Tsagaankhuu static void
aw_ir_intr(void * arg)325a6e9118bSGanbold Tsagaankhuu aw_ir_intr(void *arg)
326a6e9118bSGanbold Tsagaankhuu {
327a6e9118bSGanbold Tsagaankhuu struct aw_ir_softc *sc;
328a6e9118bSGanbold Tsagaankhuu uint32_t val;
329a6e9118bSGanbold Tsagaankhuu int i, dcnt;
330a6e9118bSGanbold Tsagaankhuu unsigned long ir_code;
331a6e9118bSGanbold Tsagaankhuu int stat;
332a6e9118bSGanbold Tsagaankhuu
333a6e9118bSGanbold Tsagaankhuu sc = (struct aw_ir_softc *)arg;
334a6e9118bSGanbold Tsagaankhuu
335a6e9118bSGanbold Tsagaankhuu /* Read RX interrupt status */
336a6e9118bSGanbold Tsagaankhuu val = READ(sc, AW_IR_RXSTA);
337bce17068SEmmanuel Vadot if (bootverbose && __predict_false(aw_cir_debug) != 0)
338c8c3a334SGanbold Tsagaankhuu device_printf(sc->dev, "RX interrupt status: %x\n", val);
339a6e9118bSGanbold Tsagaankhuu
340a6e9118bSGanbold Tsagaankhuu /* Clean all pending interrupt statuses */
341a6e9118bSGanbold Tsagaankhuu WRITE(sc, AW_IR_RXSTA, val | AW_IR_RXSTA_CLEARALL);
342a6e9118bSGanbold Tsagaankhuu
343a6e9118bSGanbold Tsagaankhuu /* When Rx FIFO Data available or Packet end */
344a6e9118bSGanbold Tsagaankhuu if (val & (AW_IR_RXINT_RAI_EN | AW_IR_RXINT_RPEI_EN)) {
345bce17068SEmmanuel Vadot if (bootverbose && __predict_false(aw_cir_debug) != 0)
346c8c3a334SGanbold Tsagaankhuu device_printf(sc->dev,
347c8c3a334SGanbold Tsagaankhuu "RX FIFO Data available or Packet end\n");
348a6e9118bSGanbold Tsagaankhuu /* Get available message count in RX FIFO */
349a6e9118bSGanbold Tsagaankhuu dcnt = AW_IR_RXSTA_COUNTER(val);
350a6e9118bSGanbold Tsagaankhuu /* Read FIFO */
351a6e9118bSGanbold Tsagaankhuu for (i = 0; i < dcnt; i++) {
352a6e9118bSGanbold Tsagaankhuu if (aw_ir_buf_full(sc)) {
353a6e9118bSGanbold Tsagaankhuu if (bootverbose)
354a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev,
355a6e9118bSGanbold Tsagaankhuu "raw buffer full\n");
356a6e9118bSGanbold Tsagaankhuu break;
357a6e9118bSGanbold Tsagaankhuu } else
358a6e9118bSGanbold Tsagaankhuu aw_ir_buf_write(sc, aw_ir_read_data(sc));
359a6e9118bSGanbold Tsagaankhuu }
360a6e9118bSGanbold Tsagaankhuu }
361a6e9118bSGanbold Tsagaankhuu
362a6e9118bSGanbold Tsagaankhuu if (val & AW_IR_RXINT_RPEI_EN) {
363a6e9118bSGanbold Tsagaankhuu /* RX Packet end */
364bce17068SEmmanuel Vadot if (bootverbose && __predict_false(aw_cir_debug) != 0)
365a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev, "RX Packet end\n");
366a6e9118bSGanbold Tsagaankhuu ir_code = aw_ir_decode_packets(sc);
367a6e9118bSGanbold Tsagaankhuu stat = aw_ir_validate_code(ir_code);
368a6e9118bSGanbold Tsagaankhuu if (stat == 0) {
369a6e9118bSGanbold Tsagaankhuu evdev_push_event(sc->sc_evdev,
370a6e9118bSGanbold Tsagaankhuu EV_MSC, MSC_SCAN, ir_code);
371a6e9118bSGanbold Tsagaankhuu evdev_sync(sc->sc_evdev);
372a6e9118bSGanbold Tsagaankhuu }
373bce17068SEmmanuel Vadot if (bootverbose && __predict_false(aw_cir_debug) != 0) {
374a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev, "Final IR code: %lx\n",
375a6e9118bSGanbold Tsagaankhuu ir_code);
376a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev, "IR code status: %d\n",
377a6e9118bSGanbold Tsagaankhuu stat);
378a6e9118bSGanbold Tsagaankhuu }
3798b616b26SAndriy Gapon aw_ir_buf_reset(sc);
380a6e9118bSGanbold Tsagaankhuu }
381a6e9118bSGanbold Tsagaankhuu if (val & AW_IR_RXINT_ROI_EN) {
382a6e9118bSGanbold Tsagaankhuu /* RX FIFO overflow */
383a6e9118bSGanbold Tsagaankhuu if (bootverbose)
384a6e9118bSGanbold Tsagaankhuu device_printf(sc->dev, "RX FIFO overflow\n");
385a6e9118bSGanbold Tsagaankhuu /* Flush raw buffer */
386a6e9118bSGanbold Tsagaankhuu aw_ir_buf_reset(sc);
387a6e9118bSGanbold Tsagaankhuu }
388a6e9118bSGanbold Tsagaankhuu }
389a6e9118bSGanbold Tsagaankhuu
390a6e9118bSGanbold Tsagaankhuu static int
aw_ir_probe(device_t dev)391a6e9118bSGanbold Tsagaankhuu aw_ir_probe(device_t dev)
392a6e9118bSGanbold Tsagaankhuu {
393a6e9118bSGanbold Tsagaankhuu
394a6e9118bSGanbold Tsagaankhuu if (!ofw_bus_status_okay(dev))
395a6e9118bSGanbold Tsagaankhuu return (ENXIO);
396a6e9118bSGanbold Tsagaankhuu
397a6e9118bSGanbold Tsagaankhuu if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
398a6e9118bSGanbold Tsagaankhuu return (ENXIO);
399a6e9118bSGanbold Tsagaankhuu
400a6e9118bSGanbold Tsagaankhuu device_set_desc(dev, "Allwinner CIR controller");
401a6e9118bSGanbold Tsagaankhuu return (BUS_PROBE_DEFAULT);
402a6e9118bSGanbold Tsagaankhuu }
403a6e9118bSGanbold Tsagaankhuu
404a6e9118bSGanbold Tsagaankhuu static int
aw_ir_attach(device_t dev)405a6e9118bSGanbold Tsagaankhuu aw_ir_attach(device_t dev)
406a6e9118bSGanbold Tsagaankhuu {
407a6e9118bSGanbold Tsagaankhuu struct aw_ir_softc *sc;
408a6e9118bSGanbold Tsagaankhuu hwreset_t rst_apb;
409a6e9118bSGanbold Tsagaankhuu clk_t clk_ir, clk_gate;
410a6e9118bSGanbold Tsagaankhuu int err;
411a6e9118bSGanbold Tsagaankhuu uint32_t val = 0;
412a6e9118bSGanbold Tsagaankhuu
413a6e9118bSGanbold Tsagaankhuu clk_ir = clk_gate = NULL;
41473e62e8cSEmmanuel Vadot rst_apb = NULL;
415a6e9118bSGanbold Tsagaankhuu
416a6e9118bSGanbold Tsagaankhuu sc = device_get_softc(dev);
417a6e9118bSGanbold Tsagaankhuu sc->dev = dev;
418a6e9118bSGanbold Tsagaankhuu
419a6e9118bSGanbold Tsagaankhuu if (bus_alloc_resources(dev, aw_ir_spec, sc->res) != 0) {
420a6e9118bSGanbold Tsagaankhuu device_printf(dev, "could not allocate memory resource\n");
421a6e9118bSGanbold Tsagaankhuu return (ENXIO);
422a6e9118bSGanbold Tsagaankhuu }
423a6e9118bSGanbold Tsagaankhuu
424a6e9118bSGanbold Tsagaankhuu switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
425a6e9118bSGanbold Tsagaankhuu case A10_IR:
426a6e9118bSGanbold Tsagaankhuu sc->fifo_size = 16;
427a6e9118bSGanbold Tsagaankhuu break;
428a6e9118bSGanbold Tsagaankhuu case A13_IR:
429012fba46SAndriy Gapon case A31_IR:
430a6e9118bSGanbold Tsagaankhuu sc->fifo_size = 64;
431a6e9118bSGanbold Tsagaankhuu break;
432a6e9118bSGanbold Tsagaankhuu }
433a6e9118bSGanbold Tsagaankhuu
434a6e9118bSGanbold Tsagaankhuu /* De-assert reset */
43556578489SGanbold Tsagaankhuu if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst_apb) == 0) {
436a6e9118bSGanbold Tsagaankhuu err = hwreset_deassert(rst_apb);
437a6e9118bSGanbold Tsagaankhuu if (err != 0) {
438a6e9118bSGanbold Tsagaankhuu device_printf(dev, "cannot de-assert reset\n");
439a6e9118bSGanbold Tsagaankhuu goto error;
440a6e9118bSGanbold Tsagaankhuu }
441a6e9118bSGanbold Tsagaankhuu }
442a6e9118bSGanbold Tsagaankhuu
443a6e9118bSGanbold Tsagaankhuu /* Reset buffer */
444a6e9118bSGanbold Tsagaankhuu aw_ir_buf_reset(sc);
445a6e9118bSGanbold Tsagaankhuu
446a6e9118bSGanbold Tsagaankhuu /* Get clocks and enable them */
447a6e9118bSGanbold Tsagaankhuu err = clk_get_by_ofw_name(dev, 0, "apb", &clk_gate);
448a6e9118bSGanbold Tsagaankhuu if (err != 0) {
449a6e9118bSGanbold Tsagaankhuu device_printf(dev, "Cannot get gate clock\n");
450a6e9118bSGanbold Tsagaankhuu goto error;
451a6e9118bSGanbold Tsagaankhuu }
452a6e9118bSGanbold Tsagaankhuu err = clk_get_by_ofw_name(dev, 0, "ir", &clk_ir);
453a6e9118bSGanbold Tsagaankhuu if (err != 0) {
454a6e9118bSGanbold Tsagaankhuu device_printf(dev, "Cannot get IR clock\n");
455a6e9118bSGanbold Tsagaankhuu goto error;
456a6e9118bSGanbold Tsagaankhuu }
457a6e9118bSGanbold Tsagaankhuu /* Set clock rate */
458a6e9118bSGanbold Tsagaankhuu err = clk_set_freq(clk_ir, AW_IR_BASE_CLK, 0);
459a6e9118bSGanbold Tsagaankhuu if (err != 0) {
460a6e9118bSGanbold Tsagaankhuu device_printf(dev, "cannot set IR clock rate\n");
461a6e9118bSGanbold Tsagaankhuu goto error;
462a6e9118bSGanbold Tsagaankhuu }
463a6e9118bSGanbold Tsagaankhuu /* Enable clocks */
464a6e9118bSGanbold Tsagaankhuu err = clk_enable(clk_gate);
465a6e9118bSGanbold Tsagaankhuu if (err != 0) {
466a6e9118bSGanbold Tsagaankhuu device_printf(dev, "Cannot enable clk gate\n");
467a6e9118bSGanbold Tsagaankhuu goto error;
468a6e9118bSGanbold Tsagaankhuu }
469a6e9118bSGanbold Tsagaankhuu err = clk_enable(clk_ir);
470a6e9118bSGanbold Tsagaankhuu if (err != 0) {
471a6e9118bSGanbold Tsagaankhuu device_printf(dev, "Cannot enable IR clock\n");
472a6e9118bSGanbold Tsagaankhuu goto error;
473a6e9118bSGanbold Tsagaankhuu }
474a6e9118bSGanbold Tsagaankhuu
475a6e9118bSGanbold Tsagaankhuu if (bus_setup_intr(dev, sc->res[1],
476a6e9118bSGanbold Tsagaankhuu INTR_TYPE_MISC | INTR_MPSAFE, NULL, aw_ir_intr, sc,
477a6e9118bSGanbold Tsagaankhuu &sc->intrhand)) {
478a6e9118bSGanbold Tsagaankhuu bus_release_resources(dev, aw_ir_spec, sc->res);
479a6e9118bSGanbold Tsagaankhuu device_printf(dev, "cannot setup interrupt handler\n");
4808b616b26SAndriy Gapon err = ENXIO;
4818b616b26SAndriy Gapon goto error;
482a6e9118bSGanbold Tsagaankhuu }
483a6e9118bSGanbold Tsagaankhuu
484a6e9118bSGanbold Tsagaankhuu /* Enable CIR Mode */
485a6e9118bSGanbold Tsagaankhuu WRITE(sc, AW_IR_CTL, AW_IR_CTL_MD);
486a6e9118bSGanbold Tsagaankhuu
487a6e9118bSGanbold Tsagaankhuu /*
488a6e9118bSGanbold Tsagaankhuu * Set clock sample, filter, idle thresholds.
489a6e9118bSGanbold Tsagaankhuu * Frequency sample = 3MHz/128 = 23437.5Hz (42.7us)
490a6e9118bSGanbold Tsagaankhuu */
491a6e9118bSGanbold Tsagaankhuu val = AW_IR_SAMPLE_128;
492a6e9118bSGanbold Tsagaankhuu val |= (AW_IR_RXFILT_VAL | AW_IR_RXIDLE_VAL);
493a6e9118bSGanbold Tsagaankhuu val |= (AW_IR_ACTIVE_T | AW_IR_ACTIVE_T_C);
494a6e9118bSGanbold Tsagaankhuu WRITE(sc, AW_IR_CIR, val);
495a6e9118bSGanbold Tsagaankhuu
496a6e9118bSGanbold Tsagaankhuu /* Invert Input Signal */
497a6e9118bSGanbold Tsagaankhuu WRITE(sc, AW_IR_RXCTL, AW_IR_RXCTL_RPPI);
498a6e9118bSGanbold Tsagaankhuu
499a6e9118bSGanbold Tsagaankhuu /* Clear All RX Interrupt Status */
500a6e9118bSGanbold Tsagaankhuu WRITE(sc, AW_IR_RXSTA, AW_IR_RXSTA_CLEARALL);
501a6e9118bSGanbold Tsagaankhuu
502a6e9118bSGanbold Tsagaankhuu /*
503a6e9118bSGanbold Tsagaankhuu * Enable RX interrupt in case of overflow, packet end
504a6e9118bSGanbold Tsagaankhuu * and FIFO available.
505a6e9118bSGanbold Tsagaankhuu * RX FIFO Threshold = FIFO size / 2
506a6e9118bSGanbold Tsagaankhuu */
507a6e9118bSGanbold Tsagaankhuu WRITE(sc, AW_IR_RXINT, AW_IR_RXINT_ROI_EN | AW_IR_RXINT_RPEI_EN |
508a6e9118bSGanbold Tsagaankhuu AW_IR_RXINT_RAI_EN | AW_IR_RXINT_RAL((sc->fifo_size >> 1) - 1));
509a6e9118bSGanbold Tsagaankhuu
510a6e9118bSGanbold Tsagaankhuu /* Enable IR Module */
511a6e9118bSGanbold Tsagaankhuu val = READ(sc, AW_IR_CTL);
512a6e9118bSGanbold Tsagaankhuu WRITE(sc, AW_IR_CTL, val | AW_IR_CTL_GEN | AW_IR_CTL_RXEN);
513a6e9118bSGanbold Tsagaankhuu
514a6e9118bSGanbold Tsagaankhuu sc->sc_evdev = evdev_alloc();
515a6e9118bSGanbold Tsagaankhuu evdev_set_name(sc->sc_evdev, device_get_desc(sc->dev));
516a6e9118bSGanbold Tsagaankhuu evdev_set_phys(sc->sc_evdev, device_get_nameunit(sc->dev));
517a6e9118bSGanbold Tsagaankhuu evdev_set_id(sc->sc_evdev, BUS_HOST, 0, 0, 0);
518a6e9118bSGanbold Tsagaankhuu evdev_support_event(sc->sc_evdev, EV_SYN);
519a6e9118bSGanbold Tsagaankhuu evdev_support_event(sc->sc_evdev, EV_MSC);
520a6e9118bSGanbold Tsagaankhuu evdev_support_msc(sc->sc_evdev, MSC_SCAN);
521a6e9118bSGanbold Tsagaankhuu
522a6e9118bSGanbold Tsagaankhuu err = evdev_register(sc->sc_evdev);
523a6e9118bSGanbold Tsagaankhuu if (err) {
524a6e9118bSGanbold Tsagaankhuu device_printf(dev,
525a6e9118bSGanbold Tsagaankhuu "failed to register evdev: error=%d\n", err);
526a6e9118bSGanbold Tsagaankhuu goto error;
527a6e9118bSGanbold Tsagaankhuu }
528a6e9118bSGanbold Tsagaankhuu
529a6e9118bSGanbold Tsagaankhuu return (0);
530a6e9118bSGanbold Tsagaankhuu error:
531a6e9118bSGanbold Tsagaankhuu if (clk_gate != NULL)
532a6e9118bSGanbold Tsagaankhuu clk_release(clk_gate);
533a6e9118bSGanbold Tsagaankhuu if (clk_ir != NULL)
534a6e9118bSGanbold Tsagaankhuu clk_release(clk_ir);
535a6e9118bSGanbold Tsagaankhuu if (rst_apb != NULL)
536a6e9118bSGanbold Tsagaankhuu hwreset_release(rst_apb);
537a6e9118bSGanbold Tsagaankhuu evdev_free(sc->sc_evdev);
538a6e9118bSGanbold Tsagaankhuu sc->sc_evdev = NULL; /* Avoid double free */
539a6e9118bSGanbold Tsagaankhuu
540a6e9118bSGanbold Tsagaankhuu bus_release_resources(dev, aw_ir_spec, sc->res);
541a6e9118bSGanbold Tsagaankhuu return (ENXIO);
542a6e9118bSGanbold Tsagaankhuu }
543a6e9118bSGanbold Tsagaankhuu
544a6e9118bSGanbold Tsagaankhuu static device_method_t aw_ir_methods[] = {
545a6e9118bSGanbold Tsagaankhuu DEVMETHOD(device_probe, aw_ir_probe),
546a6e9118bSGanbold Tsagaankhuu DEVMETHOD(device_attach, aw_ir_attach),
547a6e9118bSGanbold Tsagaankhuu
548a6e9118bSGanbold Tsagaankhuu DEVMETHOD_END
549a6e9118bSGanbold Tsagaankhuu };
550a6e9118bSGanbold Tsagaankhuu
551a6e9118bSGanbold Tsagaankhuu static driver_t aw_ir_driver = {
552a6e9118bSGanbold Tsagaankhuu "aw_ir",
553a6e9118bSGanbold Tsagaankhuu aw_ir_methods,
554a6e9118bSGanbold Tsagaankhuu sizeof(struct aw_ir_softc),
555a6e9118bSGanbold Tsagaankhuu };
556a6e9118bSGanbold Tsagaankhuu
5577e1e2ba1SJohn Baldwin DRIVER_MODULE(aw_ir, simplebus, aw_ir_driver, 0, 0);
558a6e9118bSGanbold Tsagaankhuu MODULE_DEPEND(aw_ir, evdev, 1, 1, 1);
559