brintos

brintos / linux-shallow public Read only

0
0
Text · 14.4 KiB · 463b1c5 Raw
552 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.4 * Copyright (c) 2022, Linaro Ltd5 */6#include <linux/auxiliary_bus.h>7#include <linux/bitfield.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/of_device.h>11#include <linux/mutex.h>12#include <linux/property.h>13#include <linux/soc/qcom/pdr.h>14#include <drm/bridge/aux-bridge.h>15 16#include <linux/usb/typec_altmode.h>17#include <linux/usb/typec_dp.h>18#include <linux/usb/typec_mux.h>19#include <linux/usb/typec_retimer.h>20 21#include <linux/soc/qcom/pmic_glink.h>22 23#define PMIC_GLINK_MAX_PORTS	324 25#define USBC_SC8180X_NOTIFY_IND	0x1326#define USBC_CMD_WRITE_REQ      0x1527#define USBC_NOTIFY_IND		0x1628 29#define ALTMODE_PAN_EN		0x1030#define ALTMODE_PAN_ACK		0x1131 32struct usbc_write_req {33	struct pmic_glink_hdr   hdr;34	__le32 cmd;35	__le32 arg;36	__le32 reserved;37};38 39#define NOTIFY_PAYLOAD_SIZE 1640struct usbc_notify {41	struct pmic_glink_hdr hdr;42	char payload[NOTIFY_PAYLOAD_SIZE];43	u32 reserved;44};45 46struct usbc_sc8180x_notify {47	struct pmic_glink_hdr hdr;48	__le32 notification;49	__le32 reserved[2];50};51 52enum pmic_glink_altmode_pin_assignment {53	DPAM_HPD_OUT,54	DPAM_HPD_A,55	DPAM_HPD_B,56	DPAM_HPD_C,57	DPAM_HPD_D,58	DPAM_HPD_E,59	DPAM_HPD_F,60};61 62struct pmic_glink_altmode;63 64#define work_to_altmode_port(w) container_of((w), struct pmic_glink_altmode_port, work)65 66struct pmic_glink_altmode_port {67	struct pmic_glink_altmode *altmode;68	unsigned int index;69 70	struct typec_switch *typec_switch;71	struct typec_mux *typec_mux;72	struct typec_mux_state state;73	struct typec_retimer *typec_retimer;74	struct typec_retimer_state retimer_state;75	struct typec_altmode dp_alt;76 77	struct work_struct work;78 79	struct auxiliary_device *bridge;80 81	enum typec_orientation orientation;82	u16 svid;83	u8 dp_data;84	u8 mode;85	u8 hpd_state;86	u8 hpd_irq;87};88 89#define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work)90 91struct pmic_glink_altmode {92	struct device *dev;93 94	unsigned int owner_id;95 96	/* To synchronize WRITE_REQ acks */97	struct mutex lock;98 99	struct completion pan_ack;100	struct pmic_glink_client *client;101 102	struct work_struct enable_work;103 104	struct pmic_glink_altmode_port ports[PMIC_GLINK_MAX_PORTS];105};106 107static int pmic_glink_altmode_request(struct pmic_glink_altmode *altmode, u32 cmd, u32 arg)108{109	struct usbc_write_req req = {};110	unsigned long left;111	int ret;112 113	/*114	 * The USBC_CMD_WRITE_REQ ack doesn't identify the request, so wait for115	 * one ack at a time.116	 */117	mutex_lock(&altmode->lock);118 119	req.hdr.owner = cpu_to_le32(altmode->owner_id);120	req.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP);121	req.hdr.opcode = cpu_to_le32(USBC_CMD_WRITE_REQ);122	req.cmd = cpu_to_le32(cmd);123	req.arg = cpu_to_le32(arg);124 125	ret = pmic_glink_send(altmode->client, &req, sizeof(req));126	if (ret) {127		dev_err(altmode->dev, "failed to send altmode request: %#x (%d)\n", cmd, ret);128		goto out_unlock;129	}130 131	left = wait_for_completion_timeout(&altmode->pan_ack, 5 * HZ);132	if (!left) {133		dev_err(altmode->dev, "timeout waiting for altmode request ack for: %#x\n", cmd);134		ret = -ETIMEDOUT;135	}136 137out_unlock:138	mutex_unlock(&altmode->lock);139	return ret;140}141 142static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode,143					 struct pmic_glink_altmode_port *port,144					 u8 mode, bool hpd_state,145					 bool hpd_irq)146{147	struct typec_displayport_data dp_data = {};148	int ret;149 150	dp_data.status = DP_STATUS_ENABLED;151	if (hpd_state)152		dp_data.status |= DP_STATUS_HPD_STATE;153	if (hpd_irq)154		dp_data.status |= DP_STATUS_IRQ_HPD;155	dp_data.conf = DP_CONF_SET_PIN_ASSIGN(mode);156 157	port->state.alt = &port->dp_alt;158	port->state.data = &dp_data;159	port->state.mode = TYPEC_MODAL_STATE(mode);160 161	ret = typec_mux_set(port->typec_mux, &port->state);162	if (ret)163		dev_err(altmode->dev, "failed to switch mux to DP: %d\n", ret);164 165	port->retimer_state.alt = &port->dp_alt;166	port->retimer_state.data = &dp_data;167	port->retimer_state.mode = TYPEC_MODAL_STATE(mode);168 169	ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);170	if (ret)171		dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret);172}173 174static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode,175					  struct pmic_glink_altmode_port *port)176{177	int ret;178 179	port->state.alt = NULL;180	port->state.data = NULL;181	port->state.mode = TYPEC_STATE_USB;182 183	ret = typec_mux_set(port->typec_mux, &port->state);184	if (ret)185		dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret);186 187	port->retimer_state.alt = NULL;188	port->retimer_state.data = NULL;189	port->retimer_state.mode = TYPEC_STATE_USB;190 191	ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);192	if (ret)193		dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);194}195 196static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode,197				    struct pmic_glink_altmode_port *port)198{199	int ret;200 201	port->state.alt = NULL;202	port->state.data = NULL;203	port->state.mode = TYPEC_STATE_SAFE;204 205	ret = typec_mux_set(port->typec_mux, &port->state);206	if (ret)207		dev_err(altmode->dev, "failed to switch mux to safe mode: %d\n", ret);208 209	port->retimer_state.alt = NULL;210	port->retimer_state.data = NULL;211	port->retimer_state.mode = TYPEC_STATE_SAFE;212 213	ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);214	if (ret)215		dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);216}217 218static void pmic_glink_altmode_worker(struct work_struct *work)219{220	struct pmic_glink_altmode_port *alt_port = work_to_altmode_port(work);221	struct pmic_glink_altmode *altmode = alt_port->altmode;222 223	typec_switch_set(alt_port->typec_switch, alt_port->orientation);224 225	if (alt_port->svid == USB_TYPEC_DP_SID && alt_port->mode == 0xff)226		pmic_glink_altmode_safe(altmode, alt_port);227	else if (alt_port->svid == USB_TYPEC_DP_SID)228		pmic_glink_altmode_enable_dp(altmode, alt_port, alt_port->mode,229					     alt_port->hpd_state, alt_port->hpd_irq);230	else231		pmic_glink_altmode_enable_usb(altmode, alt_port);232 233	drm_aux_hpd_bridge_notify(&alt_port->bridge->dev,234				  alt_port->hpd_state ?235				  connector_status_connected :236				  connector_status_disconnected);237 238	pmic_glink_altmode_request(altmode, ALTMODE_PAN_ACK, alt_port->index);239}240 241static enum typec_orientation pmic_glink_altmode_orientation(unsigned int orientation)242{243	if (orientation == 0)244		return TYPEC_ORIENTATION_NORMAL;245	else if (orientation == 1)246		return TYPEC_ORIENTATION_REVERSE;247	else248		return TYPEC_ORIENTATION_NONE;249}250 251#define SC8180X_PORT_MASK		0x000000ff252#define SC8180X_ORIENTATION_MASK	0x0000ff00253#define SC8180X_MUX_MASK		0x00ff0000254#define SC8180X_MODE_MASK		0x3f000000255#define SC8180X_HPD_STATE_MASK		0x40000000256#define SC8180X_HPD_IRQ_MASK		0x80000000257 258static void pmic_glink_altmode_sc8180xp_notify(struct pmic_glink_altmode *altmode,259					       const void *data, size_t len)260{261	struct pmic_glink_altmode_port *alt_port;262	const struct usbc_sc8180x_notify *msg;263	u32 notification;264	u8 orientation;265	u8 hpd_state;266	u8 hpd_irq;267	u16 svid;268	u8 port;269	u8 mode;270	u8 mux;271 272	if (len != sizeof(*msg)) {273		dev_warn(altmode->dev, "invalid length of USBC_NOTIFY indication: %zd\n", len);274		return;275	}276 277	msg = data;278	notification = le32_to_cpu(msg->notification);279	port = FIELD_GET(SC8180X_PORT_MASK, notification);280	orientation = FIELD_GET(SC8180X_ORIENTATION_MASK, notification);281	mux = FIELD_GET(SC8180X_MUX_MASK, notification);282	mode = FIELD_GET(SC8180X_MODE_MASK, notification);283	hpd_state = FIELD_GET(SC8180X_HPD_STATE_MASK, notification);284	hpd_irq = FIELD_GET(SC8180X_HPD_IRQ_MASK, notification);285 286	svid = mux == 2 ? USB_TYPEC_DP_SID : 0;287 288	if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) {289		dev_dbg(altmode->dev, "notification on undefined port %d\n", port);290		return;291	}292 293	alt_port = &altmode->ports[port];294	alt_port->orientation = pmic_glink_altmode_orientation(orientation);295	alt_port->svid = svid;296	alt_port->mode = mode;297	alt_port->hpd_state = hpd_state;298	alt_port->hpd_irq = hpd_irq;299	schedule_work(&alt_port->work);300}301 302#define SC8280XP_DPAM_MASK	0x3f303#define SC8280XP_HPD_STATE_MASK BIT(6)304#define SC8280XP_HPD_IRQ_MASK	BIT(7)305 306static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmode,307					       u16 svid, const void *data, size_t len)308{309	struct pmic_glink_altmode_port *alt_port;310	const struct usbc_notify *notify;311	u8 orientation;312	u8 hpd_state;313	u8 hpd_irq;314	u8 mode;315	u8 port;316 317	if (len != sizeof(*notify)) {318		dev_warn(altmode->dev, "invalid length USBC_NOTIFY_IND: %zd\n",319			 len);320		return;321	}322 323	notify = data;324 325	port = notify->payload[0];326	orientation = notify->payload[1];327	mode = FIELD_GET(SC8280XP_DPAM_MASK, notify->payload[8]) - DPAM_HPD_A;328	hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]);329	hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]);330 331	if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) {332		dev_dbg(altmode->dev, "notification on undefined port %d\n", port);333		return;334	}335 336	alt_port = &altmode->ports[port];337	alt_port->orientation = pmic_glink_altmode_orientation(orientation);338	alt_port->svid = svid;339	alt_port->mode = mode;340	alt_port->hpd_state = hpd_state;341	alt_port->hpd_irq = hpd_irq;342	schedule_work(&alt_port->work);343}344 345static void pmic_glink_altmode_callback(const void *data, size_t len, void *priv)346{347	struct pmic_glink_altmode *altmode = priv;348	const struct pmic_glink_hdr *hdr = data;349	u16 opcode;350	u16 svid;351 352	opcode = le32_to_cpu(hdr->opcode) & 0xff;353	svid = le32_to_cpu(hdr->opcode) >> 16;354 355	switch (opcode) {356	case USBC_CMD_WRITE_REQ:357		complete(&altmode->pan_ack);358		break;359	case USBC_NOTIFY_IND:360		pmic_glink_altmode_sc8280xp_notify(altmode, svid, data, len);361		break;362	case USBC_SC8180X_NOTIFY_IND:363		pmic_glink_altmode_sc8180xp_notify(altmode, data, len);364		break;365	}366}367 368static void pmic_glink_altmode_put_retimer(void *data)369{370	typec_retimer_put(data);371}372 373static void pmic_glink_altmode_put_mux(void *data)374{375	typec_mux_put(data);376}377 378static void pmic_glink_altmode_put_switch(void *data)379{380	typec_switch_put(data);381}382 383static void pmic_glink_altmode_enable_worker(struct work_struct *work)384{385	struct pmic_glink_altmode *altmode = work_to_altmode(work);386	int ret;387 388	ret = pmic_glink_altmode_request(altmode, ALTMODE_PAN_EN, 0);389	if (ret)390		dev_err(altmode->dev, "failed to request altmode notifications: %d\n", ret);391}392 393static void pmic_glink_altmode_pdr_notify(void *priv, int state)394{395	struct pmic_glink_altmode *altmode = priv;396 397	if (state == SERVREG_SERVICE_STATE_UP)398		schedule_work(&altmode->enable_work);399}400 401static const struct of_device_id pmic_glink_altmode_of_quirks[] = {402	{ .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)PMIC_GLINK_OWNER_USBC },403	{}404};405 406static int pmic_glink_altmode_probe(struct auxiliary_device *adev,407				    const struct auxiliary_device_id *id)408{409	struct pmic_glink_altmode_port *alt_port;410	struct pmic_glink_altmode *altmode;411	const struct of_device_id *match;412	struct fwnode_handle *fwnode;413	struct device *dev = &adev->dev;414	u32 port;415	int ret;416 417	altmode = devm_kzalloc(dev, sizeof(*altmode), GFP_KERNEL);418	if (!altmode)419		return -ENOMEM;420 421	altmode->dev = dev;422 423	match = of_match_device(pmic_glink_altmode_of_quirks, dev->parent);424	if (match)425		altmode->owner_id = (unsigned long)match->data;426	else427		altmode->owner_id = PMIC_GLINK_OWNER_USBC_PAN;428 429	INIT_WORK(&altmode->enable_work, pmic_glink_altmode_enable_worker);430	init_completion(&altmode->pan_ack);431	mutex_init(&altmode->lock);432 433	device_for_each_child_node(dev, fwnode) {434		ret = fwnode_property_read_u32(fwnode, "reg", &port);435		if (ret < 0) {436			dev_err(dev, "missing reg property of %pOFn\n", fwnode);437			fwnode_handle_put(fwnode);438			return ret;439		}440 441		if (port >= ARRAY_SIZE(altmode->ports)) {442			dev_warn(dev, "invalid connector number, ignoring\n");443			continue;444		}445 446		if (altmode->ports[port].altmode) {447			dev_err(dev, "multiple connector definition for port %u\n", port);448			fwnode_handle_put(fwnode);449			return -EINVAL;450		}451 452		alt_port = &altmode->ports[port];453		alt_port->altmode = altmode;454		alt_port->index = port;455		INIT_WORK(&alt_port->work, pmic_glink_altmode_worker);456 457		alt_port->bridge = devm_drm_dp_hpd_bridge_alloc(dev, to_of_node(fwnode));458		if (IS_ERR(alt_port->bridge)) {459			fwnode_handle_put(fwnode);460			return PTR_ERR(alt_port->bridge);461		}462 463		alt_port->dp_alt.svid = USB_TYPEC_DP_SID;464		alt_port->dp_alt.mode = USB_TYPEC_DP_MODE;465		alt_port->dp_alt.active = 1;466 467		alt_port->typec_mux = fwnode_typec_mux_get(fwnode);468		if (IS_ERR(alt_port->typec_mux)) {469			fwnode_handle_put(fwnode);470			return dev_err_probe(dev, PTR_ERR(alt_port->typec_mux),471					     "failed to acquire mode-switch for port: %d\n",472					     port);473		}474 475		ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_mux,476					       alt_port->typec_mux);477		if (ret) {478			fwnode_handle_put(fwnode);479			return ret;480		}481 482		alt_port->typec_retimer = fwnode_typec_retimer_get(fwnode);483		if (IS_ERR(alt_port->typec_retimer)) {484			fwnode_handle_put(fwnode);485			return dev_err_probe(dev, PTR_ERR(alt_port->typec_retimer),486					     "failed to acquire retimer-switch for port: %d\n",487					     port);488		}489 490		ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_retimer,491					       alt_port->typec_retimer);492		if (ret) {493			fwnode_handle_put(fwnode);494			return ret;495		}496 497		alt_port->typec_switch = fwnode_typec_switch_get(fwnode);498		if (IS_ERR(alt_port->typec_switch)) {499			fwnode_handle_put(fwnode);500			return dev_err_probe(dev, PTR_ERR(alt_port->typec_switch),501					     "failed to acquire orientation-switch for port: %d\n",502					     port);503		}504 505		ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_switch,506					       alt_port->typec_switch);507		if (ret) {508			fwnode_handle_put(fwnode);509			return ret;510		}511	}512 513	for (port = 0; port < ARRAY_SIZE(altmode->ports); port++) {514		alt_port = &altmode->ports[port];515		if (!alt_port->bridge)516			continue;517 518		ret = devm_drm_dp_hpd_bridge_add(dev, alt_port->bridge);519		if (ret)520			return ret;521	}522 523	altmode->client = devm_pmic_glink_client_alloc(dev,524						       altmode->owner_id,525						       pmic_glink_altmode_callback,526						       pmic_glink_altmode_pdr_notify,527						       altmode);528	if (IS_ERR(altmode->client))529		return PTR_ERR(altmode->client);530 531	pmic_glink_client_register(altmode->client);532 533	return 0;534}535 536static const struct auxiliary_device_id pmic_glink_altmode_id_table[] = {537	{ .name = "pmic_glink.altmode", },538	{},539};540MODULE_DEVICE_TABLE(auxiliary, pmic_glink_altmode_id_table);541 542static struct auxiliary_driver pmic_glink_altmode_driver = {543	.name = "pmic_glink_altmode",544	.probe = pmic_glink_altmode_probe,545	.id_table = pmic_glink_altmode_id_table,546};547 548module_auxiliary_driver(pmic_glink_altmode_driver);549 550MODULE_DESCRIPTION("Qualcomm PMIC GLINK Altmode driver");551MODULE_LICENSE("GPL");552