summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/udc/atmel_usba_udc.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-26 15:36:28 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-26 15:36:28 +0100
commitbc46e23c34259c842a138f10acc371e4e2cdb287 (patch)
tree4f6d6938f824abae24e0bb9c563cf07522a19c89 /drivers/usb/gadget/udc/atmel_usba_udc.c
parent6969408de2681e1f9dfaed0b311d067ce3c75474 (diff)
parente42a5dbb8a3d14f5a35bffa3bf7dcb87883f767a (diff)
Merge tag 'usb-for-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes: USB: changes for v4.11 Here's the big pull request for the Gadget API. Again the majority of changes sit in dwc2 driver. Most important changes contain a workaround for GOTGCTL being wrong, a sleep-inside-spinlock fix and the big series of cleanups on dwc2. One important thing on dwc3 is that we don't anymore need gadget drivers to cope with unaligned OUT transfers for us. We have support for appending one extra chained TRB to align transfer ourselves. Apart from these, the usual set of typos, non-critical fixes, etc.
Diffstat (limited to 'drivers/usb/gadget/udc/atmel_usba_udc.c')
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c236
1 files changed, 204 insertions, 32 deletions
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 12c7687216e6..11bbce28bc23 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -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");
@@ -2000,6 +2157,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);