#include <linux/device.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
+#include <linux/list.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/serdev.h>
#include <linux/slab.h>
+struct pwrseq_pci_dev {
+ struct serdev_device *serdev;
+ struct of_changeset *ocs;
+ struct pci_dev *pdev;
+ struct list_head list;
+};
+
struct pwrseq_pcie_m2_pdata {
const struct pwrseq_target_data **targets;
};
struct notifier_block nb;
struct gpio_desc *w_disable1_gpio;
struct gpio_desc *w_disable2_gpio;
- struct serdev_device *serdev;
- struct of_changeset *ocs;
struct device *dev;
+ struct list_head pci_devices;
+ struct mutex list_lock;
};
static int pwrseq_pcie_m2_vregs_enable(struct pwrseq_device *pwrseq)
}
static int pwrseq_pcie_m2_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx,
+ struct pwrseq_pci_dev *pci_dev,
struct device_node *parent)
{
struct device *dev = ctx->dev;
struct device_node *np;
int ret;
- ctx->ocs = kzalloc_obj(*ctx->ocs);
- if (!ctx->ocs)
+ pci_dev->ocs = kzalloc_obj(*pci_dev->ocs);
+ if (!pci_dev->ocs)
return -ENOMEM;
- of_changeset_init(ctx->ocs);
+ of_changeset_init(pci_dev->ocs);
- np = of_changeset_create_node(ctx->ocs, parent, "bluetooth");
+ np = of_changeset_create_node(pci_dev->ocs, parent, "bluetooth");
if (!np) {
dev_err(dev, "Failed to create bluetooth node\n");
ret = -ENODEV;
goto err_destroy_changeset;
}
- ret = of_changeset_add_prop_string(ctx->ocs, np, "compatible", "qcom,wcn7850-bt");
+ ret = of_changeset_add_prop_string(pci_dev->ocs, np, "compatible", "qcom,wcn7850-bt");
if (ret) {
dev_err(dev, "Failed to add bluetooth compatible: %d\n", ret);
goto err_destroy_changeset;
}
- ret = of_changeset_apply(ctx->ocs);
+ ret = of_changeset_apply(pci_dev->ocs);
if (ret) {
dev_err(dev, "Failed to apply changeset: %d\n", ret);
goto err_destroy_changeset;
}
- ret = device_add_of_node(&ctx->serdev->dev, np);
+ ret = device_add_of_node(&pci_dev->serdev->dev, np);
if (ret) {
dev_err(dev, "Failed to add OF node: %d\n", ret);
goto err_revert_changeset;
return 0;
err_revert_changeset:
- of_changeset_revert(ctx->ocs);
+ of_changeset_revert(pci_dev->ocs);
err_destroy_changeset:
- of_changeset_destroy(ctx->ocs);
- kfree(ctx->ocs);
- ctx->ocs = NULL;
+ of_changeset_destroy(pci_dev->ocs);
+ kfree(pci_dev->ocs);
+ pci_dev->ocs = NULL;
return ret;
}
-static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx)
+static int pwrseq_pcie_m2_create_serdev(struct pwrseq_pcie_m2_ctx *ctx,
+ struct pci_dev *pdev)
{
struct serdev_controller *serdev_ctrl;
struct device *dev = ctx->dev;
+ struct pwrseq_pci_dev *pci_dev;
int ret;
struct device_node *serdev_parent __free(device_node) =
return 0;
}
- ctx->serdev = serdev_device_alloc(serdev_ctrl);
- if (!ctx->serdev) {
+ pci_dev = kzalloc(sizeof(*pci_dev), GFP_KERNEL);
+ if (!pci_dev) {
ret = -ENOMEM;
goto err_put_ctrl;
}
- ret = pwrseq_pcie_m2_create_bt_node(ctx, serdev_parent);
+ pci_dev->serdev = serdev_device_alloc(serdev_ctrl);
+ if (!pci_dev->serdev) {
+ ret = -ENOMEM;
+ goto err_free_pci_dev;
+ }
+
+ ret = pwrseq_pcie_m2_create_bt_node(ctx, pci_dev, serdev_parent);
if (ret)
goto err_free_serdev;
- ret = serdev_device_add(ctx->serdev);
+ ret = serdev_device_add(pci_dev->serdev);
if (ret) {
dev_err(dev, "Failed to add serdev for WCN7850: %d\n", ret);
goto err_free_dt_node;
serdev_controller_put(serdev_ctrl);
+ pci_dev->pdev = pci_dev_get(pdev);
+
+ mutex_lock(&ctx->list_lock);
+ list_add_tail(&pci_dev->list, &ctx->pci_devices);
+ mutex_unlock(&ctx->list_lock);
+
return 0;
err_free_dt_node:
- device_remove_of_node(&ctx->serdev->dev);
- of_changeset_revert(ctx->ocs);
- of_changeset_destroy(ctx->ocs);
- kfree(ctx->ocs);
- ctx->ocs = NULL;
+ device_remove_of_node(&pci_dev->serdev->dev);
+ of_changeset_revert(pci_dev->ocs);
+ of_changeset_destroy(pci_dev->ocs);
+ kfree(pci_dev->ocs);
+ pci_dev->ocs = NULL;
err_free_serdev:
- serdev_device_put(ctx->serdev);
- ctx->serdev = NULL;
+ serdev_device_put(pci_dev->serdev);
+ pci_dev->serdev = NULL;
+err_free_pci_dev:
+ kfree(pci_dev);
err_put_ctrl:
serdev_controller_put(serdev_ctrl);
return ret;
}
-static void pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx)
+static void __pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx,
+ struct pwrseq_pci_dev *pci_dev)
{
- if (ctx->serdev) {
- device_remove_of_node(&ctx->serdev->dev);
- serdev_device_remove(ctx->serdev);
- ctx->serdev = NULL;
+ if (pci_dev->serdev) {
+ device_remove_of_node(&pci_dev->serdev->dev);
+ serdev_device_remove(pci_dev->serdev);
}
- if (ctx->ocs) {
- of_changeset_revert(ctx->ocs);
- of_changeset_destroy(ctx->ocs);
- kfree(ctx->ocs);
- ctx->ocs = NULL;
+ if (pci_dev->ocs) {
+ of_changeset_revert(pci_dev->ocs);
+ of_changeset_destroy(pci_dev->ocs);
+ kfree(pci_dev->ocs);
}
+
+ pci_dev_put(pci_dev->pdev);
+ list_del(&pci_dev->list);
+ kfree(pci_dev);
+}
+
+static void pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx,
+ struct pci_dev *pdev)
+{
+ struct pwrseq_pci_dev *pci_dev, *tmp;
+
+ mutex_lock(&ctx->list_lock);
+ list_for_each_entry_safe(pci_dev, tmp, &ctx->pci_devices, list) {
+ if (!pdev || pci_dev->pdev == pdev) {
+ __pwrseq_pcie_m2_remove_serdev(ctx, pci_dev);
+ if (pdev)
+ break;
+ }
+ }
+ mutex_unlock(&ctx->list_lock);
}
static int pwrseq_pcie_m2_notify(struct notifier_block *nb, unsigned long action,
case BUS_NOTIFY_ADD_DEVICE:
/* Create serdev device for WCN7850 */
if (pdev->vendor == PCI_VENDOR_ID_QCOM && pdev->device == 0x1107) {
- ret = pwrseq_pcie_m2_create_serdev(ctx);
+ ret = pwrseq_pcie_m2_create_serdev(ctx, pdev);
if (ret)
return notifier_from_errno(ret);
}
case BUS_NOTIFY_REMOVED_DEVICE:
/* Destroy serdev device for WCN7850 */
if (pdev->vendor == PCI_VENDOR_ID_QCOM && pdev->device == 0x1107)
- pwrseq_pcie_m2_remove_serdev(ctx);
+ pwrseq_pcie_m2_remove_serdev(ctx, pdev);
break;
}
goto err_free_regulators;
}
+ mutex_init(&ctx->list_lock);
+ INIT_LIST_HEAD(&ctx->pci_devices);
/*
* Register a notifier for creating protocol devices for
* non-discoverable busses like UART.
*/
ret = pwrseq_pcie_m2_register_notifier(ctx, dev);
if (ret)
- goto err_free_regulators;
+ goto err_destroy_mutex;
return 0;
+err_destroy_mutex:
+ mutex_destroy(&ctx->list_lock);
err_free_regulators:
regulator_bulk_free(ctx->num_vregs, ctx->regs);
struct pwrseq_pcie_m2_ctx *ctx = platform_get_drvdata(pdev);
bus_unregister_notifier(&pci_bus_type, &ctx->nb);
- pwrseq_pcie_m2_remove_serdev(ctx);
+ pwrseq_pcie_m2_remove_serdev(ctx, NULL);
+ mutex_destroy(&ctx->list_lock);
regulator_bulk_free(ctx->num_vregs, ctx->regs);
}