123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
- */
- #define pr_fmt(fmt) "altmode-glink: %s: " fmt, __func__
- #include <linux/debugfs.h>
- #include <linux/device.h>
- #include <linux/idr.h>
- #include <linux/ipc_logging.h>
- #include <linux/ktime.h>
- #include <linux/module.h>
- #include <linux/of_platform.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
- #include <linux/soc/qcom/altmode-glink.h>
- #include <linux/soc/qcom/pmic_glink.h>
- #define MSG_OWNER_USBC_PAN 32780
- #define MSG_TYPE_REQ_RESP 1
- #define NOTIFY_PAYLOAD_SIZE 16
- #define USBC_WRITE_BUFFER_SIZE 8
- #define USBC_CMD_WRITE_REQ 0x15
- #define USBC_NOTIFY_IND 0x16
- #define MAX_NUM_PORTS 4
- #define NUM_LOG_PAGES 10
- #define IDR_KEY_GEN(svid, ind) (((svid) << 8) | (ind))
- #define IDR_KEY(client) \
- IDR_KEY_GEN((client)->data.svid, (client)->port_index)
- #define altmode_dbg(fmt, ...) \
- do { \
- ipc_log_string(altmode_ipc_log, fmt, ##__VA_ARGS__); \
- pr_debug(fmt, ##__VA_ARGS__); \
- } while (0)
- struct usbc_notify_ind_msg {
- struct pmic_glink_hdr hdr;
- u8 payload[NOTIFY_PAYLOAD_SIZE];
- u32 reserved;
- };
- struct usbc_write_buffer_req_msg {
- struct pmic_glink_hdr hdr;
- u8 buf[USBC_WRITE_BUFFER_SIZE];
- u32 reserved;
- };
- /**
- * struct altmode_dev
- * Definition of an altmode device.
- *
- * @dev: Altmode parent device for all client devices
- * @pgclient: PMIC GLINK client to talk to remote subsystem
- * @client_idr: idr list for altmode clients
- * @client_lock: mutex protecting changes to client_idr
- * @d_node: Linked list node to string together multiple amdev's
- * @client_list: Linked list head keeping track of this device's clients
- * @pan_en_sent: Flag to ensure PAN Enable msg is sent only once
- * @send_pan_en_work: To schedule the sending of the PAN Enable message
- * @send_pan_ack_work: To schedule the sending of the PAN Ack message
- * @debugfs_dir: Dentry for debugfs directory "altmode"
- * @response_received: To detect remote subsystem response failures
- * @ack_port_index: Port index to ack for a nonexistent client
- */
- struct altmode_dev {
- struct device *dev;
- struct pmic_glink_client *pgclient;
- struct idr client_idr;
- struct mutex client_lock;
- struct list_head d_node;
- struct list_head client_list;
- atomic_t pan_en_sent;
- struct delayed_work send_pan_en_work;
- struct delayed_work send_pan_ack_work;
- struct dentry *debugfs_dir;
- struct completion response_received;
- u8 ack_port_index;
- };
- /**
- * struct altmode_client
- * Definition of a client of an altmode device.
- *
- * @amdev: Parent altmode device of this client
- * @data: Supplied by client driver during registration
- * @c_node: Linked list node for parent altmode device's client list
- * @port_index: Type-C port index assigned by remote subystem
- *
- * Note: The following members are for internal use only, clients should not
- * use them.
- *
- * @client_cb_work: Work to run client callback
- * @msg: Latest notify msg stashed to be retrieved in cb_work
- */
- struct altmode_client {
- struct altmode_dev *amdev;
- struct altmode_client_data data;
- struct list_head c_node;
- u8 port_index;
- struct work_struct client_cb_work;
- u8 msg[NOTIFY_PAYLOAD_SIZE];
- };
- /**
- * struct probe_notify_node
- * Linked list node to keep track of altmode clients who, by design,
- * register with the altmode framework before altmode probes.
- *
- * @node: Linked list node for probe_notify_list
- * @amdev_node: device_node of the altmode device of the client
- * @cb: Client's probe completion callback function
- * @priv: Pointer to client's driver data struct
- */
- struct probe_notify_node {
- struct list_head node;
- struct device_node *amdev_node;
- void (*cb)(void *priv);
- void *priv;
- };
- /**
- * struct list_head probe_notify_list
- * List of altmode clients that register to get notified upon probe
- * completion.
- */
- static LIST_HEAD(probe_notify_list);
- static DEFINE_MUTEX(notify_lock);
- static void altmode_send_pan_ack(struct work_struct *work);
- static void *altmode_ipc_log;
- static struct altmode_dev *to_altmode_device(struct device_node *amdev_node)
- {
- struct platform_device *altmode_pdev;
- altmode_pdev = of_find_device_by_node(amdev_node);
- if (altmode_pdev)
- return platform_get_drvdata(altmode_pdev);
- return NULL;
- }
- #define ALTMODE_WAIT_MS 1000
- static int altmode_write(struct altmode_dev *amdev, void *data, size_t len)
- {
- int rc;
- reinit_completion(&amdev->response_received);
- rc = pmic_glink_write(amdev->pgclient, data, len);
- if (!rc) {
- rc = wait_for_completion_timeout(&amdev->response_received,
- msecs_to_jiffies(ALTMODE_WAIT_MS));
- rc = rc ? 0 : -ETIMEDOUT;
- }
- if (rc)
- pr_err("Error in sending message: %d\n", rc);
- return rc;
- }
- static int __altmode_send_data(struct altmode_dev *amdev, void *data,
- size_t len)
- {
- struct usbc_write_buffer_req_msg msg = { { 0 } };
- if (len > sizeof(msg.buf)) {
- pr_err("len %zu exceeds msg buf's size: %zu\n",
- len, USBC_WRITE_BUFFER_SIZE);
- return -EINVAL;
- }
- msg.hdr.owner = MSG_OWNER_USBC_PAN;
- msg.hdr.type = MSG_TYPE_REQ_RESP;
- msg.hdr.opcode = USBC_CMD_WRITE_REQ;
- memcpy(msg.buf, data, len);
- return altmode_write(amdev, &msg, sizeof(msg));
- }
- /**
- * altmode_register_notifier()
- * Register to be notified when altmode device probes.
- *
- * @client_dev: Client device pointer
- * @cb: Callback function to execute when altmode device of
- * interest probes successfully
- * @priv: Client's private data which is passed back when cb() is
- * called
- *
- * Returns: 0 upon success, negative upon errors.
- */
- int altmode_register_notifier(struct device *client_dev, void (*cb)(void *),
- void *priv)
- {
- int rc;
- struct probe_notify_node *notify_node;
- struct device_node *amdev_node;
- struct of_phandle_args pargs;
- struct altmode_dev *amdev;
- if (!client_dev || !cb || !priv)
- return -EINVAL;
- rc = of_parse_phandle_with_args(client_dev->of_node,
- "qcom,altmode-dev", "#altmode-cells", 0, &pargs);
- if (rc) {
- dev_err(client_dev, "Error parsing qcom,altmode-dev property: %d\n",
- rc);
- return rc;
- }
- amdev_node = pargs.np;
- mutex_lock(¬ify_lock);
- amdev = to_altmode_device(amdev_node);
- if (amdev) {
- /*
- * If altmode device has probed already, notify
- * immediately.
- */
- of_node_put(amdev_node);
- cb(priv);
- } else {
- notify_node = kzalloc(sizeof(*notify_node), GFP_KERNEL);
- if (!notify_node) {
- mutex_unlock(¬ify_lock);
- return -ENOMEM;
- }
- notify_node->cb = cb;
- notify_node->priv = priv;
- notify_node->amdev_node = amdev_node;
- list_add(¬ify_node->node, &probe_notify_list);
- }
- mutex_unlock(¬ify_lock);
- return 0;
- }
- EXPORT_SYMBOL(altmode_register_notifier);
- /**
- * altmode_deregister_notifier()
- * Deregister probe completion notifier.
- *
- * @client_dev: Client device pointer
- * @priv: Client's private data
- *
- * Returns: 0 upon success, negative upon errors.
- */
- int altmode_deregister_notifier(struct device *client_dev, void *priv)
- {
- int rc;
- struct device_node *amdev_node;
- struct probe_notify_node *pos, *tmp;
- struct of_phandle_args pargs;
- if (!client_dev)
- return -EINVAL;
- rc = of_parse_phandle_with_args(client_dev->of_node,
- "qcom,altmode-dev", "#altmode-cells", 0, &pargs);
- if (rc) {
- dev_err(client_dev, "Error parsing qcom,altmode-dev property: %d\n",
- rc);
- return rc;
- }
- amdev_node = pargs.np;
- mutex_lock(¬ify_lock);
- list_for_each_entry_safe(pos, tmp, &probe_notify_list, node) {
- if (pos->amdev_node == amdev_node && pos->priv == priv) {
- of_node_put(pos->amdev_node);
- list_del(&pos->node);
- kfree(pos);
- break;
- }
- }
- mutex_unlock(¬ify_lock);
- of_node_put(amdev_node);
- return 0;
- }
- EXPORT_SYMBOL(altmode_deregister_notifier);
- /**
- * altmode_send_data()
- * Send data from altmode client to remote subsystem.
- *
- * @client: Parent altmode device of this client
- * @data: Data to be sent
- * @len: Length in bytes of the data to be sent
- *
- * Returns: 0 upon success, -EINVAL if len exceeds message buffer's
- * capacity, and other negative error codes as appropriate.
- */
- int altmode_send_data(struct altmode_client *client, void *data,
- size_t len)
- {
- struct altmode_dev *amdev = client->amdev;
- return __altmode_send_data(amdev, data, len);
- }
- EXPORT_SYMBOL(altmode_send_data);
- static void client_cb_work(struct work_struct *work)
- {
- struct altmode_client *amclient = container_of(work,
- struct altmode_client, client_cb_work);
- amclient->data.callback(amclient->data.priv, amclient->msg,
- sizeof(amclient->msg));
- }
- /**
- * altmode_register_client()
- * Register with altmode to receive PMIC GLINK messages from remote
- * subsystem.
- *
- * @client_dev: Client device pointer
- * @client_data: Details identifying altmode client uniquely
- *
- * Returns: Valid altmode client pointer upon success, ERR_PTRs
- * upon errors.
- *
- * Notes: client_data should contain a unique SVID.
- */
- struct altmode_client *altmode_register_client(struct device *client_dev,
- const struct altmode_client_data *client_data)
- {
- int rc, key;
- struct altmode_dev *amdev;
- struct of_phandle_args pargs;
- struct device_node *amdev_node;
- struct altmode_client *amclient;
- if (!client_dev || !client_data)
- return ERR_PTR(-EINVAL);
- if (!client_data->name || !client_data->priv || !client_data->callback
- || !client_data->svid)
- return ERR_PTR(-EINVAL);
- rc = of_parse_phandle_with_args(client_dev->of_node,
- "qcom,altmode-dev", "#altmode-cells", 0, &pargs);
- if (rc) {
- dev_err(client_dev, "Error parsing qcom,altmode-dev property: %d\n",
- rc);
- return ERR_PTR(rc);
- }
- if (pargs.args_count != 1) {
- dev_err(client_dev, "Error in port_index specification\n");
- return ERR_PTR(-EINVAL);
- }
- if (pargs.args[0] >= MAX_NUM_PORTS) {
- dev_err(client_dev, "Invalid port_index: %d, max is %d\n",
- pargs.args[0], MAX_NUM_PORTS - 1);
- return ERR_PTR(-EINVAL);
- }
- amdev_node = pargs.np;
- amdev = to_altmode_device(amdev_node);
- of_node_put(amdev_node);
- if (!amdev)
- return ERR_PTR(-EPROBE_DEFER);
- amclient = kzalloc(sizeof(*amclient), GFP_KERNEL);
- if (!amclient)
- return ERR_PTR(-ENOMEM);
- amclient->amdev = amdev;
- amclient->port_index = pargs.args[0];
- amclient->data.svid = client_data->svid;
- amclient->data.priv = client_data->priv;
- amclient->data.callback = client_data->callback;
- INIT_WORK(&amclient->client_cb_work, client_cb_work);
- amclient->data.name = kstrdup(client_data->name, GFP_KERNEL);
- if (!amclient->data.name) {
- kfree(amclient);
- return ERR_PTR(-ENOMEM);
- }
- mutex_lock(&amdev->client_lock);
- key = IDR_KEY(amclient);
- rc = idr_alloc(&amdev->client_idr, amclient, key, key + 1, GFP_KERNEL);
- if (rc < 0) {
- pr_err("Error in allocating idr for client %s: %d\n",
- client_data->name, rc);
- mutex_unlock(&amdev->client_lock);
- kfree(amclient->data.name);
- kfree(amclient);
- return ERR_PTR(rc);
- }
- list_add(&amclient->c_node, &amdev->client_list);
- mutex_unlock(&amdev->client_lock);
- if (!atomic_read(&amdev->pan_en_sent))
- schedule_delayed_work(&amdev->send_pan_en_work,
- msecs_to_jiffies(20));
- return amclient;
- }
- EXPORT_SYMBOL(altmode_register_client);
- /**
- * altmode_deregister_client()
- * Deregister altmode client to stop receiving PMIC GLINK messages
- * specific to its SVID from remote subsystem.
- *
- * @client: Client returned by altmode_register_client()
- *
- * Returns: 0 upon success, negative upon errors.
- *
- * Notes: This does not stop the transmission of the messages by
- * the remote subsystem - only the reception of them by
- * the client.
- */
- int altmode_deregister_client(struct altmode_client *client)
- {
- struct altmode_dev *amdev;
- struct altmode_client *pos, *tmp;
- if (!client || !client->amdev)
- return -ENODEV;
- amdev = client->amdev;
- cancel_work_sync(&client->client_cb_work);
- mutex_lock(&amdev->client_lock);
- idr_remove(&amdev->client_idr, IDR_KEY(client));
- list_for_each_entry_safe(pos, tmp, &amdev->client_list, c_node) {
- if (pos == client)
- list_del(&pos->c_node);
- }
- mutex_unlock(&amdev->client_lock);
- kfree(client->data.name);
- kfree(client);
- return 0;
- }
- EXPORT_SYMBOL(altmode_deregister_client);
- static void altmode_send_pan_en(struct work_struct *work)
- {
- int rc;
- u32 enable_msg = ALTMODE_PAN_EN;
- struct altmode_dev *amdev = container_of(work, struct altmode_dev,
- send_pan_en_work.work);
- rc = __altmode_send_data(amdev, &enable_msg, sizeof(enable_msg));
- if (rc < 0) {
- pr_err("Failed to send PAN EN: %d\n", rc);
- return;
- }
- atomic_set(&amdev->pan_en_sent, 1);
- altmode_dbg("Sent PAN EN\n");
- }
- static int altmode_send_ack(struct altmode_dev *amdev, u8 port_index)
- {
- int rc;
- struct altmode_pan_ack_msg ack;
- ack.cmd_type = ALTMODE_PAN_ACK;
- ack.port_index = port_index;
- rc = __altmode_send_data(amdev, &ack, sizeof(ack));
- if (rc < 0) {
- pr_err("port %u: Failed to send PAN ACK: %d\n", port_index, rc);
- return rc;
- }
- altmode_dbg("port %u: Sent PAN ACK\n", port_index);
- return rc;
- }
- static void altmode_state_cb(void *priv, enum pmic_glink_state state)
- {
- struct altmode_dev *amdev = priv;
- altmode_dbg("state: %d\n", state);
- switch (state) {
- case PMIC_GLINK_STATE_DOWN:
- /* As of now, nothing to do */
- break;
- case PMIC_GLINK_STATE_UP:
- mutex_lock(&amdev->client_lock);
- if (!list_empty(&amdev->client_list))
- schedule_delayed_work(&amdev->send_pan_en_work,
- msecs_to_jiffies(20));
- mutex_unlock(&amdev->client_lock);
- break;
- default:
- return;
- }
- }
- static void altmode_send_pan_ack(struct work_struct *work)
- {
- struct altmode_dev *amdev = container_of(work, struct altmode_dev,
- send_pan_ack_work.work);
- altmode_send_ack(amdev, amdev->ack_port_index);
- }
- #define USBC_NOTIFY_IND_MASK GENMASK(7, 0)
- #define GET_OP(opcode) (opcode & USBC_NOTIFY_IND_MASK)
- #define GET_SVID(opcode) (opcode >> 16)
- static int altmode_callback(void *priv, void *data, size_t len)
- {
- u16 svid, op;
- struct usbc_notify_ind_msg *notify_msg;
- struct pmic_glink_hdr *hdr = data;
- struct altmode_dev *amdev = priv;
- struct altmode_client *amclient;
- u8 port_index;
- altmode_dbg("len: %zu owner: %u type: %u opcode %04x\n", len, hdr->owner,
- hdr->type, hdr->opcode);
- /*
- * For DisplayPort alt mode, the hdr->opcode is designed as follows:
- *
- * hdr->opcode = (svid << 16) | USBC_NOTIFY_IND
- */
- op = GET_OP(hdr->opcode);
- svid = GET_SVID(hdr->opcode);
- switch (op) {
- case USBC_CMD_WRITE_REQ:
- complete(&amdev->response_received);
- break;
- case USBC_NOTIFY_IND:
- if (len != sizeof(*notify_msg)) {
- altmode_dbg("Expected length %u, got: %zu\n",
- sizeof(*notify_msg), len);
- return -EINVAL;
- }
- notify_msg = data;
- port_index = notify_msg->payload[0];
- mutex_lock(&amdev->client_lock);
- amclient = idr_find(&amdev->client_idr, IDR_KEY_GEN(svid,
- port_index));
- mutex_unlock(&amdev->client_lock);
- if (!amclient) {
- altmode_dbg("No client associated with SVID %#x port %u\n",
- svid, port_index);
- amdev->ack_port_index = port_index;
- schedule_delayed_work(&amdev->send_pan_ack_work,
- msecs_to_jiffies(20));
- return 0;
- }
- altmode_dbg("Payload: %*ph\n", NOTIFY_PAYLOAD_SIZE,
- notify_msg->payload);
- cancel_work_sync(&amclient->client_cb_work);
- memcpy(&amclient->msg, notify_msg->payload,
- sizeof(amclient->msg));
- schedule_work(&amclient->client_cb_work);
- break;
- default:
- break;
- }
- return 0;
- }
- static void altmode_notify_clients(struct altmode_dev *amdev)
- {
- struct altmode_dev *pos_amdev;
- struct probe_notify_node *pos, *tmp;
- mutex_lock(¬ify_lock);
- list_for_each_entry_safe(pos, tmp, &probe_notify_list, node) {
- pos_amdev = to_altmode_device(pos->amdev_node);
- if (!pos_amdev)
- continue;
- if (pos_amdev == amdev) {
- pos->cb(pos->priv);
- of_node_put(pos->amdev_node);
- list_del(&pos->node);
- kfree(pos);
- }
- }
- mutex_unlock(¬ify_lock);
- }
- #ifdef CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG
- static int pan_en_write(void *data, u64 val)
- {
- struct altmode_dev *amdev = data;
- schedule_delayed_work(&amdev->send_pan_en_work,
- msecs_to_jiffies(20));
- return 0;
- }
- DEFINE_DEBUGFS_ATTRIBUTE(pan_en_fops, NULL, pan_en_write, "%llu\n");
- static int send_ack_write(void *data, u64 val)
- {
- int rc;
- struct altmode_dev *amdev = data;
- struct altmode_pan_ack_msg ack;
- if (val >= MAX_NUM_PORTS)
- return -EINVAL;
- ack.cmd_type = ALTMODE_PAN_ACK;
- ack.port_index = val;
- rc = __altmode_send_data(amdev, &ack, sizeof(ack));
- if (rc < 0) {
- dev_err(amdev->dev, "port %d: Failed sending PAN ACK: %llu\n",
- val, rc);
- return rc;
- }
- dev_dbg(amdev->dev, "port %llu: Sent PAN ACK via debugfs\n", val);
- return 0;
- }
- DEFINE_DEBUGFS_ATTRIBUTE(send_ack_fops, NULL, send_ack_write, "%llu\n");
- static int altmode_setup_debugfs(struct altmode_dev *amdev)
- {
- int rc;
- struct dentry *am_dir, *file;
- am_dir = debugfs_create_dir("altmode", NULL);
- if (IS_ERR(am_dir)) {
- rc = PTR_ERR(am_dir);
- dev_err(amdev->dev, "Failed to create altmode directory: %d\n",
- rc);
- return rc;
- }
- file = debugfs_create_file_unsafe("send_pan_en", 0200, am_dir, amdev,
- &pan_en_fops);
- if (IS_ERR(file)) {
- rc = PTR_ERR(file);
- dev_err(amdev->dev, "Failed to create send_pan_en: %d\n", rc);
- goto error;
- }
- file = debugfs_create_file_unsafe("send_pan_ack", 0200, am_dir,
- amdev, &send_ack_fops);
- if (IS_ERR(file)) {
- rc = PTR_ERR(file);
- dev_err(amdev->dev, "Failed to create send_pan_ack: %d\n", rc);
- goto error;
- }
- amdev->debugfs_dir = am_dir;
- return 0;
- error:
- debugfs_remove_recursive(am_dir);
- return rc;
- }
- #else
- static int altmode_setup_debugfs(struct altmode_dev *amdev)
- {
- return 0;
- }
- #endif
- static int altmode_probe(struct platform_device *pdev)
- {
- int rc;
- struct altmode_dev *amdev;
- struct pmic_glink_client_data pgclient_data = { };
- struct device *dev = &pdev->dev;
- amdev = devm_kzalloc(&pdev->dev, sizeof(*amdev), GFP_KERNEL);
- if (!amdev)
- return -ENOMEM;
- amdev->dev = dev;
- mutex_init(&amdev->client_lock);
- idr_init(&amdev->client_idr);
- init_completion(&amdev->response_received);
- INIT_DELAYED_WORK(&amdev->send_pan_en_work, altmode_send_pan_en);
- INIT_DELAYED_WORK(&amdev->send_pan_ack_work, altmode_send_pan_ack);
- INIT_LIST_HEAD(&amdev->d_node);
- INIT_LIST_HEAD(&amdev->client_list);
- pgclient_data.id = MSG_OWNER_USBC_PAN;
- pgclient_data.name = "altmode";
- pgclient_data.msg_cb = altmode_callback;
- pgclient_data.priv = amdev;
- pgclient_data.state_cb = altmode_state_cb;
- amdev->pgclient = pmic_glink_register_client(amdev->dev,
- &pgclient_data);
- if (IS_ERR(amdev->pgclient)) {
- rc = PTR_ERR(amdev->pgclient);
- if (rc != -EPROBE_DEFER)
- dev_err(dev, "Error in pmic_glink registration: %d\n",
- rc);
- goto error_register;
- }
- platform_set_drvdata(pdev, amdev);
- rc = altmode_setup_debugfs(amdev);
- if (rc < 0) {
- dev_err(amdev->dev, "Failed to create debugfs: %d\n", rc);
- goto unreg_pmic_glink;
- }
- altmode_ipc_log = ipc_log_context_create(NUM_LOG_PAGES, "altmode", 0);
- if (!altmode_ipc_log)
- dev_warn(dev, "Error in creating ipc_log_context\n");
- altmode_notify_clients(amdev);
- return 0;
- unreg_pmic_glink:
- pmic_glink_unregister_client(amdev->pgclient);
- error_register:
- idr_destroy(&amdev->client_idr);
- return rc;
- }
- static int altmode_remove(struct platform_device *pdev)
- {
- int rc;
- struct altmode_dev *amdev = platform_get_drvdata(pdev);
- struct altmode_client *client, *tmp;
- struct probe_notify_node *npos, *ntmp;
- debugfs_remove_recursive(amdev->debugfs_dir);
- cancel_delayed_work_sync(&amdev->send_pan_en_work);
- cancel_delayed_work_sync(&amdev->send_pan_ack_work);
- idr_destroy(&amdev->client_idr);
- atomic_set(&amdev->pan_en_sent, 0);
- mutex_lock(¬ify_lock);
- list_for_each_entry_safe(npos, ntmp, &probe_notify_list, node) {
- of_node_put(npos->amdev_node);
- list_del(&npos->node);
- kfree(npos);
- }
- mutex_unlock(¬ify_lock);
- mutex_lock(&amdev->client_lock);
- list_for_each_entry_safe(client, tmp, &amdev->client_list, c_node)
- list_del(&client->c_node);
- mutex_unlock(&amdev->client_lock);
- ipc_log_context_destroy(altmode_ipc_log);
- altmode_ipc_log = NULL;
- rc = pmic_glink_unregister_client(amdev->pgclient);
- if (rc < 0)
- dev_err(amdev->dev, "Error in pmic_glink de-registration: %d\n",
- rc);
- return rc;
- }
- static const struct of_device_id altmode_match_table[] = {
- { .compatible = "qcom,altmode-glink" },
- {},
- };
- static struct platform_driver altmode_driver = {
- .driver = {
- .name = "altmode-glink",
- .of_match_table = altmode_match_table,
- },
- .probe = altmode_probe,
- .remove = altmode_remove,
- };
- module_platform_driver(altmode_driver);
- MODULE_DESCRIPTION("QTI Type-C Alt Mode over GLINK");
- MODULE_LICENSE("GPL v2");
|