usb: gadget: udc: atmel: Update endpoint allocation scheme
authorCristian Birsan <cristian.birsan@microchip.com>
Mon, 23 Jan 2017 14:45:59 +0000 (16:45 +0200)
committerFelipe Balbi <felipe.balbi@linux.intel.com>
Tue, 24 Jan 2017 09:04:24 +0000 (11:04 +0200)
This patch updates the usb endpoint allocation scheme for atmel usba
driver to make sure all endpoints are allocated in order. This
requirement comes from the datasheet of the controller.

The allocation scheme is decided by fifo_mode parameter. For fifo_mode =
0 the driver tries to autoconfigure the endpoints fifo size. All other
modes contain fixed configurations optimized for different purposes. The
idea is somehow similar with the approach used on musb driver.

Signed-off-by: Cristian Birsan <cristian.birsan@microchip.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
drivers/usb/gadget/udc/Kconfig
drivers/usb/gadget/udc/atmel_usba_udc.c
drivers/usb/gadget/udc/atmel_usba_udc.h

index 658b8da6091523c275fd225b16355fb40cdbd70e..4b69f28a9af972dc5fc5584b9f8bbd6a38774f5a 100644 (file)
@@ -60,6 +60,20 @@ config USB_ATMEL_USBA
          USBA is the integrated high-speed USB Device controller on
          the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
 
+         The fifo_mode parameter is used to select endpoint allocation mode.
+         fifo_mode = 0 is used to let the driver autoconfigure the endpoints.
+         In this case 2 banks are allocated for isochronous endpoints and
+         only one bank is allocated for the rest of the endpoints.
+
+         fifo_mode = 1 is a generic maximum fifo size (1024 bytes) configuration
+         allowing the usage of ep1 - ep6
+
+         fifo_mode = 2 is a generic performance maximum fifo size (1024 bytes)
+         configuration allowing the usage of ep1 - ep3
+
+         fifo_mode = 3 is a balanced performance configuration allowing the
+         the usage of ep1 - ep8
+
 config USB_BCM63XX_UDC
        tristate "Broadcom BCM63xx Peripheral Controller"
        depends on BCM63XX
index f3212db9bc37bf1889c482e86c9c679720766b1f..f35949d3e8ba271d79001ba984c1bfc0780e88d5 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/mfd/syscon.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/ctype.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/atmel_usba_udc.h>
@@ -318,6 +319,91 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc)
 }
 #endif
 
