brintos

brintos / linux-shallow public Read only

0
0
Text · 3.5 KiB · 0e6bd47 Raw
140 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2024 Linaro Ltd.4 */5 6#include <linux/device.h>7#include <linux/mod_devicetable.h>8#include <linux/module.h>9#include <linux/pci-pwrctl.h>10#include <linux/platform_device.h>11#include <linux/property.h>12#include <linux/pwrseq/consumer.h>13#include <linux/slab.h>14#include <linux/types.h>15 16struct pci_pwrctl_pwrseq_data {17	struct pci_pwrctl ctx;18	struct pwrseq_desc *pwrseq;19};20 21struct pci_pwrctl_pwrseq_pdata {22	const char *target;23	/*24	 * Called before doing anything else to perform device-specific25	 * verification between requesting the power sequencing handle.26	 */27	int (*validate_device)(struct device *dev);28};29 30static int pci_pwrctl_pwrseq_qcm_wcn_validate_device(struct device *dev)31{32	/*33	 * Old device trees for some platforms already define wifi nodes for34	 * the WCN family of chips since before power sequencing was added35	 * upstream.36	 *37	 * These nodes don't consume the regulator outputs from the PMU, and38	 * if we allow this driver to bind to one of such "incomplete" nodes,39	 * we'll see a kernel log error about the indefinite probe deferral.40	 *41	 * Check the existence of the regulator supply that exists on all42	 * WCN models before moving forward.43	 */44	if (!device_property_present(dev, "vddaon-supply"))45		return -ENODEV;46 47	return 0;48}49 50static const struct pci_pwrctl_pwrseq_pdata pci_pwrctl_pwrseq_qcom_wcn_pdata = {51	.target = "wlan",52	.validate_device = pci_pwrctl_pwrseq_qcm_wcn_validate_device,53};54 55static void devm_pci_pwrctl_pwrseq_power_off(void *data)56{57	struct pwrseq_desc *pwrseq = data;58 59	pwrseq_power_off(pwrseq);60}61 62static int pci_pwrctl_pwrseq_probe(struct platform_device *pdev)63{64	const struct pci_pwrctl_pwrseq_pdata *pdata;65	struct pci_pwrctl_pwrseq_data *data;66	struct device *dev = &pdev->dev;67	int ret;68 69	pdata = device_get_match_data(dev);70	if (!pdata || !pdata->target)71		return -EINVAL;72 73	if (pdata->validate_device) {74		ret = pdata->validate_device(dev);75		if (ret)76			return ret;77	}78 79	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);80	if (!data)81		return -ENOMEM;82 83	data->pwrseq = devm_pwrseq_get(dev, pdata->target);84	if (IS_ERR(data->pwrseq))85		return dev_err_probe(dev, PTR_ERR(data->pwrseq),86				     "Failed to get the power sequencer\n");87 88	ret = pwrseq_power_on(data->pwrseq);89	if (ret)90		return dev_err_probe(dev, ret,91				     "Failed to power-on the device\n");92 93	ret = devm_add_action_or_reset(dev, devm_pci_pwrctl_pwrseq_power_off,94				       data->pwrseq);95	if (ret)96		return ret;97 98	pci_pwrctl_init(&data->ctx, dev);99 100	ret = devm_pci_pwrctl_device_set_ready(dev, &data->ctx);101	if (ret)102		return dev_err_probe(dev, ret,103				     "Failed to register the pwrctl wrapper\n");104 105	return 0;106}107 108static const struct of_device_id pci_pwrctl_pwrseq_of_match[] = {109	{110		/* ATH11K in QCA6390 package. */111		.compatible = "pci17cb,1101",112		.data = &pci_pwrctl_pwrseq_qcom_wcn_pdata,113	},114	{115		/* ATH11K in WCN6855 package. */116		.compatible = "pci17cb,1103",117		.data = &pci_pwrctl_pwrseq_qcom_wcn_pdata,118	},119	{120		/* ATH12K in WCN7850 package. */121		.compatible = "pci17cb,1107",122		.data = &pci_pwrctl_pwrseq_qcom_wcn_pdata,123	},124	{ }125};126MODULE_DEVICE_TABLE(of, pci_pwrctl_pwrseq_of_match);127 128static struct platform_driver pci_pwrctl_pwrseq_driver = {129	.driver = {130		.name = "pci-pwrctl-pwrseq",131		.of_match_table = pci_pwrctl_pwrseq_of_match,132	},133	.probe = pci_pwrctl_pwrseq_probe,134};135module_platform_driver(pci_pwrctl_pwrseq_driver);136 137MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");138MODULE_DESCRIPTION("Generic PCI Power Control module for power sequenced devices");139MODULE_LICENSE("GPL");140