]> Git Repo - linux.git/commitdiff
Merge 4.18-rc7 into usb-next
authorGreg Kroah-Hartman <[email protected]>
Mon, 30 Jul 2018 08:04:58 +0000 (10:04 +0200)
committerGreg Kroah-Hartman <[email protected]>
Mon, 30 Jul 2018 08:04:58 +0000 (10:04 +0200)
We want the USB fixes in here as well to handle merge issues.

Signed-off-by: Greg Kroah-Hartman <[email protected]>
1  2 
MAINTAINERS
drivers/usb/class/cdc-acm.c
drivers/usb/core/hub.c
drivers/usb/host/xhci.c
drivers/usb/typec/tcpm.c

diff --combined MAINTAINERS
index 5b10c2179f9b66c0ce2f8ae52c0a7ec75d746eb1,32fbc6f732d44da6bad368f00ecb000c52c3263f..b6f6c22c03f1324bacb5ae9aa98cb2dd62f34035
@@@ -1647,8 -1647,7 +1647,8 @@@ M:      Chunfeng Yun <chunfeng.yun@mediatek.
  L:    [email protected] (moderated for non-subscribers)
  L:    [email protected] (moderated for non-subscribers)
  S:    Maintained
 -F:    drivers/phy/mediatek/phy-mtk-tphy.c
 +F:    drivers/phy/mediatek/
 +F:    Documentation/devicetree/bindings/phy/phy-mtk-*
  
  ARM/MICREL KS8695 ARCHITECTURE
  M:    Greg Ungerer <[email protected]>
@@@ -2524,7 -2523,7 +2524,7 @@@ S:      Supporte
  F:    drivers/scsi/esas2r
  
  ATUSB IEEE 802.15.4 RADIO DRIVER
- M:    Stefan Schmidt <stefan@osg.samsung.com>
+ M:    Stefan Schmidt <stefan@datenfreihafen.org>
  L:    [email protected]
  S:    Maintained
  F:    drivers/net/ieee802154/atusb.c
@@@ -5791,7 -5790,6 +5791,6 @@@ F:      include/linux/fsl
  
  FREESCALE SOC FS_ENET DRIVER
  M:    Pantelis Antoniou <[email protected]>
- M:    Vitaly Bordug <[email protected]>
  L:    [email protected]
  L:    [email protected]
  S:    Maintained
@@@ -6910,7 -6908,7 +6909,7 @@@ F:      drivers/clk/clk-versaclock5.
  
  IEEE 802.15.4 SUBSYSTEM
  M:    Alexander Aring <[email protected]>
- M:    Stefan Schmidt <stefan@osg.samsung.com>
+ M:    Stefan Schmidt <stefan@datenfreihafen.org>
  L:    [email protected]
  W:    http://wpan.cakelab.org/
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/sschmidt/wpan.git
@@@ -7097,6 -7095,7 +7096,7 @@@ F:      include/uapi/linux/input.
  F:    include/uapi/linux/input-event-codes.h
  F:    include/linux/input/
  F:    Documentation/devicetree/bindings/input/
+ F:    Documentation/devicetree/bindings/serio/
  F:    Documentation/input/
  
  INPUT MULTITOUCH (MT) PROTOCOL
@@@ -8630,7 -8629,7 +8630,7 @@@ MARVELL MWIFIEX WIRELESS DRIVE
  M:    Amitkumar Karwar <[email protected]>
  M:    Nishant Sarmukadam <[email protected]>
  M:    Ganapathi Bhat <[email protected]>
- M:    Xinming Hu <huxm@marvell.com>
+ M:    Xinming Hu <huxinming820@gmail.com>
  L:    [email protected]
  S:    Maintained
  F:    drivers/net/wireless/marvell/mwifiex/
@@@ -9076,7 -9075,7 +9076,7 @@@ S:      Maintaine
  F:    drivers/usb/mtu3/
  
  MEGACHIPS STDPXXXX-GE-B850V3-FW LVDS/DP++ BRIDGES
- M:    Peter Senna Tschudin <peter.senna@collabora.com>
+ M:    Peter Senna Tschudin <peter.senna@gmail.com>
  M:    Martin Donnelly <[email protected]>
  M:    Martyn Welch <[email protected]>
  S:    Maintained
  S:    Maintained
  F:    drivers/usb/typec/mux/pi3usb30532.c
  
 -USB TYPEC SUBSYSTEM
 +USB TYPEC CLASS
  M:    Heikki Krogerus <[email protected]>
  L:    [email protected]
  S:    Maintained
