at91dci_atmelarm.c revision 266170
10Sduke#include <sys/cdefs.h>
20Sduke__FBSDID("$FreeBSD: stable/10/sys/dev/usb/controller/at91dci_atmelarm.c 266170 2014-05-15 18:38:19Z ian $");
30Sduke
46038Skshefov/*-
50Sduke * Copyright (c) 2007-2008 Hans Petter Selasky. All rights reserved.
60Sduke *
70Sduke * Redistribution and use in source and binary forms, with or without
80Sduke * modification, are permitted provided that the following conditions
90Sduke * are met:
100Sduke * 1. Redistributions of source code must retain the above copyright
110Sduke *    notice, this list of conditions and the following disclaimer.
120Sduke * 2. Redistributions in binary form must reproduce the above copyright
130Sduke *    notice, this list of conditions and the following disclaimer in the
140Sduke *    documentation and/or other materials provided with the distribution.
150Sduke *
160Sduke * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
170Sduke * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
180Sduke * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
190Sduke * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
200Sduke * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212362Sohair * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222362Sohair * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232362Sohair * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
240Sduke * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
250Sduke * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
260Sduke * SUCH DAMAGE.
270Sduke */
2815235Sgoetz
290Sduke#include <sys/stdint.h>
300Sduke#include <sys/stddef.h>
310Sduke#include <sys/param.h>
320Sduke#include <sys/queue.h>
330Sduke#include <sys/types.h>
340Sduke#include <sys/systm.h>
350Sduke#include <sys/kernel.h>
360Sduke#include <sys/bus.h>
370Sduke#include <sys/module.h>
380Sduke#include <sys/lock.h>
390Sduke#include <sys/mutex.h>
400Sduke#include <sys/condvar.h>
410Sduke#include <sys/sysctl.h>
420Sduke#include <sys/sx.h>
436038Skshefov#include <sys/unistd.h>
440Sduke#include <sys/callout.h>
450Sduke#include <sys/malloc.h>
460Sduke#include <sys/priv.h>
470Sduke
480Sduke#include <dev/usb/usb.h>
490Sduke#include <dev/usb/usbdi.h>
500Sduke
510Sduke#include <dev/usb/usb_core.h>
526038Skshefov#include <dev/usb/usb_busdma.h>
530Sduke#include <dev/usb/usb_process.h>
540Sduke#include <dev/usb/usb_util.h>
550Sduke
560Sduke#include <dev/usb/usb_controller.h>
570Sduke#include <dev/usb/usb_bus.h>
580Sduke#include <dev/usb/controller/at91dci.h>
590Sduke
600Sduke#include <sys/rman.h>
610Sduke
620Sduke#include <arm/at91/at91_pmcvar.h>
630Sduke#include <arm/at91/at91rm92reg.h>
640Sduke#include <arm/at91/at91_pioreg.h>
650Sduke#include <arm/at91/at91_piovar.h>
660Sduke
670Sduke#define	MEM_RID	0
686038Skshefov
690Sduke/* Pin Definitions - do they belong here or somewhere else ? -- YES! */
706038Skshefov
716038Skshefov#define	VBUS_MASK	AT91C_PIO_PB24
720Sduke#define	VBUS_BASE	AT91RM92_PIOB_BASE
730Sduke
740Sduke#define	PULLUP_MASK	AT91C_PIO_PB22
750Sduke#define	PULLUP_BASE	AT91RM92_PIOB_BASE
766038Skshefov
770Sdukestatic device_probe_t at91_udp_probe;
786038Skshefovstatic device_attach_t at91_udp_attach;
796038Skshefovstatic device_detach_t at91_udp_detach;
806038Skshefov
816038Skshefovstruct at91_udp_softc {
826038Skshefov	struct at91dci_softc sc_dci;	/* must be first */
836038Skshefov	struct at91_pmc_clock *sc_mclk;
846038Skshefov	struct at91_pmc_clock *sc_iclk;
856038Skshefov	struct at91_pmc_clock *sc_fclk;
866038Skshefov	struct callout sc_vbus;
876038Skshefov};
880Sduke
890Sdukestatic void
906038Skshefovat91_vbus_poll(struct at91_udp_softc *sc)
910Sduke{
926038Skshefov	uint8_t vbus_val;
930Sduke
946038Skshefov	vbus_val = at91_pio_gpio_get(VBUS_BASE, VBUS_MASK) != 0;
956038Skshefov	at91dci_vbus_interrupt(&sc->sc_dci, vbus_val);
966038Skshefov
976038Skshefov	callout_reset(&sc->sc_vbus, hz, (void *)&at91_vbus_poll, sc);
986038Skshefov}
996038Skshefov
1006038Skshefovstatic void
1016038Skshefovat91_udp_clocks_on(void *arg)
1026038Skshefov{
1036038Skshefov	struct at91_udp_softc *sc = arg;
1040Sduke
1050Sduke	at91_pmc_clock_enable(sc->sc_mclk);
1069195Ssimonis	at91_pmc_clock_enable(sc->sc_iclk);
1079195Ssimonis	at91_pmc_clock_enable(sc->sc_fclk);
1089195Ssimonis}
1099195Ssimonis
1109195Ssimonisstatic void
1119195Ssimonisat91_udp_clocks_off(void *arg)
1129195Ssimonis{
1139195Ssimonis	struct at91_udp_softc *sc = arg;
1140Sduke
1150Sduke	at91_pmc_clock_disable(sc->sc_fclk);
1160Sduke	at91_pmc_clock_disable(sc->sc_iclk);
1170Sduke	at91_pmc_clock_disable(sc->sc_mclk);
1180Sduke}
1190Sduke
1200Sdukestatic void
1210Sdukeat91_udp_pull_up(void *arg)
1226038Skshefov{
1236038Skshefov	at91_pio_gpio_set(PULLUP_BASE, PULLUP_MASK);
1240Sduke}
1250Sduke
1260Sdukestatic void
1270Sdukeat91_udp_pull_down(void *arg)
1280Sduke{
1290Sduke	at91_pio_gpio_clear(PULLUP_BASE, PULLUP_MASK);
1300Sduke}
1310Sduke
1320Sdukestatic int
1330Sdukeat91_udp_probe(device_t dev)
1340Sduke{
1350Sduke	device_set_desc(dev, "AT91 integrated AT91_UDP controller");
1360Sduke	return (0);
1370Sduke}
1386038Skshefov
1390Sdukestatic int
1400Sdukeat91_udp_attach(device_t dev)
1410Sduke{
1420Sduke	struct at91_udp_softc *sc = device_get_softc(dev);
1430Sduke	int err;
1440Sduke	int rid;
1450Sduke
1460Sduke	/* setup AT9100 USB device controller interface softc */
1476038Skshefov
1486038Skshefov	sc->sc_dci.sc_clocks_on = &at91_udp_clocks_on;
1490Sduke	sc->sc_dci.sc_clocks_off = &at91_udp_clocks_off;
1500Sduke	sc->sc_dci.sc_clocks_arg = sc;
1510Sduke	sc->sc_dci.sc_pull_up = &at91_udp_pull_up;
1526038Skshefov	sc->sc_dci.sc_pull_down = &at91_udp_pull_down;
1530Sduke	sc->sc_dci.sc_pull_arg = sc;
1540Sduke
1556038Skshefov	/* initialise some bus fields */
1560Sduke	sc->sc_dci.sc_bus.parent = dev;
1570Sduke	sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices;
1586038Skshefov	sc->sc_dci.sc_bus.devices_max = AT91_MAX_DEVICES;
1590Sduke
1600Sduke	/* get all DMA memory */
1610Sduke	if (usb_bus_mem_alloc_all(&sc->sc_dci.sc_bus,
1626038Skshefov	    USB_GET_DMA_TAG(dev), NULL)) {
1630Sduke		return (ENOMEM);
1646038Skshefov	}
1656038Skshefov	callout_init_mtx(&sc->sc_vbus, &sc->sc_dci.sc_bus.bus_mtx, 0);
1660Sduke
1670Sduke	/*
1680Sduke	 * configure VBUS input pin, enable deglitch and enable
1690Sduke	 * interrupt :
1700Sduke	 */
1710Sduke	at91_pio_use_gpio(VBUS_BASE, VBUS_MASK);
1720Sduke	at91_pio_gpio_input(VBUS_BASE, VBUS_MASK);
1730Sduke	at91_pio_gpio_set_deglitch(VBUS_BASE, VBUS_MASK, 1);
1740Sduke	at91_pio_gpio_set_interrupt(VBUS_BASE, VBUS_MASK, 0);
1750Sduke
1760Sduke	/*
1770Sduke	 * configure PULLUP output pin :
1780Sduke	 */
1799285Salanb	at91_pio_use_gpio(PULLUP_BASE, PULLUP_MASK);
1800Sduke	at91_pio_gpio_output(PULLUP_BASE, PULLUP_MASK, 0);
1810Sduke
1820Sduke	at91_udp_pull_down(sc);
1830Sduke
1840Sduke	/* wait 10ms for pulldown to stabilise */
1850Sduke	usb_pause_mtx(NULL, hz / 100);
1860Sduke
1870Sduke	sc->sc_mclk = at91_pmc_clock_ref("mck");
1880Sduke	sc->sc_iclk = at91_pmc_clock_ref("udc_clk");
1890Sduke	sc->sc_fclk = at91_pmc_clock_ref("udpck");
1900Sduke
1910Sduke	rid = MEM_RID;
1920Sduke	sc->sc_dci.sc_io_res =
1930Sduke	    bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
1940Sduke
1950Sduke	if (!(sc->sc_dci.sc_io_res)) {
196		err = ENOMEM;
197		goto error;
198	}
199	sc->sc_dci.sc_io_tag = rman_get_bustag(sc->sc_dci.sc_io_res);
200	sc->sc_dci.sc_io_hdl = rman_get_bushandle(sc->sc_dci.sc_io_res);
201	sc->sc_dci.sc_io_size = rman_get_size(sc->sc_dci.sc_io_res);
202
203	rid = 0;
204	sc->sc_dci.sc_irq_res =
205	    bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
206	if (!(sc->sc_dci.sc_irq_res)) {
207		goto error;
208	}
209	sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
210	if (!(sc->sc_dci.sc_bus.bdev)) {
211		goto error;
212	}
213	device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus);
214
215	err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
216	    NULL, (driver_intr_t *)at91dci_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
217	if (err) {
218		sc->sc_dci.sc_intr_hdl = NULL;
219		goto error;
220	}
221
222	err = at91dci_init(&sc->sc_dci);
223	if (!err) {
224		err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev);
225	}
226	if (err) {
227		goto error;
228	} else {
229		/* poll VBUS one time */
230		USB_BUS_LOCK(&sc->sc_dci.sc_bus);
231		at91_vbus_poll(sc);
232		USB_BUS_UNLOCK(&sc->sc_dci.sc_bus);
233	}
234	return (0);
235
236error:
237	at91_udp_detach(dev);
238	return (ENXIO);
239}
240
241static int
242at91_udp_detach(device_t dev)
243{
244	struct at91_udp_softc *sc = device_get_softc(dev);
245	device_t bdev;
246	int err;
247
248	if (sc->sc_dci.sc_bus.bdev) {
249		bdev = sc->sc_dci.sc_bus.bdev;
250		device_detach(bdev);
251		device_delete_child(dev, bdev);
252	}
253	/* during module unload there are lots of children leftover */
254	device_delete_children(dev);
255
256	USB_BUS_LOCK(&sc->sc_dci.sc_bus);
257	callout_stop(&sc->sc_vbus);
258	USB_BUS_UNLOCK(&sc->sc_dci.sc_bus);
259
260	callout_drain(&sc->sc_vbus);
261
262	/* disable Transceiver */
263	AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS);
264
265	/* disable and clear all interrupts */
266	AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_IDR, 0xFFFFFFFF);
267	AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_ICR, 0xFFFFFFFF);
268
269	if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) {
270		/*
271		 * only call at91_udp_uninit() after at91_udp_init()
272		 */
273		at91dci_uninit(&sc->sc_dci);
274
275		err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res,
276		    sc->sc_dci.sc_intr_hdl);
277		sc->sc_dci.sc_intr_hdl = NULL;
278	}
279	if (sc->sc_dci.sc_irq_res) {
280		bus_release_resource(dev, SYS_RES_IRQ, 0,
281		    sc->sc_dci.sc_irq_res);
282		sc->sc_dci.sc_irq_res = NULL;
283	}
284	if (sc->sc_dci.sc_io_res) {
285		bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID,
286		    sc->sc_dci.sc_io_res);
287		sc->sc_dci.sc_io_res = NULL;
288	}
289	usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL);
290
291	/* disable clocks */
292	at91_pmc_clock_disable(sc->sc_iclk);
293	at91_pmc_clock_disable(sc->sc_fclk);
294	at91_pmc_clock_disable(sc->sc_mclk);
295	at91_pmc_clock_deref(sc->sc_fclk);
296	at91_pmc_clock_deref(sc->sc_iclk);
297	at91_pmc_clock_deref(sc->sc_mclk);
298
299	return (0);
300}
301
302static device_method_t at91_udp_methods[] = {
303	/* Device interface */
304	DEVMETHOD(device_probe, at91_udp_probe),
305	DEVMETHOD(device_attach, at91_udp_attach),
306	DEVMETHOD(device_detach, at91_udp_detach),
307	DEVMETHOD(device_suspend, bus_generic_suspend),
308	DEVMETHOD(device_resume, bus_generic_resume),
309	DEVMETHOD(device_shutdown, bus_generic_shutdown),
310
311	DEVMETHOD_END
312};
313
314static driver_t at91_udp_driver = {
315	.name = "at91_udp",
316	.methods = at91_udp_methods,
317	.size = sizeof(struct at91_udp_softc),
318};
319
320static devclass_t at91_udp_devclass;
321
322DRIVER_MODULE(at91_udp, atmelarm, at91_udp_driver, at91_udp_devclass, 0, 0);
323