diff options
Diffstat (limited to 'drivers/usb/gadget/udc')
-rw-r--r-- | drivers/usb/gadget/udc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/at91_udc.c | 525 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/at91_udc.h | 9 |
3 files changed, 281 insertions, 254 deletions
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index b8e213eb36cc..366e551aeff0 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -32,6 +32,7 @@ menu "USB Peripheral Controller" config USB_AT91 tristate "Atmel AT91 USB Device Port" depends on ARCH_AT91 + depends on OF || COMPILE_TEST help Many Atmel AT91 processors (such as the AT91RM2000) have a full speed USB Device Port with support for five configurable diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index c0ec5f71f16f..2fbedca3c2b4 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -31,16 +31,9 @@ #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/platform_data/atmel.h> - -#include <asm/byteorder.h> -#include <mach/hardware.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/gpio.h> - -#include <mach/cpu.h> -#include <mach/at91sam9261_matrix.h> -#include <mach/at91_matrix.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/atmel-matrix.h> #include "at91_udc.h" @@ -66,7 +59,15 @@ #define DRIVER_VERSION "3 May 2006" static const char driver_name [] = "at91_udc"; -static const char ep0name[] = "ep0"; +static const char * const ep_names[] = { + "ep0", + "ep1", + "ep2", + "ep3-int", + "ep4", + "ep5", +}; +#define ep0name ep_names[0] #define VBUS_POLL_TIMEOUT msecs_to_jiffies(1000) @@ -895,8 +896,6 @@ static void clk_on(struct at91_udc *udc) return; udc->clocked = 1; - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_enable(udc->uclk); clk_enable(udc->iclk); clk_enable(udc->fclk); } @@ -909,8 +908,6 @@ static void clk_off(struct at91_udc *udc) udc->gadget.speed = USB_SPEED_UNKNOWN; clk_disable(udc->fclk); clk_disable(udc->iclk); - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_disable(udc->uclk); } /* @@ -919,8 +916,6 @@ static void clk_off(struct at91_udc *udc) */ static void pullup(struct at91_udc *udc, int is_on) { - int active = !udc->board.pullup_active_low; - if (!udc->enabled || !udc->vbus) is_on = 0; DBG("%sactive\n", is_on ? "" : "in"); @@ -929,40 +924,15 @@ static void pullup(struct at91_udc *udc, int is_on) clk_on(udc); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_TXVC, 0); - if (cpu_is_at91rm9200()) - gpio_set_value(udc->board.pullup_pin, active); - else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) { - u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); - - txvc |= AT91_UDP_TXVC_PUON; - at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { - u32 usbpucr; - - usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR); - usbpucr |= AT91_MATRIX_USBPUCR_PUON; - at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr); - } } else { stop_activity(udc); at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); - if (cpu_is_at91rm9200()) - gpio_set_value(udc->board.pullup_pin, !active); - else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) { - u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); - - txvc &= ~AT91_UDP_TXVC_PUON; - at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { - u32 usbpucr; - - usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR); - usbpucr &= ~AT91_MATRIX_USBPUCR_PUON; - at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr); - } clk_off(udc); } + + if (udc->caps && udc->caps->pullup) + udc->caps->pullup(udc, is_on); } /* vbus is here! turn everything on that's ready */ @@ -1535,74 +1505,6 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) /*-------------------------------------------------------------------------*/ -static struct at91_udc controller = { - .gadget = { - .ops = &at91_udc_ops, - .ep0 = &controller.ep[0].ep, - .name = driver_name, - }, - .ep[0] = { - .ep = { - .name = ep0name, - .ops = &at91_ep_ops, - }, - .udc = &controller, - .maxpacket = 8, - .int_mask = 1 << 0, - }, - .ep[1] = { - .ep = { - .name = "ep1", - .ops = &at91_ep_ops, - }, - .udc = &controller, - .is_pingpong = 1, - .maxpacket = 64, - .int_mask = 1 << 1, - }, - .ep[2] = { - .ep = { - .name = "ep2", - .ops = &at91_ep_ops, - }, - .udc = &controller, - .is_pingpong = 1, - .maxpacket = 64, - .int_mask = 1 << 2, - }, - .ep[3] = { - .ep = { - /* could actually do bulk too */ - .name = "ep3-int", - .ops = &at91_ep_ops, - }, - .udc = &controller, - .maxpacket = 8, - .int_mask = 1 << 3, - }, - .ep[4] = { - .ep = { - .name = "ep4", - .ops = &at91_ep_ops, - }, - .udc = &controller, - .is_pingpong = 1, - .maxpacket = 256, - .int_mask = 1 << 4, - }, - .ep[5] = { - .ep = { - .name = "ep5", - .ops = &at91_ep_ops, - }, - .udc = &controller, - .is_pingpong = 1, - .maxpacket = 256, - .int_mask = 1 << 5, - }, - /* ep6 and ep7 are also reserved (custom silicon might use them) */ -}; - static void at91_vbus_update(struct at91_udc *udc, unsigned value) { value ^= udc->board.vbus_active_low; @@ -1687,12 +1589,202 @@ static void at91udc_shutdown(struct platform_device *dev) spin_unlock_irqrestore(&udc->lock, flags); } -static void at91udc_of_init(struct at91_udc *udc, - struct device_node *np) +static int at91rm9200_udc_init(struct at91_udc *udc) +{ + struct at91_ep *ep; + int ret; + int i; + + for (i = 0; i < NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + + switch (i) { + case 0: + case 3: + ep->maxpacket = 8; + break; + case 1 ... 2: + ep->maxpacket = 64; + break; + case 4 ... 5: + ep->maxpacket = 256; + break; + } + } + + if (!gpio_is_valid(udc->board.pullup_pin)) { + DBG("no D+ pullup?\n"); + return -ENODEV; + } + + ret = devm_gpio_request(&udc->pdev->dev, udc->board.pullup_pin, + "udc_pullup"); + if (ret) { + DBG("D+ pullup is busy\n"); + return ret; + } + + gpio_direction_output(udc->board.pullup_pin, + udc->board.pullup_active_low); + + return 0; +} + +static void at91rm9200_udc_pullup(struct at91_udc *udc, int is_on) +{ + int active = !udc->board.pullup_active_low; + + if (is_on) + gpio_set_value(udc->board.pullup_pin, active); + else + gpio_set_value(udc->board.pullup_pin, !active); +} + +static const struct at91_udc_caps at91rm9200_udc_caps = { + .init = at91rm9200_udc_init, + .pullup = at91rm9200_udc_pullup, +}; + +static int at91sam9260_udc_init(struct at91_udc *udc) +{ + struct at91_ep *ep; + int i; + + for (i = 0; i < NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + + switch (i) { + case 0 ... 3: + ep->maxpacket = 64; + break; + case 4 ... 5: + ep->maxpacket = 512; + break; + } + } + + return 0; +} + +static void at91sam9260_udc_pullup(struct at91_udc *udc, int is_on) +{ + u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); + + if (is_on) + txvc |= AT91_UDP_TXVC_PUON; + else + txvc &= ~AT91_UDP_TXVC_PUON; + + at91_udp_write(udc, AT91_UDP_TXVC, txvc); +} + +static const struct at91_udc_caps at91sam9260_udc_caps = { + .init = at91sam9260_udc_init, + .pullup = at91sam9260_udc_pullup, +}; + +static int at91sam9261_udc_init(struct at91_udc *udc) +{ + struct at91_ep *ep; + int i; + + for (i = 0; i < NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + + switch (i) { + case 0: + ep->maxpacket = 8; + break; + case 1 ... 3: + ep->maxpacket = 64; + break; + case 4 ... 5: + ep->maxpacket = 256; + break; + } + } + + udc->matrix = syscon_regmap_lookup_by_phandle(udc->pdev->dev.of_node, + "atmel,matrix"); + if (IS_ERR(udc->matrix)) + return PTR_ERR(udc->matrix); + + return 0; +} + +static void at91sam9261_udc_pullup(struct at91_udc *udc, int is_on) +{ + u32 usbpucr = 0; + + if (is_on) + usbpucr = AT91_MATRIX_USBPUCR_PUON; + + regmap_update_bits(udc->matrix, AT91SAM9261_MATRIX_USBPUCR, + AT91_MATRIX_USBPUCR_PUON, usbpucr); +} + +static const struct at91_udc_caps at91sam9261_udc_caps = { + .init = at91sam9261_udc_init, + .pullup = at91sam9261_udc_pullup, +}; + +static int at91sam9263_udc_init(struct at91_udc *udc) +{ + struct at91_ep *ep; + int i; + + for (i = 0; i < NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + + switch (i) { + case 0: + case 1: + case 2: + case 3: + ep->maxpacket = 64; + break; + case 4: + case 5: + ep->maxpacket = 256; + break; + } + } + + return 0; +} + +static const struct at91_udc_caps at91sam9263_udc_caps = { + .init = at91sam9263_udc_init, + .pullup = at91sam9260_udc_pullup, +}; + +static const struct of_device_id at91_udc_dt_ids[] = { + { + .compatible = "atmel,at91rm9200-udc", + .data = &at91rm9200_udc_caps, + }, + { + .compatible = "atmel,at91sam9260-udc", + .data = &at91sam9260_udc_caps, + }, + { + .compatible = "atmel,at91sam9261-udc", + .data = &at91sam9261_udc_caps, + }, + { + .compatible = "atmel,at91sam9263-udc", + .data = &at91sam9263_udc_caps, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, at91_udc_dt_ids); + +static void at91udc_of_init(struct at91_udc *udc, struct device_node *np) { struct at91_udc_data *board = &udc->board; - u32 val; + const struct of_device_id *match; enum of_gpio_flags flags; + u32 val; if (of_property_read_u32(np, "atmel,vbus-polled", &val) == 0) board->vbus_polled = 1; @@ -1705,6 +1797,10 @@ static void at91udc_of_init(struct at91_udc *udc, &flags); board->pullup_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; + + match = of_match_node(at91_udc_dt_ids, np); + if (match) + udc->caps = match->data; } static int at91udc_probe(struct platform_device *pdev) @@ -1713,97 +1809,67 @@ static int at91udc_probe(struct platform_device *pdev) struct at91_udc *udc; int retval; struct resource *res; + struct at91_ep *ep; + int i; - if (!dev_get_platdata(dev) && !pdev->dev.of_node) { - /* small (so we copy it) but critical! */ - DBG("missing platform_data\n"); - return -ENODEV; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - - if (!request_mem_region(res->start, resource_size(res), driver_name)) { - DBG("someone's using UDC memory\n"); - return -EBUSY; - } + udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL); + if (!udc) + return -ENOMEM; /* init software state */ - udc = &controller; udc->gadget.dev.parent = dev; - if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) - at91udc_of_init(udc, pdev->dev.of_node); - else - memcpy(&udc->board, dev_get_platdata(dev), - sizeof(struct at91_udc_data)); + at91udc_of_init(udc, pdev->dev.of_node); udc->pdev = pdev; udc->enabled = 0; spin_lock_init(&udc->lock); - /* rm9200 needs manual D+ pullup; off by default */ - if (cpu_is_at91rm9200()) { - if (!gpio_is_valid(udc->board.pullup_pin)) { - DBG("no D+ pullup?\n"); - retval = -ENODEV; - goto fail0; - } - retval = gpio_request(udc->board.pullup_pin, "udc_pullup"); - if (retval) { - DBG("D+ pullup is busy\n"); - goto fail0; - } - gpio_direction_output(udc->board.pullup_pin, - udc->board.pullup_active_low); - } + udc->gadget.ops = &at91_udc_ops; + udc->gadget.ep0 = &udc->ep[0].ep; + udc->gadget.name = driver_name; + udc->gadget.dev.init_name = "gadget"; - /* newer chips have more FIFO memory than rm9200 */ - if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) { - udc->ep[0].maxpacket = 64; - udc->ep[3].maxpacket = 64; - udc->ep[4].maxpacket = 512; - udc->ep[5].maxpacket = 512; - } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { - udc->ep[3].maxpacket = 64; - } else if (cpu_is_at91sam9263()) { - udc->ep[0].maxpacket = 64; - udc->ep[3].maxpacket = 64; + for (i = 0; i < NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + ep->ep.name = ep_names[i]; + ep->ep.ops = &at91_ep_ops; + ep->udc = udc; + ep->int_mask = BIT(i); + if (i != 0 && i != 3) + ep->is_pingpong = 1; } - udc->udp_baseaddr = ioremap(res->start, resource_size(res)); - if (!udc->udp_baseaddr) { - retval = -ENOMEM; - goto fail0a; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + udc->udp_baseaddr = devm_ioremap_resource(dev, res); + if (IS_ERR(udc->udp_baseaddr)) + return PTR_ERR(udc->udp_baseaddr); + + if (udc->caps && udc->caps->init) { + retval = udc->caps->init(udc); + if (retval) + return retval; } udc_reinit(udc); /* get interface and function clocks */ - udc->iclk = clk_get(dev, "udc_clk"); - udc->fclk = clk_get(dev, "udpck"); - if (IS_ENABLED(CONFIG_COMMON_CLK)) - udc->uclk = clk_get(dev, "usb_clk"); - if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk) || - (IS_ENABLED(CONFIG_COMMON_CLK) && IS_ERR(udc->uclk))) { - DBG("clocks missing\n"); - retval = -ENODEV; - goto fail1; - } + udc->iclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(udc->iclk)) + return PTR_ERR(udc->iclk); + + udc->fclk = devm_clk_get(dev, "hclk"); + if (IS_ERR(udc->fclk)) + return PTR_ERR(udc->fclk); /* don't do anything until we have both gadget driver and VBUS */ - if (IS_ENABLED(CONFIG_COMMON_CLK)) { - clk_set_rate(udc->uclk, 48000000); - retval = clk_prepare(udc->uclk); - if (retval) - goto fail1; - } + clk_set_rate(udc->fclk, 48000000); retval = clk_prepare(udc->fclk); if (retval) - goto fail1a; + return retval; retval = clk_prepare_enable(udc->iclk); if (retval) - goto fail1b; + goto err_unprepare_fclk; + at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); at91_udp_write(udc, AT91_UDP_IDR, 0xffffffff); /* Clear all pending interrupts - UDP may be used by bootloader. */ @@ -1812,18 +1878,21 @@ static int at91udc_probe(struct platform_device *pdev) /* request UDC and maybe VBUS irqs */ udc->udp_irq = platform_get_irq(pdev, 0); - retval = request_irq(udc->udp_irq, at91_udc_irq, - 0, driver_name, udc); - if (retval < 0) { + retval = devm_request_irq(dev, udc->udp_irq, at91_udc_irq, 0, + driver_name, udc); + if (retval) { DBG("request irq %d failed\n", udc->udp_irq); - goto fail1c; + goto err_unprepare_iclk; } + if (gpio_is_valid(udc->board.vbus_pin)) { - retval = gpio_request(udc->board.vbus_pin, "udc_vbus"); - if (retval < 0) { + retval = devm_gpio_request(dev, udc->board.vbus_pin, + "udc_vbus"); + if (retval) { DBG("request vbus pin failed\n"); - goto fail2; + goto err_unprepare_iclk; } + gpio_direction_input(udc->board.vbus_pin); /* @@ -1840,12 +1909,13 @@ static int at91udc_probe(struct platform_device *pdev) mod_timer(&udc->vbus_timer, jiffies + VBUS_POLL_TIMEOUT); } else { - if (request_irq(gpio_to_irq(udc->board.vbus_pin), - at91_vbus_irq, 0, driver_name, udc)) { + retval = devm_request_irq(dev, + gpio_to_irq(udc->board.vbus_pin), + at91_vbus_irq, 0, driver_name, udc); + if (retval) { DBG("request vbus irq %d failed\n", udc->board.vbus_pin); - retval = -EBUSY; - goto fail3; + goto err_unprepare_iclk; } } } else { @@ -1854,49 +1924,27 @@ static int at91udc_probe(struct platform_device *pdev) } retval = usb_add_gadget_udc(dev, &udc->gadget); if (retval) - goto fail4; + goto err_unprepare_iclk; dev_set_drvdata(dev, udc); device_init_wakeup(dev, 1); create_debug_file(udc); INFO("%s version %s\n", driver_name, DRIVER_VERSION); return 0; -fail4: - if (gpio_is_valid(udc->board.vbus_pin) && !udc->board.vbus_polled) - free_irq(gpio_to_irq(udc->board.vbus_pin), udc); -fail3: - if (gpio_is_valid(udc->board.vbus_pin)) - gpio_free(udc->board.vbus_pin); -fail2: - free_irq(udc->udp_irq, udc); -fail1c: + +err_unprepare_iclk: clk_unprepare(udc->iclk); -fail1b: +err_unprepare_fclk: clk_unprepare(udc->fclk); -fail1a: - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_unprepare(udc->uclk); -fail1: - if (IS_ENABLED(CONFIG_COMMON_CLK) && !IS_ERR(udc->uclk)) - clk_put(udc->uclk); - if (!IS_ERR(udc->fclk)) - clk_put(udc->fclk); - if (!IS_ERR(udc->iclk)) - clk_put(udc->iclk); - iounmap(udc->udp_baseaddr); -fail0a: - if (cpu_is_at91rm9200()) - gpio_free(udc->board.pullup_pin); -fail0: - release_mem_region(res->start, resource_size(res)); + DBG("%s probe failed, %d\n", driver_name, retval); + return retval; } static int __exit at91udc_remove(struct platform_device *pdev) { struct at91_udc *udc = platform_get_drvdata(pdev); - struct resource *res; unsigned long flags; DBG("remove\n"); @@ -1911,29 +1959,9 @@ static int __exit at91udc_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); remove_debug_file(udc); - if (gpio_is_valid(udc->board.vbus_pin)) { - free_irq(gpio_to_irq(udc->board.vbus_pin), udc); - gpio_free(udc->board.vbus_pin); - } - free_irq(udc->udp_irq, udc); - iounmap(udc->udp_baseaddr); - - if (cpu_is_at91rm9200()) - gpio_free(udc->board.pullup_pin); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_unprepare(udc->uclk); clk_unprepare(udc->fclk); clk_unprepare(udc->iclk); - clk_put(udc->iclk); - clk_put(udc->fclk); - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_put(udc->uclk); - return 0; } @@ -1989,15 +2017,6 @@ static int at91udc_resume(struct platform_device *pdev) #define at91udc_resume NULL #endif -#if defined(CONFIG_OF) -static const struct of_device_id at91_udc_dt_ids[] = { - { .compatible = "atmel,at91rm9200-udc" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, at91_udc_dt_ids); -#endif - static struct platform_driver at91_udc_driver = { .remove = __exit_p(at91udc_remove), .shutdown = at91udc_shutdown, @@ -2005,7 +2024,7 @@ static struct platform_driver at91_udc_driver = { .resume = at91udc_resume, .driver = { .name = (char *) driver_name, - .of_match_table = of_match_ptr(at91_udc_dt_ids), + .of_match_table = at91_udc_dt_ids, }, }; diff --git a/drivers/usb/gadget/udc/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index 467dcfb74a51..2679c8b217cc 100644 --- a/drivers/usb/gadget/udc/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h @@ -107,6 +107,11 @@ struct at91_ep { unsigned fifo_bank:1; }; +struct at91_udc_caps { + int (*init)(struct at91_udc *udc); + void (*pullup)(struct at91_udc *udc, int is_on); +}; + /* * driver is non-SMP, and just blocks IRQs whenever it needs * access protection for chip registers or driver state @@ -115,6 +120,7 @@ struct at91_udc { struct usb_gadget gadget; struct at91_ep ep[NUM_ENDPOINTS]; struct usb_gadget_driver *driver; + const struct at91_udc_caps *caps; unsigned vbus:1; unsigned enabled:1; unsigned clocked:1; @@ -125,7 +131,7 @@ struct at91_udc { unsigned active_suspend:1; u8 addr; struct at91_udc_data board; - struct clk *iclk, *fclk, *uclk; + struct clk *iclk, *fclk; struct platform_device *pdev; struct proc_dir_entry *pde; void __iomem *udp_baseaddr; @@ -133,6 +139,7 @@ struct at91_udc { spinlock_t lock; struct timer_list vbus_timer; struct work_struct vbus_timer_work; + struct regmap *matrix; }; static inline struct at91_udc *to_udc(struct usb_gadget *g) |