@@@ -14968,15 -14967,6 +14968,15 @@@ F: Documentation/driver-api/usb/typec.r
  F:    drivers/usb/typec/
  F:    include/linux/usb/typec.h
  
 +USB TYPEC BUS FOR ALTERNATE MODES
 +M:    Heikki Krogerus <[email protected]>
 +L:    [email protected]
 +S:    Maintained
 +F:    Documentation/ABI/testing/sysfs-bus-typec
 +F:    Documentation/driver-api/usb/typec_bus.rst
 +F:    drivers/usb/typec/altmodes/
 +F:    include/linux/usb/typec_altmode.h
 +
  USB UHCI DRIVER
  M:    Alan Stern <[email protected]>
  L:    [email protected]
index 020a13decf2c24fe3fa4d340ba98d8355c92c39d,75c4623ad779eecd64b0164a24b6d8ac86177ca3..27346d69f3938c0222fc59b467bbeb6e9ce778e7
@@@ -276,7 -276,6 +276,7 @@@ static void acm_process_notification(st
  {
        int newctrl;
        int difference;
 +      unsigned long flags;
        struct usb_cdc_notification *dr = (struct usb_cdc_notification *)buf;
        unsigned char *data = buf + sizeof(struct usb_cdc_notification);
  
                }
  
                difference = acm->ctrlin ^ newctrl;
 -              spin_lock(&acm->read_lock);
 +              spin_lock_irqsave(&acm->read_lock, flags);
                acm->ctrlin = newctrl;
                acm->oldcount = acm->iocount;
  
                        acm->iocount.parity++;
                if (difference & ACM_CTRL_OVERRUN)
                        acm->iocount.overrun++;
 -              spin_unlock(&acm->read_lock);
 +              spin_unlock_irqrestore(&acm->read_lock, flags);
  
                if (difference)
                        wake_up_all(&acm->wioctl);
@@@ -1379,9 -1378,6 +1379,9 @@@ made_compressed_probe
        if (acm == NULL)
                goto alloc_fail;
  
 +      tty_port_init(&acm->port);
 +      acm->port.ops = &acm_port_ops;
 +
        minor = acm_alloc_minor(acm);
        if (minor < 0)
                goto alloc_fail1;
                acm->out = usb_sndintpipe(usb_dev, epwrite->bEndpointAddress);
        else
                acm->out = usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress);
 -      tty_port_init(&acm->port);
 -      acm->port.ops = &acm_port_ops;
        init_usb_anchor(&acm->delayed);
        acm->quirks = quirks;
  
        buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
        if (!buf)
 -              goto alloc_fail2;
 +              goto alloc_fail1;
        acm->ctrl_buffer = buf;
  
        if (acm_write_buffers_alloc(acm) < 0)
 -              goto alloc_fail4;
 +              goto alloc_fail2;
  
        acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
        if (!acm->ctrlurb)
 -              goto alloc_fail5;
 +              goto alloc_fail3;
  
        for (i = 0; i < num_rx_buf; i++) {
                struct acm_rb *rb = &(acm->read_buffers[i]);
                rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
                                                                &rb->dma);
                if (!rb->base)
 -                      goto alloc_fail6;
 +                      goto alloc_fail4;
                rb->index = i;
                rb->instance = acm;
  
                urb = usb_alloc_urb(0, GFP_KERNEL);
                if (!urb)
 -                      goto alloc_fail6;
 +                      goto alloc_fail4;
  
                urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
                urb->transfer_dma = rb->dma;
  
                snd->urb = usb_alloc_urb(0, GFP_KERNEL);
                if (snd->urb == NULL)
 -                      goto alloc_fail7;
 +                      goto alloc_fail5;
  
                if (usb_endpoint_xfer_int(epwrite))
                        usb_fill_int_urb(snd->urb, usb_dev, acm->out,
  
        i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
        if (i < 0)
 -              goto alloc_fail7;
 +              goto alloc_fail5;
  
        if (h.usb_cdc_country_functional_desc) { /* export the country data */
                struct usb_cdc_country_functional_desc * cfd =
@@@ -1544,7 -1542,7 +1544,7 @@@ skip_countries
                        &control_interface->dev);
        if (IS_ERR(tty_dev)) {
                rv = PTR_ERR(tty_dev);
 -              goto alloc_fail8;
 +              goto alloc_fail6;
        }
  
        if (quirks & CLEAR_HALT_CONDITIONS) {
        }
  
        return 0;
 -alloc_fail8:
 +alloc_fail6:
        if (acm->country_codes) {
                device_remove_file(&acm->control->dev,
                                &dev_attr_wCountryCodes);
                kfree(acm->country_codes);
        }
        device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
 -alloc_fail7:
 +alloc_fail5:
        usb_set_intfdata(intf, NULL);
        for (i = 0; i < ACM_NW; i++)
                usb_free_urb(acm->wb[i].urb);
 -alloc_fail6:
 +alloc_fail4:
        for (i = 0; i < num_rx_buf; i++)
                usb_free_urb(acm->read_urbs[i]);
        acm_read_buffers_free(acm);
        usb_free_urb(acm->ctrlurb);
 -alloc_fail5:
 +alloc_fail3:
        acm_write_buffers_free(acm);
 -alloc_fail4:
 -      usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
  alloc_fail2:
 -      acm_release_minor(acm);
 +      usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
  alloc_fail1:
 -      kfree(acm);
 +      tty_port_put(&acm->port);
  alloc_fail:
        return rv;
  }
