source: rtems-libbsd/rtemsbsd/sys/dev/usb/controller/ohci_lpc.c @ 9f2205a

55-freebsd-126-freebsd-12
Last change on this file since 9f2205a was 9f2205a, checked in by Kevin Kirspel <kevin-kirspel@…>, on 01/30/17 at 16:58:16

Port LPC32XX Ethernet and USB OHCI to RTEMS

  • Property mode set to 100755
File size: 11.9 KB
Line 
1/*
2 * Copyright (c) 2009, 2013 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <rtems@embedded-brains.de>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <machine/rtems-bsd-kernel-space.h>
33#include <machine/rtems-bsd-support.h>
34
35#include <bsp.h>
36
37#include <errno.h>
38
39#if defined(LIBBSP_ARM_LPC24XX_BSP_H)
40
41#include <bsp/irq.h>
42#include <bsp/io.h>
43#include <bsp/lpc24xx.h>
44
45#define LPC_USB_OHCI_BASE USBHC_BASE_ADDR
46
47#define LPC_USB_I2C_BASE USBOTG_I2C_BASE_ADDR
48
49#define LPC_OTG_CLK_CTRL OTG_CLK_CTRL
50
51#define LPC_OTG_CLK_STAT OTG_CLK_STAT
52
53#define LPC_USB_OHCI_IRQ LPC24XX_IRQ_USB
54
55static void lpc_usb_module_enable(void)
56{
57        rtems_status_code sc;
58
59        sc = lpc24xx_module_enable(
60                LPC24XX_MODULE_USB,
61                LPC24XX_MODULE_PCLK_DEFAULT
62        );
63        BSD_ASSERT_SC(sc);
64}
65
66static void lpc_usb_module_disable(void)
67{
68        rtems_status_code sc;
69
70        sc = lpc24xx_module_disable(LPC24XX_MODULE_USB);
71        BSD_ASSERT_SC(sc);
72}
73
74static void lpc_usb_pin_config(void)
75{
76        static const lpc24xx_pin_range pins [] = {
77                LPC24XX_PIN_USB_D_PLUS_1,
78                LPC24XX_PIN_USB_D_MINUS_1,
79                LPC24XX_PIN_USB_PPWR_1,
80                LPC24XX_PIN_USB_SCL_1,
81                LPC24XX_PIN_USB_SDA_1,
82                LPC24XX_PIN_TERMINAL
83        };
84
85        rtems_status_code sc;
86
87        sc = lpc24xx_pin_config(&pins [0], LPC24XX_PIN_SET_FUNCTION);
88        BSD_ASSERT_SC(sc);
89}
90
91static void lpc_usb_host_clock_enable(void)
92{
93        /* Nothing to do */
94}
95
96static void lpc_otg_status_and_control(void)
97{
98        OTG_STAT_CTRL = 0x3;
99}
100
101static rtems_interval lpc_usb_timeout_init(void)
102{
103        return rtems_clock_get_ticks_since_boot();
104}
105
106static bool lpc_usb_timeout_not_expired(rtems_interval start)
107{
108        rtems_interval elapsed = rtems_clock_get_ticks_since_boot() - start;
109
110        return elapsed < rtems_clock_get_ticks_per_second() / 10;
111}
112
113#define LPC_OTG_CLK_HOST BSP_BIT32(0)
114#define LPC_OTG_CLK_DEV BSP_BIT32(1)
115#define LPC_OTG_CLK_I2C BSP_BIT32(2)
116#define LPC_OTG_CLK_OTG BSP_BIT32(3)
117#define LPC_OTG_CLK_AHB BSP_BIT32(4)
118
119static int lpc_otg_clk_ctrl(uint32_t otg_clk_ctrl)
120{
121        rtems_interval start;
122        bool not_ok;
123
124        LPC_OTG_CLK_CTRL = otg_clk_ctrl;
125
126        start = lpc_usb_timeout_init();
127        while (
128                (not_ok = (LPC_OTG_CLK_STAT & otg_clk_ctrl) != otg_clk_ctrl)
129                        && lpc_usb_timeout_not_expired(start)
130        ) {
131                /* Wait */
132        }
133
134        return not_ok ? EIO : 0;
135}
136
137#include <sys/cdefs.h>
138#include <sys/stdint.h>
139#include <sys/stddef.h>
140#include <rtems/bsd/sys/param.h>
141#include <sys/queue.h>
142#include <sys/types.h>
143#include <sys/systm.h>
144#include <sys/kernel.h>
145#include <sys/bus.h>
146#include <sys/linker_set.h>
147#include <sys/module.h>
148#include <rtems/bsd/sys/lock.h>
149#include <sys/mutex.h>
150#include <sys/condvar.h>
151#include <sys/sysctl.h>
152#include <sys/sx.h>
153#include <sys/unistd.h>
154#include <sys/callout.h>
155#include <sys/malloc.h>
156#include <sys/priv.h>
157
158#include <dev/usb/usb.h>
159#include <dev/usb/usbdi.h>
160
161#include <dev/usb/usb_core.h>
162#include <dev/usb/usb_busdma.h>
163#include <dev/usb/usb_process.h>
164#include <dev/usb/usb_util.h>
165
166#include <dev/usb/usb_controller.h>
167#include <dev/usb/usb_bus.h>
168#include <dev/usb/controller/ohci.h>
169
170#ifdef BSP_USB_OTG_TRANSCEIVER_I2C_ADDR
171
172#define I2C_CTL_SRST (1U << 8)
173
174#define I2C_TX_DATA_MASK 0xffU
175#define I2C_TX_ADDR_SHIFT 1
176#define I2C_TX_ADDR_MASK 0x7fU
177#define I2C_TX_READ (1U << 0)
178#define I2C_TX_START (1U << 8)
179#define I2C_TX_STOP (1U << 9)
180
181#define I2C_STS_TDI (1U << 0)
182#define I2C_STS_AFI (1U << 1)
183#define I2C_STS_NAI (1U << 2)
184#define I2C_STS_RFE (1U << 9)
185
186typedef struct {
187        uint32_t rx_tx;
188        uint32_t sts;
189        uint32_t ctl;
190        uint32_t clkhi;
191        uint32_t clklo;
192} i2c_regs;
193
194static volatile i2c_regs *i2c =
195        (volatile i2c_regs *) LPC_USB_I2C_BASE;
196
197static int i2c_wait_for_receive_fifo_not_empty(void)
198{
199        rtems_interval start;
200        bool not_ok;
201
202        start = lpc_usb_timeout_init();
203        while (
204                (not_ok = (i2c->sts & I2C_STS_RFE) != 0)
205                        && lpc_usb_timeout_not_expired(start)
206        ) {
207                /* Wait */
208        }
209
210        return not_ok ? EIO : 0;
211}
212
213static int i2c_wait_for_transaction_done(void)
214{
215        rtems_interval start;
216        bool not_ok;
217
218        start = lpc_usb_timeout_init();
219        while (
220                (not_ok = (i2c->sts & I2C_STS_TDI) == 0)
221                        && lpc_usb_timeout_not_expired(start)
222        ) {
223                /* Wait */
224        }
225
226        return not_ok ? EIO : 0;
227}
228
229static int i2c_read(
230        const struct usb_otg_transceiver *self,
231        uint8_t reg_addr,
232        uint8_t *value
233)
234{
235        int eno;
236
237        i2c->ctl = I2C_CTL_SRST;
238
239        i2c->rx_tx = self->i2c_addr | I2C_TX_START;
240        i2c->rx_tx = reg_addr;
241        i2c->rx_tx = self->i2c_addr | I2C_TX_READ | I2C_TX_START;
242        i2c->rx_tx = I2C_TX_STOP;
243
244        eno = i2c_wait_for_receive_fifo_not_empty();
245
246        if (eno == 0) {
247                *value = (int) i2c->rx_tx;
248        }
249
250        return eno;
251}
252
253static int i2c_write(
254        const struct usb_otg_transceiver *self,
255        uint8_t reg_addr,
256        uint8_t value
257)
258{
259        int eno;
260
261        i2c->ctl = I2C_CTL_SRST;
262        i2c->sts = I2C_STS_TDI;
263
264        i2c->rx_tx = self->i2c_addr | I2C_TX_START;
265        i2c->rx_tx = reg_addr;
266        i2c->rx_tx = value | I2C_TX_STOP;
267
268        eno = i2c_wait_for_transaction_done();
269
270        return eno;
271}
272
273#endif /* BSP_USB_OTG_TRANSCEIVER_I2C_ADDR */
274
275static device_probe_t ohci_lpc_probe;
276static device_attach_t ohci_lpc_attach;
277static device_detach_t ohci_lpc_detach;
278static device_resume_t ohci_lpc_resume;
279
280static int
281ohci_lpc_otg_transceiver_suspend(ohci_softc_t *e)
282{
283        int eno = 0;
284
285#ifdef BSP_USB_OTG_TRANSCEIVER_I2C_ADDR
286        if (eno == 0) {
287                eno = lpc_otg_clk_ctrl(
288                        LPC_OTG_CLK_AHB | LPC_OTG_CLK_HOST | LPC_OTG_CLK_I2C
289                );
290        }
291
292        if (eno == 0) {
293                eno = usb_otg_transceiver_suspend(&e->sc_otg_trans);
294        }
295
296#ifdef BSP_USB_OTG_TRANSCEIVER_DUMP
297        usb_otg_transceiver_dump(&e->sc_otg_trans);
298#endif /* BSP_USB_OTG_TRANSCEIVER_DUMP */
299
300        if (eno == 0) {
301                eno = lpc_otg_clk_ctrl(LPC_OTG_CLK_AHB | LPC_OTG_CLK_HOST);
302        }
303#endif /* BSP_USB_OTG_TRANSCEIVER_I2C_ADDR */
304
305        return eno;
306}
307
308static int
309ohci_lpc_resume(device_t self)
310{
311        int eno = 0;
312
313#ifdef BSP_USB_OTG_TRANSCEIVER_I2C_ADDR
314        if (eno == 0) {
315                eno = lpc_otg_clk_ctrl(
316                        LPC_OTG_CLK_AHB | LPC_OTG_CLK_HOST | LPC_OTG_CLK_I2C
317                );
318        }
319
320        if (eno == 0) {
321                eno = usb_otg_transceiver_resume(&e->sc_otg_trans);
322        }
323
324#ifdef BSP_USB_OTG_TRANSCEIVER_VBUS
325        if (eno == 0) {
326                eno = usb_otg_transceiver_set_vbus(
327                        &e->sc_otg_trans,
328                        BSP_USB_OTG_TRANSCEIVER_VBUS
329                );
330        }
331#endif /* BSP_USB_OTG_TRANSCEIVER_VBUS */
332
333#ifdef BSP_USB_OTG_TRANSCEIVER_DUMP
334        usb_otg_transceiver_dump(&e->sc_otg_trans);
335#endif /* BSP_USB_OTG_TRANSCEIVER_DUMP */
336
337        if (eno == 0) {
338                eno = lpc_otg_clk_ctrl(LPC_OTG_CLK_AHB | LPC_OTG_CLK_HOST);
339        }
340#endif /* BSP_USB_OTG_TRANSCEIVER_I2C_ADDR */
341
342        if (eno == 0) {
343                eno = bus_generic_resume(self);
344        }
345
346        return (eno);
347}
348
349static int
350ohci_lpc_probe(device_t self)
351{
352        device_set_desc(self, "LPC OHCI controller");
353
354        return (0);
355}
356
357static int
358ohci_lpc_attach(device_t self)
359{
360        rtems_status_code sc = RTEMS_SUCCESSFUL;
361        ohci_softc_t *e = device_get_softc(self);
362        usb_error_t ue = USB_ERR_NORMAL_COMPLETION;
363        int eno = 0;
364
365        memset(e, 0, sizeof(*e));
366
367        /* Initialize some bus fields */
368        e->sc_bus.parent = self;
369        e->sc_bus.devices = e->sc_devices;
370        e->sc_bus.devices_max = OHCI_MAX_DEVICES;
371        e->sc_bus.dma_bits = 32;
372
373        /* Get all DMA memory */
374        if (usb_bus_mem_alloc_all(&e->sc_bus, USB_GET_DMA_TAG(self), &ohci_iterate_hw_softc)) {
375                return (ENOMEM);
376        }
377        e->sc_dev = self;
378
379        /* Child device */
380        e->sc_bus.bdev = device_add_child(self, "usbus", -1);
381        if (e->sc_bus.bdev == NULL) {
382                device_printf(self, "Could not add USB device\n");
383                goto error;
384        }
385        device_set_ivars(e->sc_bus.bdev, &e->sc_bus);
386        device_set_desc(e->sc_bus.bdev, "LPC OHCI bus");
387        snprintf(e->sc_vendor, sizeof(e->sc_vendor), "NXP");
388
389        /* Register space */
390        e->sc_io_tag = 0U;
391        e->sc_io_hdl = LPC_USB_OHCI_BASE;
392        e->sc_io_size = 0x5cU;
393
394        lpc_usb_module_enable();
395
396        eno = lpc_otg_clk_ctrl(LPC_OTG_CLK_AHB | LPC_OTG_CLK_I2C);
397        if (eno != 0) {
398                goto error;
399        }
400
401        lpc_usb_pin_config();
402
403#ifdef BSP_USB_OTG_TRANSCEIVER_I2C_ADDR
404        e->sc_otg_trans.read = i2c_read;
405        e->sc_otg_trans.write = i2c_write;
406        e->sc_otg_trans.i2c_addr = BSP_USB_OTG_TRANSCEIVER_I2C_ADDR;
407        eno = usb_otg_transceiver_init(&e->sc_otg_trans);
408        if (eno != 0) {
409                goto error;
410        }
411
412#ifdef BSP_USB_OTG_TRANSCEIVER_DUMP
413        usb_otg_transceiver_dump(&e->sc_otg_trans);
414#endif /* BSP_USB_OTG_TRANSCEIVER_DUMP */
415
416        eno = usb_otg_transceiver_resume(&e->sc_otg_trans);
417        if (eno != 0) {
418                goto error;
419        }
420#endif /* BSP_USB_OTG_TRANSCEIVER_I2C_ADDR */
421
422        lpc_usb_host_clock_enable();
423
424        eno = lpc_otg_clk_ctrl(
425                LPC_OTG_CLK_AHB | LPC_OTG_CLK_HOST
426                        | LPC_OTG_CLK_I2C | LPC_OTG_CLK_OTG
427        );
428        if (eno != 0) {
429                goto error;
430        }
431
432        lpc_otg_status_and_control();
433
434#if defined(BSP_USB_OTG_TRANSCEIVER_I2C_ADDR) \
435        && defined(BSP_USB_OTG_TRANSCEIVER_VBUS)
436        eno = usb_otg_transceiver_set_vbus(
437                &e->sc_otg_trans,
438                BSP_USB_OTG_TRANSCEIVER_VBUS
439        );
440        if (eno != 0) {
441                goto error;
442        }
443#endif /* defined(BSP_USB_OTG_TRANSCEIVER_I2C_ADDR)
444          && defined(BSP_USB_OTG_TRANSCEIVER_VBUS) */
445
446#if defined(BSP_USB_OTG_TRANSCEIVER_I2C_ADDR) \
447        && defined(BSP_USB_OTG_TRANSCEIVER_DUMP)
448        usb_otg_transceiver_dump(&e->sc_otg_trans);
449#endif /* defined(BSP_USB_OTG_TRANSCEIVER_I2C_ADDR)
450          && defined(BSP_USB_OTG_TRANSCEIVER_DUMP) */
451
452        eno = lpc_otg_clk_ctrl(LPC_OTG_CLK_AHB | LPC_OTG_CLK_HOST);
453        if (eno != 0) {
454                goto error;
455        }
456
457        /* Install interrupt handler */
458        sc = rtems_interrupt_server_handler_install(
459                RTEMS_ID_NONE,
460                LPC_USB_OHCI_IRQ,
461                "USB",
462                RTEMS_INTERRUPT_UNIQUE,
463                (rtems_interrupt_handler) ohci_interrupt,
464                e
465        );
466        BSD_ASSERT_SC(sc);
467
468        /* OHCI intitialization */
469        ue = ohci_init(e);
470        if (ue != USB_ERR_NORMAL_COMPLETION) {
471                goto error;
472        }
473        e->sc_init_done = 1;
474
475        /* Probe and attach child */
476        eno = device_probe_and_attach(e->sc_bus.bdev);
477        if (eno != 0) {
478                goto error;
479        }
480
481        return (0);
482
483error:
484        ohci_lpc_detach(self);
485
486        return (ENXIO);
487}
488
489static int
490ohci_lpc_detach(device_t self)
491{
492        rtems_status_code sc = RTEMS_SUCCESSFUL;
493        ohci_softc_t *e = device_get_softc(self);
494
495        if (e->sc_bus.bdev) {
496                device_t bdev = e->sc_bus.bdev;
497
498                device_detach(bdev);
499                device_delete_child(self, bdev);
500        }
501
502        device_delete_children(self);
503
504        if (e->sc_init_done) {
505                ohci_detach(e);
506        }
507
508        sc = rtems_interrupt_server_handler_remove(
509                RTEMS_ID_NONE,
510                LPC_USB_OHCI_IRQ,
511                (rtems_interrupt_handler) ohci_interrupt,
512                e
513        );
514        BSD_ASSERT_SC(sc);
515
516        ohci_lpc_otg_transceiver_suspend(e);
517
518        lpc_otg_clk_ctrl(0);
519
520        lpc_usb_module_disable();
521
522        usb_bus_mem_free_all(&e->sc_bus, &ohci_iterate_hw_softc);
523
524        return (0);
525}
526
527static device_method_t ohci_methods [] = {
528        /* Device interface */
529        DEVMETHOD(device_probe, ohci_lpc_probe),
530        DEVMETHOD(device_attach, ohci_lpc_attach),
531        DEVMETHOD(device_detach, ohci_lpc_detach),
532        DEVMETHOD(device_suspend, bus_generic_suspend),
533        DEVMETHOD(device_resume, ohci_lpc_resume),
534        DEVMETHOD(device_shutdown, bus_generic_shutdown),
535
536        /* Bus interface */
537        DEVMETHOD(bus_print_child, bus_generic_print_child),
538
539        {0, 0}
540};
541
542static driver_t ohci_driver = {
543        .name = "ohci",
544        .methods = ohci_methods,
545        .size = sizeof(struct ohci_softc)
546};
547
548static devclass_t ohci_devclass;
549
550DRIVER_MODULE(ohci, nexus, ohci_driver, ohci_devclass, 0, 0);
551MODULE_DEPEND(ohci, usb, 1, 1, 1);
552
553#endif /* defined(LIBBSP_ARM_LPC24XX_BSP_H) */
Note: See TracBrowser for help on using the repository browser.