+static ushort fifo_mode;
+
+/* "modprobe ... fifo_mode=1" etc */
+module_param(fifo_mode, ushort, 0x0);
+MODULE_PARM_DESC(fifo_mode, "Endpoint configuration mode");
+
+/* mode 0 - uses autoconfig */
+
+/* mode 1 - fits in 8KB, generic max fifo configuration */
+static struct usba_fifo_cfg mode_1_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64,     .nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 1024,   .nr_banks = 2, },
+{ .hw_ep_num = 2, .fifo_size = 1024,   .nr_banks = 1, },
+{ .hw_ep_num = 3, .fifo_size = 1024,   .nr_banks = 1, },
+{ .hw_ep_num = 4, .fifo_size = 1024,   .nr_banks = 1, },
+{ .hw_ep_num = 5, .fifo_size = 1024,   .nr_banks = 1, },
+{ .hw_ep_num = 6, .fifo_size = 1024,   .nr_banks = 1, },
+};
+
+/* mode 2 - fits in 8KB, performance max fifo configuration */
+static struct usba_fifo_cfg mode_2_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64,     .nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 1024,   .nr_banks = 3, },
+{ .hw_ep_num = 2, .fifo_size = 1024,   .nr_banks = 2, },
+{ .hw_ep_num = 3, .fifo_size = 1024,   .nr_banks = 2, },
+};
+
+/* mode 3 - fits in 8KB, mixed fifo configuration */
+static struct usba_fifo_cfg mode_3_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64,     .nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 1024,   .nr_banks = 2, },
+{ .hw_ep_num = 2, .fifo_size = 512,    .nr_banks = 2, },
+{ .hw_ep_num = 3, .fifo_size = 512,    .nr_banks = 2, },
+{ .hw_ep_num = 4, .fifo_size = 512,    .nr_banks = 2, },
+{ .hw_ep_num = 5, .fifo_size = 512,    .nr_banks = 2, },
+{ .hw_ep_num = 6, .fifo_size = 512,    .nr_banks = 2, },
+};
+
+/* mode 4 - fits in 8KB, custom fifo configuration */
+static struct usba_fifo_cfg mode_4_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64,     .nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 512,    .nr_banks = 2, },
+{ .hw_ep_num = 2, .fifo_size = 512,    .nr_banks = 2, },
+{ .hw_ep_num = 3, .fifo_size = 8,      .nr_banks = 2, },
+{ .hw_ep_num = 4, .fifo_size = 512,    .nr_banks = 2, },
+{ .hw_ep_num = 5, .fifo_size = 512,    .nr_banks = 2, },
+{ .hw_ep_num = 6, .fifo_size = 16,     .nr_banks = 2, },
+{ .hw_ep_num = 7, .fifo_size = 8,      .nr_banks = 2, },
+{ .hw_ep_num = 8, .fifo_size = 8,      .nr_banks = 2, },
+};
+/* Add additional configurations here */
+
+int usba_config_fifo_table(struct usba_udc *udc)
+{
+       int n;
+
+       switch (fifo_mode) {
+       default:
+               fifo_mode = 0;
+       case 0:
+               udc->fifo_cfg = NULL;
+               n = 0;
+               break;
+       case 1:
+               udc->fifo_cfg = mode_1_cfg;
+               n = ARRAY_SIZE(mode_1_cfg);
+               break;
+       case 2:
+               udc->fifo_cfg = mode_2_cfg;
+               n = ARRAY_SIZE(mode_2_cfg);
+               break;
+       case 3:
+               udc->fifo_cfg = mode_3_cfg;
+               n = ARRAY_SIZE(mode_3_cfg);
+               break;
+       case 4:
+               udc->fifo_cfg = mode_4_cfg;
+               n = ARRAY_SIZE(mode_4_cfg);
+               break;
+       }
+       DBG(DBG_HW, "Setup fifo_mode %d\n", fifo_mode);
+
+       return n;
+}
+
 static inline u32 usba_int_enb_get(struct usba_udc *udc)
 {
        return udc->int_enb_cache;
@@ -543,24 +629,17 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
        ep->is_isoc = 0;
        ep->is_in = 0;
 
-       if (maxpacket <= 8)
-               ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
-       else
-               /* LSB is bit 1, not 0 */
-               ept_cfg = USBA_BF(EPT_SIZE, fls(maxpacket - 1) - 3);
-
-       DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n",
+       DBG(DBG_ERR, "%s: EPT_CFG = 0x%lx (maxpacket = %lu)\n",
                        ep->ep.name, ept_cfg, maxpacket);
 
        if (usb_endpoint_dir_in(desc)) {
                ep->is_in = 1;
-               ept_cfg |= USBA_EPT_DIR_IN;
+               ep->ept_cfg |= USBA_EPT_DIR_IN;
        }
 
        switch (usb_endpoint_type(desc)) {
        case USB_ENDPOINT_XFER_CONTROL:
-               ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
-               ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+               ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
                break;
        case USB_ENDPOINT_XFER_ISOC:
                if (!ep->can_isoc) {
@@ -578,24 +657,15 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
                        return -EINVAL;
 
                ep->is_isoc = 1;
-               ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO);
+               ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO);
+               ep->ept_cfg |= USBA_BF(NB_TRANS, nr_trans);
 
-               /*
-                * Do triple-buffering on high-bandwidth iso endpoints.
-                */
-               if (nr_trans > 1 && ep->nr_banks == 3)
-                       ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_TRIPLE);
-               else
-                       ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
-               ept_cfg |= USBA_BF(NB_TRANS, nr_trans);
                break;
        case USB_ENDPOINT_XFER_BULK:
-               ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK);
-               ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
+               ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK);
                break;
        case USB_ENDPOINT_XFER_INT:
-               ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT);
-               ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
+               ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT);
                break;
        }
 
@@ -604,7 +674,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
        ep->ep.desc = desc;
        ep->ep.maxpacket = maxpacket;
 
-       usba_ep_writel(ep, CFG, ept_cfg);
+       usba_ep_writel(ep, CFG, ep->ept_cfg);
        usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
 
        if (ep->can_dma) {
@@ -1006,12 +1076,81 @@ static int atmel_usba_start(struct usb_gadget *gadget,
                struct usb_gadget_driver *driver);
 static int atmel_usba_stop(struct usb_gadget *gadget);
 
+static struct usb_ep *atmel_usba_match_ep(
+               struct usb_gadget               *gadget,
+               struct usb_endpoint_descriptor  *desc,
+               struct usb_ss_ep_comp_descriptor *ep_comp
+)
+{
+       struct usb_ep   *_ep;
+       struct usba_ep *ep;
+
+       /* Look at endpoints until an unclaimed one looks usable */
+       list_for_each_entry(_ep, &gadget->ep_list, ep_list) {
+               if (usb_gadget_ep_match_desc(gadget, _ep, desc, ep_comp))
+                       goto found_ep;
+       }
+       /* Fail */
+       return NULL;
+
+found_ep:
+
+       if (fifo_mode == 0) {
+               /* Optimize hw fifo size based on ep type and other info */
+               ep = to_usba_ep(_ep);
+
+               switch (usb_endpoint_type(desc)) {
+
+               case USB_ENDPOINT_XFER_CONTROL:
+                       break;
+
+               case USB_ENDPOINT_XFER_ISOC:
+                       ep->fifo_size = 1024;
+                       ep->nr_banks = 2;
+                       break;
+
+               case USB_ENDPOINT_XFER_BULK:
+                       ep->fifo_size = 512;
+                       ep->nr_banks = 1;
+                       break;
+
+               case USB_ENDPOINT_XFER_INT:
+                       if (desc->wMaxPacketSize == 0)
+                               ep->fifo_size =
+                                   roundup_pow_of_two(_ep->maxpacket_limit);
+                       else
+                               ep->fifo_size =
+                                   roundup_pow_of_two(le16_to_cpu(desc->wMaxPacketSize));
+                       ep->nr_banks = 1;
+                       break;
+               }
+
+               /* It might be a little bit late to set this */
+               usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size);
+
+               /* Generate ept_cfg basd on FIFO size and number of banks */
+               if (ep->fifo_size  <= 8)
+                       ep->ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
+               else
+                       /* LSB is bit 1, not 0 */
+                       ep->ept_cfg =
+                               USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3);
+
+               ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks);
+
+               ep->udc->configured_ep++;
+       }
+
+return _ep;
+}
+
 static const struct usb_gadget_ops usba_udc_ops = {
        .get_frame              = usba_udc_get_frame,
        .wakeup                 = usba_udc_wakeup,
        .set_selfpowered        = usba_udc_set_selfpowered,
        .udc_start              = atmel_usba_start,
        .udc_stop               = atmel_usba_stop,
+       .match_ep               = atmel_usba_match_ep,
 };
 
 static struct usb_endpoint_descriptor usba_ep0_desc = {
@@ -1678,7 +1817,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
        }
 
        if (status & USBA_END_OF_RESET) {
-               struct usba_ep *ep0;
+               struct usba_ep *ep0, *ep;
+               int i, n;
 
                usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
                generate_bias_pulse(udc);
@@ -1717,6 +1857,16 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
                if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED))
                        dev_dbg(&udc->pdev->dev,
                                 "ODD: EP0 configuration is invalid!\n");
+
+               /* Preallocate other endpoints */
+               n = fifo_mode ? udc->num_ep : udc->configured_ep;
+               for (i = 1; i < n; i++) {
+                       ep = &udc->usba_ep[i];
+                       usba_ep_writel(ep, CFG, ep->ept_cfg);
+                       if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED))
+                               dev_dbg(&udc->pdev->dev,
+                                "ODD: EP%d configuration is invalid!\n", i);
+               }
        }
 
        spin_unlock(&udc->lock);
