| .. | .. |
|---|
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
|---|
| 2 | | -/** |
|---|
| 2 | +/* |
|---|
| 3 | 3 | * Bus for USB Type-C Alternate Modes |
|---|
| 4 | 4 | * |
|---|
| 5 | 5 | * Copyright (C) 2018 Intel Corporation |
|---|
| .. | .. |
|---|
| 10 | 10 | |
|---|
| 11 | 11 | #include "bus.h" |
|---|
| 12 | 12 | |
|---|
| 13 | | -static inline int typec_altmode_set_mux(struct altmode *alt, u8 state) |
|---|
| 13 | +static inline int |
|---|
| 14 | +typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data) |
|---|
| 14 | 15 | { |
|---|
| 15 | | - return alt->mux ? alt->mux->set(alt->mux, state) : 0; |
|---|
| 16 | + struct typec_mux_state state; |
|---|
| 17 | + |
|---|
| 18 | + if (!alt->mux) |
|---|
| 19 | + return 0; |
|---|
| 20 | + |
|---|
| 21 | + state.alt = &alt->adev; |
|---|
| 22 | + state.mode = conf; |
|---|
| 23 | + state.data = data; |
|---|
| 24 | + |
|---|
| 25 | + return alt->mux->set(alt->mux, &state); |
|---|
| 16 | 26 | } |
|---|
| 17 | 27 | |
|---|
| 18 | | -static int typec_altmode_set_state(struct typec_altmode *adev, int state) |
|---|
| 28 | +static int typec_altmode_set_state(struct typec_altmode *adev, |
|---|
| 29 | + unsigned long conf, void *data) |
|---|
| 19 | 30 | { |
|---|
| 20 | 31 | bool is_port = is_typec_port(adev->dev.parent); |
|---|
| 21 | 32 | struct altmode *port_altmode; |
|---|
| 22 | | - int ret; |
|---|
| 23 | 33 | |
|---|
| 24 | 34 | port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner; |
|---|
| 25 | 35 | |
|---|
| 26 | | - ret = typec_altmode_set_mux(port_altmode, state); |
|---|
| 27 | | - if (ret) |
|---|
| 28 | | - return ret; |
|---|
| 29 | | - |
|---|
| 30 | | - blocking_notifier_call_chain(&port_altmode->nh, state, NULL); |
|---|
| 31 | | - |
|---|
| 32 | | - return 0; |
|---|
| 36 | + return typec_altmode_set_mux(port_altmode, conf, data); |
|---|
| 33 | 37 | } |
|---|
| 34 | 38 | |
|---|
| 35 | 39 | /* -------------------------------------------------------------------------- */ |
|---|
| .. | .. |
|---|
| 67 | 71 | is_port = is_typec_port(adev->dev.parent); |
|---|
| 68 | 72 | partner = altmode->partner; |
|---|
| 69 | 73 | |
|---|
| 70 | | - ret = typec_altmode_set_mux(is_port ? altmode : partner, (u8)conf); |
|---|
| 74 | + ret = typec_altmode_set_mux(is_port ? altmode : partner, conf, data); |
|---|
| 71 | 75 | if (ret) |
|---|
| 72 | 76 | return ret; |
|---|
| 73 | | - |
|---|
| 74 | | - blocking_notifier_call_chain(is_port ? &altmode->nh : &partner->nh, |
|---|
| 75 | | - conf, data); |
|---|
| 76 | 77 | |
|---|
| 77 | 78 | if (partner->adev.ops && partner->adev.ops->notify) |
|---|
| 78 | 79 | return partner->adev.ops->notify(&partner->adev, conf, data); |
|---|
| .. | .. |
|---|
| 84 | 85 | /** |
|---|
| 85 | 86 | * typec_altmode_enter - Enter Mode |
|---|
| 86 | 87 | * @adev: The alternate mode |
|---|
| 88 | + * @vdo: VDO for the Enter Mode command |
|---|
| 87 | 89 | * |
|---|
| 88 | 90 | * The alternate mode drivers use this function to enter mode. The port drivers |
|---|
| 89 | 91 | * use this to inform the alternate mode drivers that the partner has initiated |
|---|
| 90 | | - * Enter Mode command. |
|---|
| 92 | + * Enter Mode command. If the alternate mode does not require VDO, @vdo must be |
|---|
| 93 | + * NULL. |
|---|
| 91 | 94 | */ |
|---|
| 92 | | -int typec_altmode_enter(struct typec_altmode *adev) |
|---|
| 95 | +int typec_altmode_enter(struct typec_altmode *adev, u32 *vdo) |
|---|
| 93 | 96 | { |
|---|
| 94 | 97 | struct altmode *partner = to_altmode(adev)->partner; |
|---|
| 95 | 98 | struct typec_altmode *pdev = &partner->adev; |
|---|
| .. | .. |
|---|
| 101 | 104 | if (!pdev->ops || !pdev->ops->enter) |
|---|
| 102 | 105 | return -EOPNOTSUPP; |
|---|
| 103 | 106 | |
|---|
| 107 | + if (is_typec_port(pdev->dev.parent) && !pdev->active) |
|---|
| 108 | + return -EPERM; |
|---|
| 109 | + |
|---|
| 104 | 110 | /* Moving to USB Safe State */ |
|---|
| 105 | | - ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); |
|---|
| 111 | + ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE, NULL); |
|---|
| 106 | 112 | if (ret) |
|---|
| 107 | 113 | return ret; |
|---|
| 108 | 114 | |
|---|
| 109 | 115 | /* Enter Mode */ |
|---|
| 110 | | - return pdev->ops->enter(pdev); |
|---|
| 116 | + return pdev->ops->enter(pdev, vdo); |
|---|
| 111 | 117 | } |
|---|
| 112 | 118 | EXPORT_SYMBOL_GPL(typec_altmode_enter); |
|---|
| 113 | 119 | |
|---|
| .. | .. |
|---|
| 130 | 136 | return -EOPNOTSUPP; |
|---|
| 131 | 137 | |
|---|
| 132 | 138 | /* Moving to USB Safe State */ |
|---|
| 133 | | - ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); |
|---|
| 139 | + ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE, NULL); |
|---|
| 134 | 140 | if (ret) |
|---|
| 135 | 141 | return ret; |
|---|
| 136 | 142 | |
|---|
| .. | .. |
|---|
| 386 | 392 | drv->remove(to_typec_altmode(dev)); |
|---|
| 387 | 393 | |
|---|
| 388 | 394 | if (adev->active) { |
|---|
| 389 | | - WARN_ON(typec_altmode_set_state(adev, TYPEC_STATE_SAFE)); |
|---|
| 395 | + WARN_ON(typec_altmode_set_state(adev, TYPEC_STATE_SAFE, NULL)); |
|---|
| 390 | 396 | typec_altmode_update_active(adev, false); |
|---|
| 391 | 397 | } |
|---|
| 392 | 398 | |
|---|