@@@ -1831,6 -1831,9 +1831,9 @@@ static const struct usb_device_id acm_i
        { USB_DEVICE(0x09d8, 0x0320), /* Elatec GmbH TWN3 */
        .driver_info = NO_UNION_NORMAL, /* has misplaced union descriptor */
        },
+       { USB_DEVICE(0x0ca6, 0xa050), /* Castles VEGA3000 */
+       .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
+       },
  
        { USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */
        .driver_info = CLEAR_HALT_CONDITIONS,
diff --combined drivers/usb/core/hub.c
index fef5af7aab92f46ece0d5027790c4f93b09a6eec,1fb2668099663e08ffa3f5e7713ab0eaa55fb10a..462ce49f683a01da9b6ddd7045ccb358cb897ab4
@@@ -1142,10 -1142,14 +1142,14 @@@ static void hub_activate(struct usb_hu
  
                if (!udev || udev->state == USB_STATE_NOTATTACHED) {
                        /* Tell hub_wq to disconnect the device or
-                        * check for a new connection
+                        * check for a new connection or over current condition.
+                        * Based on USB2.0 Spec Section 11.12.5,
+                        * C_PORT_OVER_CURRENT could be set while
+                        * PORT_OVER_CURRENT is not. So check for any of them.
                         */
                        if (udev || (portstatus & USB_PORT_STAT_CONNECTION) ||
-                           (portstatus & USB_PORT_STAT_OVERCURRENT))
+                           (portstatus & USB_PORT_STAT_OVERCURRENT) ||
+                           (portchange & USB_PORT_STAT_C_OVERCURRENT))
                                set_bit(port1, hub->change_bits);
  
                } else if (portstatus & USB_PORT_STAT_ENABLE) {
@@@ -3656,54 -3660,12 +3660,54 @@@ static int hub_suspend(struct usb_inter
        return 0;
  }
  
 +/* Report wakeup requests from the ports of a resuming root hub */
 +static void report_wakeup_requests(struct usb_hub *hub)
 +{
 +      struct usb_device       *hdev = hub->hdev;
 +      struct usb_device       *udev;
 +      struct usb_hcd          *hcd;
 +      unsigned long           resuming_ports;
 +      int                     i;
 +
 +      if (hdev->parent)
 +              return;         /* Not a root hub */
 +
 +      hcd = bus_to_hcd(hdev->bus);
 +      if (hcd->driver->get_resuming_ports) {
 +
 +              /*
 +               * The get_resuming_ports() method returns a bitmap (origin 0)
 +               * of ports which have started wakeup signaling but have not
 +               * yet finished resuming.  During system resume we will
 +               * resume all the enabled ports, regardless of any wakeup
 +               * signals, which means the wakeup requests would be lost.
 +               * To prevent this, report them to the PM core here.
 +               */
 +              resuming_ports = hcd->driver->get_resuming_ports(hcd);
 +              for (i = 0; i < hdev->maxchild; ++i) {
 +                      if (test_bit(i, &resuming_ports)) {
 +                              udev = hub->ports[i]->child;
 +                              if (udev)
 +                                      pm_wakeup_event(&udev->dev, 0);
 +                      }
 +              }
 +      }
 +}
 +
  static int hub_resume(struct usb_interface *intf)
  {
        struct usb_hub *hub = usb_get_intfdata(intf);
  
        dev_dbg(&intf->dev, "%s\n", __func__);
        hub_activate(hub, HUB_RESUME);
 +
 +      /*
 +       * This should be called only for system resume, not runtime resume.
 +       * We can't tell the difference here, so some wakeup requests will be
 +       * reported at the wrong time or more than once.  This shouldn't
 +       * matter much, so long as they do get reported.
 +       */
 +      report_wakeup_requests(hub);
        return 0;
  }
  
diff --combined drivers/usb/host/xhci.c
index ebad4073fb4d8b022a28c5ce0d7f9199ee8c3d50,68e6132aa8b2a3985f01ac12c6bdd75882c4748d..61f48b17e57b6688599e9269c6933cfeb51fdf92
@@@ -3051,6 -3051,7 +3051,7 @@@ static void xhci_endpoint_reset(struct 
        if (!list_empty(&ep->ring->td_list)) {
                dev_err(&udev->dev, "EP not empty, refuse reset\n");
                spin_unlock_irqrestore(&xhci->lock, flags);
+               xhci_free_command(xhci, cfg_cmd);
                goto cleanup;
        }
        xhci_queue_stop_endpoint(xhci, stop_cmd, udev->slot_id, ep_index, 0);
@@@ -5120,7 -5121,6 +5121,7 @@@ static const struct hc_driver xhci_hc_d
        .hub_status_data =      xhci_hub_status_data,
        .bus_suspend =          xhci_bus_suspend,
        .bus_resume =           xhci_bus_resume,
 +      .get_resuming_ports =   xhci_get_resuming_ports,
  
        /*
         * call back when device connected and addressed
diff --combined drivers/usb/typec/tcpm.c
index 74e0cdabfcc8c73e52f925936a63a49a0e4e409a,d1d20252bad86889bb87b7f755545020abd6b0ea..4f1f4215f3d6e29b191a4cdc7e722b4ca36249fd
@@@ -26,7 -26,7 +26,7 @@@
  #include <linux/usb/pd_vdo.h>
  #include <linux/usb/role.h>
  #include <linux/usb/tcpm.h>
 -#include <linux/usb/typec.h>
 +#include <linux/usb/typec_altmode.h>
  #include <linux/workqueue.h>
  
  #define FOREACH_STATE(S)                      \
@@@ -169,14 -169,13 +169,14 @@@ enum pd_msg_request 
  /* Alternate mode support */
  
  #define SVID_DISCOVERY_MAX    16
 +#define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX)
  
  struct pd_mode_data {
        int svid_index;         /* current SVID index           */
        int nsvids;
        u16 svids[SVID_DISCOVERY_MAX];
        int altmodes;           /* number of alternate modes    */
 -      struct typec_altmode_desc altmode_desc[SVID_DISCOVERY_MAX];
 +      struct typec_altmode_desc altmode_desc[ALTMODE_DISCOVERY_MAX];
  };
  
  struct pd_pps_data {
@@@ -311,8 -310,8 +311,8 @@@ struct tcpm_port 
  
        /* Alternate mode data */
        struct pd_mode_data mode_data;
 -      struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX];
 -      struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX];
 +      struct typec_altmode *partner_altmode[ALTMODE_DISCOVERY_MAX];
 +      struct typec_altmode *port_altmode[ALTMODE_DISCOVERY_MAX];
  
        /* Deadline in jiffies to exit src_try_wait state */
        unsigned long max_wait;
@@@ -642,14 -641,14 +642,14 @@@ void tcpm_pd_transmit_complete(struct t
  }
  EXPORT_SYMBOL_GPL(tcpm_pd_transmit_complete);
  
 -static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode,
 +static int tcpm_mux_set(struct tcpm_port *port, int state,
                        enum usb_role usb_role,
                        enum typec_orientation orientation)
  {
        int ret;
  
 -      tcpm_log(port, "Requesting mux mode %d, usb-role %d, orientation %d",
 -               mode, usb_role, orientation);
 +      tcpm_log(port, "Requesting mux state %d, usb-role %d, orientation %d",
 +               state, usb_role, orientation);
  
        ret = typec_set_orientation(port->typec_port, orientation);
        if (ret)
                        return ret;
        }
  
 -      return typec_set_mode(port->typec_port, mode);
 +      return typec_set_mode(port->typec_port, state);
  }
  
  static int tcpm_set_polarity(struct tcpm_port *port,
@@@ -791,7 -790,7 +791,7 @@@ static int tcpm_set_roles(struct tcpm_p
        else
                usb_role = USB_ROLE_DEVICE;
  
 -      ret = tcpm_mux_set(port, TYPEC_MUX_USB, usb_role, orientation);
 +      ret = tcpm_mux_set(port, TYPEC_STATE_USB, usb_role, orientation);
        if (ret < 0)
                return ret;
  
@@@ -999,6 -998,7 +999,6 @@@ static void svdm_consume_modes(struct t
  {
        struct pd_mode_data *pmdata = &port->mode_data;
        struct typec_altmode_desc *paltmode;
 -      struct typec_mode_desc *pmode;
        int i;
  
        if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
                return;
        }
  
 -      paltmode = &pmdata->altmode_desc[pmdata->altmodes];
 -      memset(paltmode, 0, sizeof(*paltmode));
 +      for (i = 1; i < cnt; i++) {
 +              paltmode = &pmdata->altmode_desc[pmdata->altmodes];
 +              memset(paltmode, 0, sizeof(*paltmode));
  
 -      paltmode->svid = pmdata->svids[pmdata->svid_index];
 +              paltmode->svid = pmdata->svids[pmdata->svid_index];
 +              paltmode->mode = i;
 +              paltmode->vdo = le32_to_cpu(payload[i]);
  
 -      tcpm_log(port, " Alternate mode %d: SVID 0x%04x",
 -               pmdata->altmodes, paltmode->svid);
 +              tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
 +                       pmdata->altmodes, paltmode->svid,
 +                       paltmode->mode, paltmode->vdo);
  
 -      for (i = 1; i < cnt && paltmode->n_modes < ALTMODE_MAX_MODES; i++) {
 -              pmode = &paltmode->modes[paltmode->n_modes];
 -              memset(pmode, 0, sizeof(*pmode));
 -              pmode->vdo = le32_to_cpu(payload[i]);
 -              pmode->index = i - 1;
 -              paltmode->n_modes++;
 -              tcpm_log(port, "  VDO %d: 0x%08x",
 -                       pmode->index, pmode->vdo);
 +              pmdata->altmodes++;
        }
 -      port->partner_altmode[pmdata->altmodes] =
 -              typec_partner_register_altmode(port->partner, paltmode);
 -      if (!port->partner_altmode[pmdata->altmodes]) {
 -              tcpm_log(port,
 -                       "Failed to register alternate modes for SVID 0x%04x",
 -                       paltmode->svid);
 -              return;
 +}
 +
 +static void tcpm_register_partner_altmodes(struct tcpm_port *port)
 +{
 +      struct pd_mode_data *modep = &port->mode_data;
 +      struct typec_altmode *altmode;
 +      int i;
 +
 +      for (i = 0; i < modep->altmodes; i++) {
 +              altmode = typec_partner_register_altmode(port->partner,
 +                                              &modep->altmode_desc[i]);
 +              if (!altmode)
 +                      tcpm_log(port, "Failed to register partner SVID 0x%04x",
 +                               modep->altmode_desc[i].svid);
 +              port->partner_altmode[i] = altmode;
        }
 -      pmdata->altmodes++;
  }
  
  #define supports_modal(port)  PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
  static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
                        u32 *response)
  {
 -      u32 p0 = le32_to_cpu(payload[0]);
 -      int cmd_type = PD_VDO_CMDT(p0);
 -      int cmd = PD_VDO_CMD(p0);
 +      struct typec_altmode *adev;
 +      struct typec_altmode *pdev;
        struct pd_mode_data *modep;
 +      u32 p[PD_MAX_PAYLOAD];
        int rlen = 0;
 -      u16 svid;
 +      int cmd_type;
 +      int cmd;
        int i;
  
 +      for (i = 0; i < cnt; i++)
 +              p[i] = le32_to_cpu(payload[i]);
 +
 +      cmd_type = PD_VDO_CMDT(p[0]);
 +      cmd = PD_VDO_CMD(p[0]);
 +
        tcpm_log(port, "Rx VDM cmd 0x%x type %d cmd %d len %d",
 -               p0, cmd_type, cmd, cnt);
 +               p[0], cmd_type, cmd, cnt);
  
        modep = &port->mode_data;
  
 +      adev = typec_match_altmode(port->port_altmode, ALTMODE_DISCOVERY_MAX,
 +                                 PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));
 +
 +      pdev = typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX,
 +                                 PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));
 +
        switch (cmd_type) {
        case CMDT_INIT:
                switch (cmd) {
                case CMD_EXIT_MODE:
                        break;
                case CMD_ATTENTION:
 -                      break;
 +                      /* Attention command does not have response */
 +                      typec_altmode_attention(adev, p[1]);
 +                      return 0;
                default:
                        break;
                }
                if (rlen >= 1) {
 -                      response[0] = p0 | VDO_CMDT(CMDT_RSP_ACK);
 +                      response[0] = p[0] | VDO_CMDT(CMDT_RSP_ACK);
                } else if (rlen == 0) {
 -                      response[0] = p0 | VDO_CMDT(CMDT_RSP_NAK);
 +                      response[0] = p[0] | VDO_CMDT(CMDT_RSP_NAK);
                        rlen = 1;
                } else {
 -                      response[0] = p0 | VDO_CMDT(CMDT_RSP_BUSY);
 +                      response[0] = p[0] | VDO_CMDT(CMDT_RSP_BUSY);
                        rlen = 1;
                }
                break;
                        svdm_consume_modes(port, payload, cnt);
                        modep->svid_index++;
                        if (modep->svid_index < modep->nsvids) {
 -                              svid = modep->svids[modep->svid_index];
 +                              u16 svid = modep->svids[modep->svid_index];
                                response[0] = VDO(svid, 1, CMD_DISCOVER_MODES);
                                rlen = 1;
                        } else {
 -                              /* enter alternate mode if/when implemented */
 +                              tcpm_register_partner_altmodes(port);
                        }
                        break;
                case CMD_ENTER_MODE:
 +                      typec_altmode_update_active(pdev, true);
 +
 +                      if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) {
 +                              response[0] = VDO(adev->svid, 1, CMD_EXIT_MODE);
 +                              response[0] |= VDO_OPOS(adev->mode);
 +                              return 1;
 +                      }
 +                      return 0;
 +              case CMD_EXIT_MODE:
 +                      typec_altmode_update_active(pdev, false);
 +
 +                      /* Back to USB Operation */
 +                      WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB,
 +                                                   NULL));
 +                      break;
 +              default:
 +                      break;
 +              }
 +              break;
 +      case CMDT_RSP_NAK:
 +              switch (cmd) {
 +              case CMD_ENTER_MODE:
 +                      /* Back to USB Operation */
 +                      WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB,
 +                                                   NULL));
                        break;
                default:
                        break;
                break;
        }
  
 +      /* Informing the alternate mode drivers about everything */
 +      typec_altmode_vdm(adev, p[0], &p[1], cnt);
 +
        return rlen;
  }
  
