summaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/udc/at91_udc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/udc/at91_udc.c')
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c525
1 files changed, 272 insertions, 253 deletions
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,
},
};