Commit cf07c55f authored by Luca Weiss's avatar Luca Weiss Committed by Greg Kroah-Hartman
Browse files

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: default avatarNeil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: default avatarLuca Weiss <luca.weiss@fairphone.com>
Link: https://lore.kernel.org/r/20231020-fsa4480-swap-v2-2-9a7f9bb59873@fairphone.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent fad89aa1
Loading
Loading
Loading
Loading
+71 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ struct fsa4480 {
	unsigned int svid;

	u8 cur_enable;
	bool swap_sbu_lanes;
};

static const struct regmap_config fsa4480_regmap_config = {
@@ -76,6 +77,9 @@ static int fsa4480_set(struct fsa4480 *fsa)
	u8 enable = FSA4480_ENABLE_DEVICE;
	u8 sel = 0;

	if (fsa->swap_sbu_lanes)
		reverse = !reverse;

	/* USB Mode */
	if (fsa->mode < TYPEC_STATE_MODAL ||
	    (!fsa->svid && (fsa->mode == TYPEC_MODE_USB2 ||
@@ -179,12 +183,75 @@ static int fsa4480_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *st
	return ret;
}

enum {
	NORMAL_LANE_MAPPING,
	INVERT_LANE_MAPPING,
};

#define DATA_LANES_COUNT	2

static const int supported_data_lane_mapping[][DATA_LANES_COUNT] = {
	[NORMAL_LANE_MAPPING] = { 0, 1 },
	[INVERT_LANE_MAPPING] = { 1, 0 },
};

static int fsa4480_parse_data_lanes_mapping(struct fsa4480 *fsa)
{
	struct fwnode_handle *ep;
	u32 data_lanes[DATA_LANES_COUNT];
	int ret, i, j;

	ep = fwnode_graph_get_next_endpoint(dev_fwnode(&fsa->client->dev), NULL);
	if (!ep)
		return 0;

	ret = fwnode_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
	if (ret == -EINVAL)
		/* Property isn't here, consider default mapping */
		goto out_done;
	if (ret) {
		dev_err(&fsa->client->dev, "invalid data-lanes property: %d\n", ret);
		goto out_error;
	}

	for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
		for (j = 0; j < DATA_LANES_COUNT; j++) {
			if (data_lanes[j] != supported_data_lane_mapping[i][j])
				break;
		}

		if (j == DATA_LANES_COUNT)
			break;
	}

	switch (i) {
	case NORMAL_LANE_MAPPING:
		break;
	case INVERT_LANE_MAPPING:
		fsa->swap_sbu_lanes = true;
		break;
	default:
		dev_err(&fsa->client->dev, "invalid data-lanes mapping\n");
		ret = -EINVAL;
		goto out_error;
	}

out_done:
	ret = 0;

out_error:
	fwnode_handle_put(ep);

	return ret;
}

static int fsa4480_probe(struct i2c_client *client)
{
	struct device *dev = &client->dev;
	struct typec_switch_desc sw_desc = { };
	struct typec_mux_desc mux_desc = { };
	struct fsa4480 *fsa;
	int ret;

	fsa = devm_kzalloc(dev, sizeof(*fsa), GFP_KERNEL);
	if (!fsa)
@@ -193,6 +260,10 @@ static int fsa4480_probe(struct i2c_client *client)
	fsa->client = client;
	mutex_init(&fsa->lock);

	ret = fsa4480_parse_data_lanes_mapping(fsa);
	if (ret)
		return ret;

	fsa->regmap = devm_regmap_init_i2c(client, &fsa4480_regmap_config);
	if (IS_ERR(fsa->regmap))
		return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n");