Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: typec: fsa4480: Add support to swap SBU orientation

On some hardware designs the AUX+/- lanes are connected reversed to
SBU1/2 compared to the expected design by FSA4480.

Made more complicated, the otherwise compatible Orient-Chip OCP96011
expects the lanes to be connected reversed compared to FSA4480.

* FSA4480 block diagram shows AUX+ connected to SBU2 and AUX- to SBU1.
* OCP96011 block diagram shows AUX+ connected to SBU1 and AUX- to SBU2.

So if OCP96011 is used as drop-in for FSA4480 then the orientation
handling in the driver needs to be reversed to match the expectation of
the OCP96011 hardware.

Support parsing the data-lanes parameter in the endpoint node to swap
this in the driver.

The parse_data_lanes_mapping function is mostly taken from nb7vpq904m.c.

Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
Link: https://lore.kernel.org/r/20231020-fsa4480-swap-v2-2-9a7f9bb59873@fairphone.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Luca Weiss and committed by
Greg Kroah-Hartman
cf07c55f fad89aa1

+71
+71
drivers/usb/typec/mux/fsa4480.c
··· 60 60 unsigned int svid; 61 61 62 62 u8 cur_enable; 63 + bool swap_sbu_lanes; 63 64 }; 64 65 65 66 static const struct regmap_config fsa4480_regmap_config = { ··· 76 75 bool reverse = (fsa->orientation == TYPEC_ORIENTATION_REVERSE); 77 76 u8 enable = FSA4480_ENABLE_DEVICE; 78 77 u8 sel = 0; 78 + 79 + if (fsa->swap_sbu_lanes) 80 + reverse = !reverse; 79 81 80 82 /* USB Mode */ 81 83 if (fsa->mode < TYPEC_STATE_MODAL || ··· 183 179 return ret; 184 180 } 185 181 182 + enum { 183 + NORMAL_LANE_MAPPING, 184 + INVERT_LANE_MAPPING, 185 + }; 186 + 187 + #define DATA_LANES_COUNT 2 188 + 189 + static const int supported_data_lane_mapping[][DATA_LANES_COUNT] = { 190 + [NORMAL_LANE_MAPPING] = { 0, 1 }, 191 + [INVERT_LANE_MAPPING] = { 1, 0 }, 192 + }; 193 + 194 + static int fsa4480_parse_data_lanes_mapping(struct fsa4480 *fsa) 195 + { 196 + struct fwnode_handle *ep; 197 + u32 data_lanes[DATA_LANES_COUNT]; 198 + int ret, i, j; 199 + 200 + ep = fwnode_graph_get_next_endpoint(dev_fwnode(&fsa->client->dev), NULL); 201 + if (!ep) 202 + return 0; 203 + 204 + ret = fwnode_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT); 205 + if (ret == -EINVAL) 206 + /* Property isn't here, consider default mapping */ 207 + goto out_done; 208 + if (ret) { 209 + dev_err(&fsa->client->dev, "invalid data-lanes property: %d\n", ret); 210 + goto out_error; 211 + } 212 + 213 + for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) { 214 + for (j = 0; j < DATA_LANES_COUNT; j++) { 215 + if (data_lanes[j] != supported_data_lane_mapping[i][j]) 216 + break; 217 + } 218 + 219 + if (j == DATA_LANES_COUNT) 220 + break; 221 + } 222 + 223 + switch (i) { 224 + case NORMAL_LANE_MAPPING: 225 + break; 226 + case INVERT_LANE_MAPPING: 227 + fsa->swap_sbu_lanes = true; 228 + break; 229 + default: 230 + dev_err(&fsa->client->dev, "invalid data-lanes mapping\n"); 231 + ret = -EINVAL; 232 + goto out_error; 233 + } 234 + 235 + out_done: 236 + ret = 0; 237 + 238 + out_error: 239 + fwnode_handle_put(ep); 240 + 241 + return ret; 242 + } 243 + 186 244 static int fsa4480_probe(struct i2c_client *client) 187 245 { 188 246 struct device *dev = &client->dev; 189 247 struct typec_switch_desc sw_desc = { }; 190 248 struct typec_mux_desc mux_desc = { }; 191 249 struct fsa4480 *fsa; 250 + int ret; 192 251 193 252 fsa = devm_kzalloc(dev, sizeof(*fsa), GFP_KERNEL); 194 253 if (!fsa) ··· 259 192 260 193 fsa->client = client; 261 194 mutex_init(&fsa->lock); 195 + 196 + ret = fsa4480_parse_data_lanes_mapping(fsa); 197 + if (ret) 198 + return ret; 262 199 263 200 fsa->regmap = devm_regmap_init_i2c(client, &fsa4480_regmap_config); 264 201 if (IS_ERR(fsa->regmap))