@@@ -1463,57 -1416,6 +1463,57 @@@ static int tcpm_validate_caps(struct tc
        return 0;
  }
  
 +static int tcpm_altmode_enter(struct typec_altmode *altmode)
 +{
 +      struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
 +      u32 header;
 +
 +      mutex_lock(&port->lock);
 +      header = VDO(altmode->svid, 1, CMD_ENTER_MODE);
 +      header |= VDO_OPOS(altmode->mode);
 +
 +      tcpm_queue_vdm(port, header, NULL, 0);
 +      mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
 +      mutex_unlock(&port->lock);
 +
 +      return 0;
 +}
 +
 +static int tcpm_altmode_exit(struct typec_altmode *altmode)
 +{
 +      struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
 +      u32 header;
 +
 +      mutex_lock(&port->lock);
 +      header = VDO(altmode->svid, 1, CMD_EXIT_MODE);
 +      header |= VDO_OPOS(altmode->mode);
 +
 +      tcpm_queue_vdm(port, header, NULL, 0);
 +      mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
 +      mutex_unlock(&port->lock);
 +
 +      return 0;
 +}
 +
 +static int tcpm_altmode_vdm(struct typec_altmode *altmode,
 +                          u32 header, const u32 *data, int count)
 +{
 +      struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
 +
 +      mutex_lock(&port->lock);
 +      tcpm_queue_vdm(port, header, data, count - 1);
 +      mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
 +      mutex_unlock(&port->lock);
 +
 +      return 0;
 +}
 +
 +static const struct typec_altmode_ops tcpm_altmode_ops = {
 +      .enter = tcpm_altmode_enter,
 +      .exit = tcpm_altmode_exit,
 +      .vdm = tcpm_altmode_vdm,
 +};
 +
  /*
   * PD (data, control) command handling functions
   */
