Loading drivers/soc/qcom/altmode-glink.c +148 −131 Original line number Diff line number Diff line Loading @@ -24,7 +24,11 @@ #define USBC_CMD_WRITE_REQ 0x15 #define USBC_NOTIFY_IND 0x16 #define ALTMODE_NAME_MAX_LEN 10 #define MAX_NUM_PORTS 4 #define IDR_KEY_GEN(svid, ind) (((svid) << 8) | (ind)) #define IDR_KEY(client) \ IDR_KEY_GEN((client)->data.svid, (client)->port_index) struct usbc_notify_ind_msg { struct pmic_glink_hdr hdr; Loading @@ -43,7 +47,6 @@ struct usbc_write_buffer_req_msg { * Definition of an altmode device. * * @dev: Altmode parent device for all client devices * @name: Short descriptive name of this altmode device * @pgclient: PMIC GLINK client to talk to remote subsystem * @client_idr: idr list for altmode clients * @client_lock: mutex protecting changes to client_idr Loading @@ -51,11 +54,9 @@ struct usbc_write_buffer_req_msg { * @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 * @probe_notifier: Inform clients of altmode probe completion */ struct altmode_dev { struct device *dev; char name[ALTMODE_NAME_MAX_LEN]; struct pmic_glink_client *pgclient; struct idr client_idr; struct mutex client_lock; Loading @@ -63,7 +64,6 @@ struct altmode_dev { struct list_head client_list; atomic_t pan_en_sent; struct delayed_work send_pan_en_work; struct raw_notifier_head probe_notifier; }; /** Loading @@ -87,45 +87,33 @@ struct altmode_client { * Linked list node to keep track of altmode clients who, by design, * register with the altmode framework before altmode probes. * * @amdev_name: Name of the altmode device client wants to bind to * @nb: Client's notifier block * @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 { char *amdev_name; struct notifier_block *nb; struct list_head node; struct device_node *amdev_node; void (*cb)(void *priv); void *priv; }; /** * struct list_head amdev_list * List of altmode devices currently using this driver. */ static LIST_HEAD(amdev_list); static DEFINE_MUTEX(amdev_lock); /** * struct list_head probe_notify_list * List of altmode clients that register to get notified upon probe * completion. This list is traversed and clients are removed from this * list after they have been registered with the altmode device's * raw_notifier_head. * completion. */ static LIST_HEAD(probe_notify_list); static DEFINE_MUTEX(notify_lock); static struct altmode_dev *get_amdev_from_dev(struct device *dev) static struct altmode_dev *to_altmode_device(struct device_node *amdev_node) { struct altmode_dev *pos, *tmp; struct platform_device *altmode_pdev; mutex_lock(&amdev_lock); list_for_each_entry_safe(pos, tmp, &amdev_list, d_node) { if (pos->dev == dev) { mutex_unlock(&amdev_lock); return pos; } } mutex_unlock(&amdev_lock); altmode_pdev = of_find_device_by_node(amdev_node); if (altmode_pdev) return platform_get_drvdata(altmode_pdev); return NULL; } Loading Loading @@ -157,33 +145,60 @@ static int __altmode_send_data(struct altmode_dev *amdev, void *data, /** * altmode_register_notifier() * Register to be notified when altmode probes. * Register to be notified when altmode device probes. * * @amdev_name: The altmode device being registered with by client * @nb: Notifier block to get notified upon probe completion * @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(const char *amdev_name, struct notifier_block *nb) 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 (!amdev_name || !nb) if (!client_dev || !cb || !priv) return -EINVAL; notify_node = kzalloc(sizeof(*notify_node), GFP_KERNEL); if (!notify_node) return -ENOMEM; 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; notify_node->nb = nb; notify_node->amdev_name = kstrdup(amdev_name, GFP_KERNEL); if (!notify_node->amdev_name) { kfree(notify_node); 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; } mutex_lock(¬ify_lock); 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; Loading @@ -194,24 +209,45 @@ EXPORT_SYMBOL(altmode_register_notifier); * altmode_deregister_notifier() * Deregister probe completion notifier. * * @client: The altmode client obtained during registration * @nb: Notifier block used for registration * @client_dev: Client device pointer * @priv: Client's private data * * Returns: 0 upon success, negative upon errors. */ int altmode_deregister_notifier(struct altmode_client *client, struct notifier_block *nb) int altmode_deregister_notifier(struct device *client_dev, void *priv) { struct altmode_dev *amdev; int rc; struct device_node *amdev_node; struct probe_notify_node *pos, *tmp; struct of_phandle_args pargs; if (!client || !nb) if (!client_dev) return -EINVAL; amdev = client->amdev; if (!amdev) return -ENODEV; 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 raw_notifier_chain_unregister(&amdev->probe_notifier, nb); return 0; } EXPORT_SYMBOL(altmode_deregister_notifier); Loading Loading @@ -240,7 +276,7 @@ EXPORT_SYMBOL(altmode_send_data); * Register with altmode to receive PMIC GLINK messages from remote * subsystem. * * @dev: Device of the parent altmode (platform) device * @client_dev: Client device pointer * @client_data: Details identifying altmode client uniquely * * Returns: Valid altmode client pointer upon success, ERR_PTRs Loading @@ -248,32 +284,54 @@ EXPORT_SYMBOL(altmode_send_data); * * Notes: client_data should contain a unique SVID. */ struct altmode_client *altmode_register_client(struct device *dev, struct altmode_client *altmode_register_client(struct device *client_dev, const struct altmode_client_data *client_data) { int rc; int rc, key; struct altmode_dev *amdev; struct of_phandle_args pargs; struct device_node *amdev_node; struct altmode_client *amclient; if (!dev || !dev->parent) return ERR_PTR(-ENODEV); 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); amdev = get_amdev_from_dev(dev); if (!amdev) { pr_err("No alt mode device exists for %s\n", client_data->name); return ERR_PTR(-ENODEV); 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 = U8_MAX; /* invalid */ amclient->port_index = pargs.args[0]; amclient->data.svid = client_data->svid; amclient->data.priv = client_data->priv; amclient->data.callback = client_data->callback; Loading @@ -284,8 +342,8 @@ struct altmode_client *altmode_register_client(struct device *dev, } mutex_lock(&amdev->client_lock); rc = idr_alloc(&amdev->client_idr, amclient, amclient->data.svid, amclient->data.svid + 1, GFP_KERNEL); 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); Loading Loading @@ -330,7 +388,7 @@ int altmode_deregister_client(struct altmode_client *client) amdev = client->amdev; mutex_lock(&amdev->client_lock); idr_remove(&amdev->client_idr, client->data.svid); idr_remove(&amdev->client_idr, IDR_KEY(client)); list_for_each_entry_safe(pos, tmp, &amdev->client_list, c_node) { if (pos == client) Loading Loading @@ -429,68 +487,50 @@ static int altmode_callback(void *priv, void *data, size_t len) port_index = notify_msg->payload[0]; mutex_lock(&amdev->client_lock); amclient = idr_find(&amdev->client_idr, svid); amclient = idr_find(&amdev->client_idr, IDR_KEY_GEN(svid, port_index)); mutex_unlock(&amdev->client_lock); if (op == USBC_NOTIFY_IND) { if (!amclient) { pr_debug("No client associated with SVID %#x\n", svid); pr_debug("No client associated with SVID %#x port %u\n", svid, port_index); altmode_send_ack(amdev, port_index); return 0; } if (amclient->port_index == U8_MAX) amclient->port_index = port_index; pr_debug("Payload: %*ph\n", NOTIFY_PAYLOAD_SIZE, notify_msg->payload); amclient->data.callback(amclient->data.priv, notify_msg->payload, len); } return 0; } static void altmode_gather_clients(struct altmode_dev *amdev) 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) { if (!strcmp(pos->amdev_name, amdev->name)) { raw_notifier_chain_register(&amdev->probe_notifier, pos->nb); /* * Client's nb has been added to amdev's notifier, so * it may be removed from the global list. */ 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->amdev_name); kfree(pos); } } mutex_unlock(¬ify_lock); } static void altmode_notify_clients(struct altmode_dev *amdev, struct platform_device *pdev) { altmode_gather_clients(amdev); raw_notifier_call_chain(&amdev->probe_notifier, 0, pdev); } static void altmode_device_add(struct altmode_dev *amdev) { mutex_lock(&amdev_lock); list_add(&amdev->d_node, &amdev_list); mutex_unlock(&amdev_lock); } static int altmode_probe(struct platform_device *pdev) { int rc; const char *str = NULL; struct altmode_dev *amdev; struct pmic_glink_client_data pgclient_data = { }; struct device *dev = &pdev->dev; Loading @@ -499,23 +539,7 @@ static int altmode_probe(struct platform_device *pdev) if (!amdev) return -ENOMEM; rc = of_property_read_string(dev->of_node, "qcom,altmode-name", &str); if (rc < 0) { dev_err(dev, "No altmode device name specified: %d\n", rc); return rc; } if (!str || (strlen(str) >= ALTMODE_NAME_MAX_LEN) || !str_has_prefix(str, "altmode_")) { dev_err(dev, "Incorrect altmode device name format\n"); return -EINVAL; } amdev->dev = dev; strlcpy(amdev->name, str, ALTMODE_NAME_MAX_LEN); RAW_INIT_NOTIFIER_HEAD(&amdev->probe_notifier); mutex_init(&amdev->client_lock); idr_init(&amdev->client_idr); Loading @@ -541,8 +565,7 @@ static int altmode_probe(struct platform_device *pdev) platform_set_drvdata(pdev, amdev); altmode_device_add(amdev); altmode_notify_clients(amdev, pdev); altmode_notify_clients(amdev); return 0; Loading @@ -551,36 +574,30 @@ static int altmode_probe(struct platform_device *pdev) return rc; } static void altmode_device_remove(struct altmode_dev *amdev) { struct altmode_dev *pos, *tmp; atomic_set(&amdev->pan_en_sent, 0); mutex_lock(&amdev_lock); list_for_each_entry_safe(pos, tmp, &amdev_list, d_node) { if (pos == amdev) list_del(&pos->d_node); } mutex_unlock(&amdev_lock); } 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; cancel_delayed_work_sync(&amdev->send_pan_en_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); altmode_device_remove(amdev); rc = pmic_glink_unregister_client(amdev->pgclient); if (rc < 0) dev_err(amdev->dev, "Error in pmic_glink de-registration: %d\n", Loading include/linux/soc/qcom/altmode-glink.h +7 −9 Original line number Diff line number Diff line Loading @@ -38,13 +38,11 @@ struct altmode_pan_ack_msg { #if IS_ENABLED(CONFIG_QTI_ALTMODE_GLINK) struct notifier_block; struct device; int altmode_register_notifier(const char *amdev_name, struct notifier_block *nb); int altmode_deregister_notifier(struct altmode_client *client, struct notifier_block *nb); int altmode_register_notifier(struct device *client_dev, void (*cb)(void *), void *priv); int altmode_deregister_notifier(struct device *client_dev, void *priv); struct altmode_client *altmode_register_client(struct device *dev, const struct altmode_client_data *client_data); int altmode_deregister_client(struct altmode_client *client); Loading @@ -52,14 +50,14 @@ int altmode_send_data(struct altmode_client *client, void *data, size_t len); #else static inline int altmode_register_notifier(const char *amdev_name, struct notifier_block *nb) static inline int altmode_register_notifier(struct device *client_dev, void (*cb)(void *), void *priv) { return -ENODEV; } static inline int altmode_deregister_notifier(struct altmode_client *client, struct notifier_block *nb) static inline int altmode_deregister_notifier(struct device *client_dev, void *priv) { return -ENODEV; } Loading Loading
drivers/soc/qcom/altmode-glink.c +148 −131 Original line number Diff line number Diff line Loading @@ -24,7 +24,11 @@ #define USBC_CMD_WRITE_REQ 0x15 #define USBC_NOTIFY_IND 0x16 #define ALTMODE_NAME_MAX_LEN 10 #define MAX_NUM_PORTS 4 #define IDR_KEY_GEN(svid, ind) (((svid) << 8) | (ind)) #define IDR_KEY(client) \ IDR_KEY_GEN((client)->data.svid, (client)->port_index) struct usbc_notify_ind_msg { struct pmic_glink_hdr hdr; Loading @@ -43,7 +47,6 @@ struct usbc_write_buffer_req_msg { * Definition of an altmode device. * * @dev: Altmode parent device for all client devices * @name: Short descriptive name of this altmode device * @pgclient: PMIC GLINK client to talk to remote subsystem * @client_idr: idr list for altmode clients * @client_lock: mutex protecting changes to client_idr Loading @@ -51,11 +54,9 @@ struct usbc_write_buffer_req_msg { * @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 * @probe_notifier: Inform clients of altmode probe completion */ struct altmode_dev { struct device *dev; char name[ALTMODE_NAME_MAX_LEN]; struct pmic_glink_client *pgclient; struct idr client_idr; struct mutex client_lock; Loading @@ -63,7 +64,6 @@ struct altmode_dev { struct list_head client_list; atomic_t pan_en_sent; struct delayed_work send_pan_en_work; struct raw_notifier_head probe_notifier; }; /** Loading @@ -87,45 +87,33 @@ struct altmode_client { * Linked list node to keep track of altmode clients who, by design, * register with the altmode framework before altmode probes. * * @amdev_name: Name of the altmode device client wants to bind to * @nb: Client's notifier block * @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 { char *amdev_name; struct notifier_block *nb; struct list_head node; struct device_node *amdev_node; void (*cb)(void *priv); void *priv; }; /** * struct list_head amdev_list * List of altmode devices currently using this driver. */ static LIST_HEAD(amdev_list); static DEFINE_MUTEX(amdev_lock); /** * struct list_head probe_notify_list * List of altmode clients that register to get notified upon probe * completion. This list is traversed and clients are removed from this * list after they have been registered with the altmode device's * raw_notifier_head. * completion. */ static LIST_HEAD(probe_notify_list); static DEFINE_MUTEX(notify_lock); static struct altmode_dev *get_amdev_from_dev(struct device *dev) static struct altmode_dev *to_altmode_device(struct device_node *amdev_node) { struct altmode_dev *pos, *tmp; struct platform_device *altmode_pdev; mutex_lock(&amdev_lock); list_for_each_entry_safe(pos, tmp, &amdev_list, d_node) { if (pos->dev == dev) { mutex_unlock(&amdev_lock); return pos; } } mutex_unlock(&amdev_lock); altmode_pdev = of_find_device_by_node(amdev_node); if (altmode_pdev) return platform_get_drvdata(altmode_pdev); return NULL; } Loading Loading @@ -157,33 +145,60 @@ static int __altmode_send_data(struct altmode_dev *amdev, void *data, /** * altmode_register_notifier() * Register to be notified when altmode probes. * Register to be notified when altmode device probes. * * @amdev_name: The altmode device being registered with by client * @nb: Notifier block to get notified upon probe completion * @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(const char *amdev_name, struct notifier_block *nb) 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 (!amdev_name || !nb) if (!client_dev || !cb || !priv) return -EINVAL; notify_node = kzalloc(sizeof(*notify_node), GFP_KERNEL); if (!notify_node) return -ENOMEM; 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; notify_node->nb = nb; notify_node->amdev_name = kstrdup(amdev_name, GFP_KERNEL); if (!notify_node->amdev_name) { kfree(notify_node); 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; } mutex_lock(¬ify_lock); 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; Loading @@ -194,24 +209,45 @@ EXPORT_SYMBOL(altmode_register_notifier); * altmode_deregister_notifier() * Deregister probe completion notifier. * * @client: The altmode client obtained during registration * @nb: Notifier block used for registration * @client_dev: Client device pointer * @priv: Client's private data * * Returns: 0 upon success, negative upon errors. */ int altmode_deregister_notifier(struct altmode_client *client, struct notifier_block *nb) int altmode_deregister_notifier(struct device *client_dev, void *priv) { struct altmode_dev *amdev; int rc; struct device_node *amdev_node; struct probe_notify_node *pos, *tmp; struct of_phandle_args pargs; if (!client || !nb) if (!client_dev) return -EINVAL; amdev = client->amdev; if (!amdev) return -ENODEV; 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 raw_notifier_chain_unregister(&amdev->probe_notifier, nb); return 0; } EXPORT_SYMBOL(altmode_deregister_notifier); Loading Loading @@ -240,7 +276,7 @@ EXPORT_SYMBOL(altmode_send_data); * Register with altmode to receive PMIC GLINK messages from remote * subsystem. * * @dev: Device of the parent altmode (platform) device * @client_dev: Client device pointer * @client_data: Details identifying altmode client uniquely * * Returns: Valid altmode client pointer upon success, ERR_PTRs Loading @@ -248,32 +284,54 @@ EXPORT_SYMBOL(altmode_send_data); * * Notes: client_data should contain a unique SVID. */ struct altmode_client *altmode_register_client(struct device *dev, struct altmode_client *altmode_register_client(struct device *client_dev, const struct altmode_client_data *client_data) { int rc; int rc, key; struct altmode_dev *amdev; struct of_phandle_args pargs; struct device_node *amdev_node; struct altmode_client *amclient; if (!dev || !dev->parent) return ERR_PTR(-ENODEV); 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); amdev = get_amdev_from_dev(dev); if (!amdev) { pr_err("No alt mode device exists for %s\n", client_data->name); return ERR_PTR(-ENODEV); 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 = U8_MAX; /* invalid */ amclient->port_index = pargs.args[0]; amclient->data.svid = client_data->svid; amclient->data.priv = client_data->priv; amclient->data.callback = client_data->callback; Loading @@ -284,8 +342,8 @@ struct altmode_client *altmode_register_client(struct device *dev, } mutex_lock(&amdev->client_lock); rc = idr_alloc(&amdev->client_idr, amclient, amclient->data.svid, amclient->data.svid + 1, GFP_KERNEL); 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); Loading Loading @@ -330,7 +388,7 @@ int altmode_deregister_client(struct altmode_client *client) amdev = client->amdev; mutex_lock(&amdev->client_lock); idr_remove(&amdev->client_idr, client->data.svid); idr_remove(&amdev->client_idr, IDR_KEY(client)); list_for_each_entry_safe(pos, tmp, &amdev->client_list, c_node) { if (pos == client) Loading Loading @@ -429,68 +487,50 @@ static int altmode_callback(void *priv, void *data, size_t len) port_index = notify_msg->payload[0]; mutex_lock(&amdev->client_lock); amclient = idr_find(&amdev->client_idr, svid); amclient = idr_find(&amdev->client_idr, IDR_KEY_GEN(svid, port_index)); mutex_unlock(&amdev->client_lock); if (op == USBC_NOTIFY_IND) { if (!amclient) { pr_debug("No client associated with SVID %#x\n", svid); pr_debug("No client associated with SVID %#x port %u\n", svid, port_index); altmode_send_ack(amdev, port_index); return 0; } if (amclient->port_index == U8_MAX) amclient->port_index = port_index; pr_debug("Payload: %*ph\n", NOTIFY_PAYLOAD_SIZE, notify_msg->payload); amclient->data.callback(amclient->data.priv, notify_msg->payload, len); } return 0; } static void altmode_gather_clients(struct altmode_dev *amdev) 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) { if (!strcmp(pos->amdev_name, amdev->name)) { raw_notifier_chain_register(&amdev->probe_notifier, pos->nb); /* * Client's nb has been added to amdev's notifier, so * it may be removed from the global list. */ 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->amdev_name); kfree(pos); } } mutex_unlock(¬ify_lock); } static void altmode_notify_clients(struct altmode_dev *amdev, struct platform_device *pdev) { altmode_gather_clients(amdev); raw_notifier_call_chain(&amdev->probe_notifier, 0, pdev); } static void altmode_device_add(struct altmode_dev *amdev) { mutex_lock(&amdev_lock); list_add(&amdev->d_node, &amdev_list); mutex_unlock(&amdev_lock); } static int altmode_probe(struct platform_device *pdev) { int rc; const char *str = NULL; struct altmode_dev *amdev; struct pmic_glink_client_data pgclient_data = { }; struct device *dev = &pdev->dev; Loading @@ -499,23 +539,7 @@ static int altmode_probe(struct platform_device *pdev) if (!amdev) return -ENOMEM; rc = of_property_read_string(dev->of_node, "qcom,altmode-name", &str); if (rc < 0) { dev_err(dev, "No altmode device name specified: %d\n", rc); return rc; } if (!str || (strlen(str) >= ALTMODE_NAME_MAX_LEN) || !str_has_prefix(str, "altmode_")) { dev_err(dev, "Incorrect altmode device name format\n"); return -EINVAL; } amdev->dev = dev; strlcpy(amdev->name, str, ALTMODE_NAME_MAX_LEN); RAW_INIT_NOTIFIER_HEAD(&amdev->probe_notifier); mutex_init(&amdev->client_lock); idr_init(&amdev->client_idr); Loading @@ -541,8 +565,7 @@ static int altmode_probe(struct platform_device *pdev) platform_set_drvdata(pdev, amdev); altmode_device_add(amdev); altmode_notify_clients(amdev, pdev); altmode_notify_clients(amdev); return 0; Loading @@ -551,36 +574,30 @@ static int altmode_probe(struct platform_device *pdev) return rc; } static void altmode_device_remove(struct altmode_dev *amdev) { struct altmode_dev *pos, *tmp; atomic_set(&amdev->pan_en_sent, 0); mutex_lock(&amdev_lock); list_for_each_entry_safe(pos, tmp, &amdev_list, d_node) { if (pos == amdev) list_del(&pos->d_node); } mutex_unlock(&amdev_lock); } 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; cancel_delayed_work_sync(&amdev->send_pan_en_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); altmode_device_remove(amdev); rc = pmic_glink_unregister_client(amdev->pgclient); if (rc < 0) dev_err(amdev->dev, "Error in pmic_glink de-registration: %d\n", Loading
include/linux/soc/qcom/altmode-glink.h +7 −9 Original line number Diff line number Diff line Loading @@ -38,13 +38,11 @@ struct altmode_pan_ack_msg { #if IS_ENABLED(CONFIG_QTI_ALTMODE_GLINK) struct notifier_block; struct device; int altmode_register_notifier(const char *amdev_name, struct notifier_block *nb); int altmode_deregister_notifier(struct altmode_client *client, struct notifier_block *nb); int altmode_register_notifier(struct device *client_dev, void (*cb)(void *), void *priv); int altmode_deregister_notifier(struct device *client_dev, void *priv); struct altmode_client *altmode_register_client(struct device *dev, const struct altmode_client_data *client_data); int altmode_deregister_client(struct altmode_client *client); Loading @@ -52,14 +50,14 @@ int altmode_send_data(struct altmode_client *client, void *data, size_t len); #else static inline int altmode_register_notifier(const char *amdev_name, struct notifier_block *nb) static inline int altmode_register_notifier(struct device *client_dev, void (*cb)(void *), void *priv) { return -ENODEV; } static inline int altmode_deregister_notifier(struct altmode_client *client, struct notifier_block *nb) static inline int altmode_deregister_notifier(struct device *client_dev, void *priv) { return -ENODEV; } Loading