@@ -1864,6 +2014,9 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
        if (gpio_is_valid(udc->vbus_pin))
                disable_irq(gpio_to_irq(udc->vbus_pin));
 
+       if (fifo_mode == 0)
+               udc->configured_ep = 1;
+
        usba_stop(udc);
 
        udc->driver = NULL;
@@ -1931,9 +2084,13 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
                                                &flags);
        udc->vbus_pin_inverted = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0;
 
-       pp = NULL;
-       while ((pp = of_get_next_child(np, pp)))
-               udc->num_ep++;
+       if (fifo_mode == 0) {
+               pp = NULL;
+               while ((pp = of_get_next_child(np, pp)))
+                       udc->num_ep++;
+               udc->configured_ep = 1;
+       } else
+               udc->num_ep = usba_config_fifo_table(udc);
 
        eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * udc->num_ep,
                           GFP_KERNEL);
@@ -1946,7 +2103,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
 
        pp = NULL;
        i = 0;
-       while ((pp = of_get_next_child(np, pp))) {
+       while ((pp = of_get_next_child(np, pp)) && i < udc->num_ep) {
                ep = &eps[i];
 
                ret = of_property_read_u32(pp, "reg", &val);
@@ -1954,21 +2111,21 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
                        dev_err(&pdev->dev, "of_probe: reg error(%d)\n", ret);
                        goto err;
                }
-               ep->index = val;
+               ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : val;
 
                ret = of_property_read_u32(pp, "atmel,fifo-size", &val);
                if (ret) {
                        dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret);
                        goto err;
                }
-               ep->fifo_size = val;
+               ep->fifo_size = fifo_mode ? udc->fifo_cfg[i].fifo_size : val;
 
                ret = of_property_read_u32(pp, "atmel,nb-banks", &val);
                if (ret) {
                        dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret);
                        goto err;
                }
-               ep->nr_banks = val;
+               ep->nr_banks = fifo_mode ? udc->fifo_cfg[i].nr_banks : val;
 
                ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
                ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
@@ -1999,6 +2156,21 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
                ep->ep.caps.dir_in = true;
                ep->ep.caps.dir_out = true;
 
+               if (fifo_mode != 0) {
+                       /*
+                        * Generate ept_cfg based on FIFO size and
+                        * banks number
+                        */
+                       if (ep->fifo_size  <= 8)
+                               ep->ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
+                       else
+                               /* LSB is bit 1, not 0 */
+                               ep->ept_cfg =
+                                 USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3);
+
+                       ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks);
+               }
+
                if (i)
                        list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
 
index 3e1c9d589dfa34ade18fffa7e300a4c9dfcc1563..a186cdde1f584bf73a9212ddfde7957390a9f7f7 100644 (file)
@@ -275,6 +275,12 @@ struct usba_dma_desc {
        u32 ctrl;
 };
 
+struct usba_fifo_cfg {
+       u8                      hw_ep_num;
+       u16                     fifo_size;
+       u8                      nr_banks;
+};
+
 struct usba_ep {
        int                                     state;
        void __iomem                            *ep_regs;
@@ -292,7 +298,7 @@ struct usba_ep {
        unsigned int                            can_isoc:1;
        unsigned int                            is_isoc:1;
        unsigned int                            is_in:1;
-
+       unsigned long                           ept_cfg;
 #ifdef CONFIG_USB_GADGET_DEBUG_FS
        u32                                     last_dma_status;
        struct dentry                           *debugfs_dir;
@@ -337,6 +343,8 @@ struct usba_udc {
        int vbus_pin;
        int vbus_pin_inverted;
        int num_ep;
+       int configured_ep;
+       struct usba_fifo_cfg *fifo_cfg;
        struct clk *pclk;
        struct clk *hclk;
        struct usba_ep *usba_ep;