@@@ -2238,7 -2140,7 +2238,7 @@@ static unsigned int tcpm_pd_select_pps_
                         * PPS APDO. Again skip the first sink PDO as this will
                         * always be 5V 3A.
                         */
-                       for (j = i; j < port->nr_snk_pdo; j++) {
+                       for (j = 1; j < port->nr_snk_pdo; j++) {
                                pdo = port->snk_pdo[j];
  
                                switch (pdo_type(pdo)) {
@@@ -2533,15 -2435,15 +2533,15 @@@ static int tcpm_set_charge(struct tcpm_
        return 0;
  }
  
 -static bool tcpm_start_drp_toggling(struct tcpm_port *port)
 +static bool tcpm_start_drp_toggling(struct tcpm_port *port,
 +                                  enum typec_cc_status cc)
  {
        int ret;
  
        if (port->tcpc->start_drp_toggling &&
            port->port_type == TYPEC_PORT_DRP) {
                tcpm_log_force(port, "Start DRP toggling");
 -              ret = port->tcpc->start_drp_toggling(port->tcpc,
 -                                                   tcpm_rp_cc(port));
 +              ret = port->tcpc->start_drp_toggling(port->tcpc, cc);
                if (!ret)
                        return true;
        }
@@@ -2645,7 -2547,7 +2645,7 @@@ out_disable_vconn
  out_disable_pd:
        port->tcpc->set_pd_rx(port->tcpc, false);
  out_disable_mux:
 -      tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE,
 +      tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE,
                     TYPEC_ORIENTATION_NONE);
        return ret;
  }
@@@ -2691,7 -2593,7 +2691,7 @@@ static void tcpm_reset_port(struct tcpm
        tcpm_init_vconn(port);
        tcpm_set_current_limit(port, 0, 0);
        tcpm_set_polarity(port, TYPEC_POLARITY_CC1);
 -      tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE,
 +      tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE,
                     TYPEC_ORIENTATION_NONE);
        tcpm_set_attached_state(port, false);
        port->try_src_count = 0;
@@@ -2847,7 -2749,7 +2847,7 @@@ static void run_state_machine(struct tc
                if (!port->non_pd_role_swap)
                        tcpm_swap_complete(port, -ENOTCONN);
                tcpm_src_detach(port);
 -              if (tcpm_start_drp_toggling(port)) {
 +              if (tcpm_start_drp_toggling(port, tcpm_rp_cc(port))) {
                        tcpm_set_state(port, DRP_TOGGLING, 0);
                        break;
                }
                        tcpm_swap_complete(port, -ENOTCONN);
                tcpm_pps_complete(port, -ENOTCONN);
                tcpm_snk_detach(port);
 -              if (tcpm_start_drp_toggling(port)) {
 +              if (tcpm_start_drp_toggling(port, TYPEC_CC_RD)) {
                        tcpm_set_state(port, DRP_TOGGLING, 0);
                        break;
                }
@@@ -4337,81 -4239,6 +4337,81 @@@ static int tcpm_copy_vdos(u32 *dest_vdo
        return nr_vdo;
  }
  
 +static int tcpm_fw_get_caps(struct tcpm_port *port,
 +                          struct fwnode_handle *fwnode)
 +{
 +      const char *cap_str;
 +      int ret;
 +      u32 mw;
 +
 +      if (!fwnode)
 +              return -EINVAL;
 +
 +      /* USB data support is optional */
 +      ret = fwnode_property_read_string(fwnode, "data-role", &cap_str);
 +      if (ret == 0) {
 +              port->typec_caps.data = typec_find_port_data_role(cap_str);
 +              if (port->typec_caps.data < 0)
 +                      return -EINVAL;
 +      }
 +
 +      ret = fwnode_property_read_string(fwnode, "power-role", &cap_str);
 +      if (ret < 0)
 +              return ret;
 +
 +      port->typec_caps.type = typec_find_port_power_role(cap_str);
 +      if (port->typec_caps.type < 0)
 +              return -EINVAL;
 +      port->port_type = port->typec_caps.type;
 +
 +      if (port->port_type == TYPEC_PORT_SNK)
 +              goto sink;
 +
 +      /* Get source pdos */
 +      ret = fwnode_property_read_u32_array(fwnode, "source-pdos",
 +                                           NULL, 0);
 +      if (ret <= 0)
 +              return -EINVAL;
 +
 +      port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS);
 +      ret = fwnode_property_read_u32_array(fwnode, "source-pdos",
 +                                           port->src_pdo, port->nr_src_pdo);
 +      if ((ret < 0) || tcpm_validate_caps(port, port->src_pdo,
 +                                          port->nr_src_pdo))
 +              return -EINVAL;
 +
 +      if (port->port_type == TYPEC_PORT_SRC)
 +              return 0;
 +
 +      /* Get the preferred power role for DRP */
 +      ret = fwnode_property_read_string(fwnode, "try-power-role", &cap_str);
 +      if (ret < 0)
 +              return ret;
 +
 +      port->typec_caps.prefer_role = typec_find_power_role(cap_str);
 +      if (port->typec_caps.prefer_role < 0)
 +              return -EINVAL;
 +sink:
 +      /* Get sink pdos */
 +      ret = fwnode_property_read_u32_array(fwnode, "sink-pdos",
 +                                           NULL, 0);
 +      if (ret <= 0)
 +              return -EINVAL;
 +
 +      port->nr_snk_pdo = min(ret, PDO_MAX_OBJECTS);
 +      ret = fwnode_property_read_u32_array(fwnode, "sink-pdos",
 +                                           port->snk_pdo, port->nr_snk_pdo);
 +      if ((ret < 0) || tcpm_validate_caps(port, port->snk_pdo,
 +                                          port->nr_snk_pdo))
 +              return -EINVAL;
 +
 +      if (fwnode_property_read_u32(fwnode, "op-sink-microwatt", &mw) < 0)
 +              return -EINVAL;
 +      port->operating_snk_mw = mw / 1000;
 +
 +      return 0;
 +}
 +
  int tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo,
                                    unsigned int nr_pdo)
  {
@@@ -4697,36 -4524,12 +4697,36 @@@ static int devm_tcpm_psy_register(struc
        return PTR_ERR_OR_ZERO(port->psy);
  }
  
 +static int tcpm_copy_caps(struct tcpm_port *port,
 +                        const struct tcpc_config *tcfg)
 +{
 +      if (tcpm_validate_caps(port, tcfg->src_pdo, tcfg->nr_src_pdo) ||
 +          tcpm_validate_caps(port, tcfg->snk_pdo, tcfg->nr_snk_pdo))
 +              return -EINVAL;
 +
 +      port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcfg->src_pdo,
 +                                        tcfg->nr_src_pdo);
 +      port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcfg->snk_pdo,
 +                                        tcfg->nr_snk_pdo);
 +
 +      port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcfg->snk_vdo,
 +                                        tcfg->nr_snk_vdo);
 +
 +      port->operating_snk_mw = tcfg->operating_snk_mw;
 +
 +      port->typec_caps.prefer_role = tcfg->default_role;
 +      port->typec_caps.type = tcfg->type;
 +      port->typec_caps.data = tcfg->data;
 +
 +      return 0;
 +}
 +
  struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
  {
        struct tcpm_port *port;
        int i, err;
  
 -      if (!dev || !tcpc || !tcpc->config ||
 +      if (!dev || !tcpc ||
            !tcpc->get_vbus || !tcpc->set_cc || !tcpc->get_cc ||
            !tcpc->set_polarity || !tcpc->set_vconn || !tcpc->set_vbus ||
            !tcpc->set_pd_rx || !tcpc->set_roles || !tcpc->pd_transmit)
        init_completion(&port->pps_complete);
        tcpm_debugfs_init(port);
  
 -      if (tcpm_validate_caps(port, tcpc->config->src_pdo,
 -                             tcpc->config->nr_src_pdo) ||
 -          tcpm_validate_caps(port, tcpc->config->snk_pdo,
 -                             tcpc->config->nr_snk_pdo)) {
 -              err = -EINVAL;
 +      err = tcpm_fw_get_caps(port, tcpc->fwnode);
 +      if ((err < 0) && tcpc->config)
 +              err = tcpm_copy_caps(port, tcpc->config);
 +      if (err < 0)
                goto out_destroy_wq;
 -      }
 -      port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcpc->config->src_pdo,
 -                                        tcpc->config->nr_src_pdo);
 -      port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo,
 -                                        tcpc->config->nr_snk_pdo);
 -      port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcpc->config->snk_vdo,
 -                                        tcpc->config->nr_snk_vdo);
 -
 -      port->operating_snk_mw = tcpc->config->operating_snk_mw;
 -      if (!tcpc->config->try_role_hw)
 -              port->try_role = tcpc->config->default_role;
 +
 +      if (!tcpc->config || !tcpc->config->try_role_hw)
 +              port->try_role = port->typec_caps.prefer_role;
        else
                port->try_role = TYPEC_NO_PREFERRED_ROLE;
  
 -      port->typec_caps.prefer_role = tcpc->config->default_role;
 -      port->typec_caps.type = tcpc->config->type;
 -      port->typec_caps.data = tcpc->config->data;
 +      port->typec_caps.fwnode = tcpc->fwnode;
        port->typec_caps.revision = 0x0120;     /* Type-C spec release 1.2 */
        port->typec_caps.pd_revision = 0x0300;  /* USB-PD spec release 3.0 */
        port->typec_caps.dr_set = tcpm_dr_set;
        port->typec_caps.port_type_set = tcpm_port_type_set;
  
        port->partner_desc.identity = &port->partner_ident;
 -      port->port_type = tcpc->config->type;
 +      port->port_type = port->typec_caps.type;
  
        port->role_sw = usb_role_switch_get(port->dev);
        if (IS_ERR(port->role_sw)) {
                goto out_destroy_wq;
        }
  
 -      if (tcpc->config->alt_modes) {
 +      if (tcpc->config && tcpc->config->alt_modes) {
                const struct typec_altmode_desc *paltmode = tcpc->config->alt_modes;
  
                i = 0;
                                         dev_name(dev), paltmode->svid);
                                break;
                        }
 +                      typec_altmode_set_drvdata(alt, port);
 +                      alt->ops = &tcpm_altmode_ops;
                        port->port_altmode[i] = alt;
                        i++;
                        paltmode++;
This page took 0.185461 seconds and 4 git commands to generate.