]> git.ipfire.org Git - thirdparty/linux.git/blame - drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
Merge tag 'usb-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
[thirdparty/linux.git] / drivers / usb / typec / tcpm / qcom / qcom_pmic_typec.c
CommitLineData
a4422ff2
BD
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2023, Linaro Ltd. All rights reserved.
4 */
5
6#include <linux/err.h>
7#include <linux/interrupt.h>
8#include <linux/kernel.h>
9#include <linux/mod_devicetable.h>
10#include <linux/module.h>
484468fb 11#include <linux/of.h>
a4422ff2
BD
12#include <linux/of_graph.h>
13#include <linux/platform_device.h>
14#include <linux/regmap.h>
15#include <linux/regulator/consumer.h>
16#include <linux/slab.h>
17#include <linux/usb/role.h>
18#include <linux/usb/tcpm.h>
19#include <linux/usb/typec_mux.h>
4b3cd783 20
7d9f1b72 21#include <drm/bridge/aux-bridge.h>
4b3cd783 22
d2f9b93d 23#include "qcom_pmic_typec.h"
a4422ff2
BD
24#include "qcom_pmic_typec_pdphy.h"
25#include "qcom_pmic_typec_port.h"
26
27struct pmic_typec_resources {
d2f9b93d 28 const struct pmic_typec_pdphy_resources *pdphy_res;
f1a27f08 29 const struct pmic_typec_port_resources *port_res;
a4422ff2
BD
30};
31
a4422ff2
BD
32static int qcom_pmic_typec_init(struct tcpc_dev *tcpc)
33{
34 return 0;
35}
36
37static int qcom_pmic_typec_probe(struct platform_device *pdev)
38{
39 struct pmic_typec *tcpm;
40 struct device *dev = &pdev->dev;
41 struct device_node *np = dev->of_node;
42 const struct pmic_typec_resources *res;
43 struct regmap *regmap;
7d9f1b72 44 struct device *bridge_dev;
cf92b9df 45 u32 base;
a4422ff2
BD
46 int ret;
47
48 res = of_device_get_match_data(dev);
49 if (!res)
50 return -ENODEV;
51
52 tcpm = devm_kzalloc(dev, sizeof(*tcpm), GFP_KERNEL);
53 if (!tcpm)
54 return -ENOMEM;
55
56 tcpm->dev = dev;
57 tcpm->tcpc.init = qcom_pmic_typec_init;
a4422ff2
BD
58
59 regmap = dev_get_regmap(dev->parent, NULL);
60 if (!regmap) {
61 dev_err(dev, "Failed to get regmap\n");
62 return -ENODEV;
63 }
64
cf92b9df 65 ret = of_property_read_u32_index(np, "reg", 0, &base);
a4422ff2
BD
66 if (ret)
67 return ret;
68
f1a27f08 69 ret = qcom_pmic_typec_port_probe(pdev, tcpm,
cf92b9df 70 res->port_res, regmap, base);
a4422ff2
BD
71 if (ret)
72 return ret;
73
cf92b9df
DB
74 if (res->pdphy_res) {
75 ret = of_property_read_u32_index(np, "reg", 1, &base);
76 if (ret)
77 return ret;
78
79 ret = qcom_pmic_typec_pdphy_probe(pdev, tcpm,
80 res->pdphy_res, regmap, base);
81 if (ret)
82 return ret;
83 } else {
84 ret = qcom_pmic_typec_pdphy_stub_probe(pdev, tcpm);
85 if (ret)
86 return ret;
87 }
a4422ff2 88
a4422ff2
BD
89 platform_set_drvdata(pdev, tcpm);
90
91 tcpm->tcpc.fwnode = device_get_named_child_node(tcpm->dev, "connector");
da6d91ed
YY
92 if (!tcpm->tcpc.fwnode)
93 return -EINVAL;
a4422ff2 94
7d9f1b72
DB
95 bridge_dev = drm_dp_hpd_bridge_register(tcpm->dev, to_of_node(tcpm->tcpc.fwnode));
96 if (IS_ERR(bridge_dev))
97 return PTR_ERR(bridge_dev);
98
a4422ff2
BD
99 tcpm->tcpm_port = tcpm_register_port(tcpm->dev, &tcpm->tcpc);
100 if (IS_ERR(tcpm->tcpm_port)) {
101 ret = PTR_ERR(tcpm->tcpm_port);
102 goto fwnode_remove;
103 }
104
f1a27f08 105 ret = tcpm->port_start(tcpm, tcpm->tcpm_port);
a4422ff2
BD
106 if (ret)
107 goto fwnode_remove;
108
d2f9b93d 109 ret = tcpm->pdphy_start(tcpm, tcpm->tcpm_port);
a4422ff2
BD
110 if (ret)
111 goto fwnode_remove;
112
113 return 0;
114
115fwnode_remove:
116 fwnode_remove_software_node(tcpm->tcpc.fwnode);
117
118 return ret;
119}
120
922c0cb5 121static void qcom_pmic_typec_remove(struct platform_device *pdev)
a4422ff2
BD
122{
123 struct pmic_typec *tcpm = platform_get_drvdata(pdev);
124
d2f9b93d 125 tcpm->pdphy_stop(tcpm);
f1a27f08 126 tcpm->port_stop(tcpm);
a4422ff2
BD
127 tcpm_unregister_port(tcpm->tcpm_port);
128 fwnode_remove_software_node(tcpm->tcpc.fwnode);
a4422ff2
BD
129}
130
f1a27f08 131static const struct pmic_typec_resources pm8150b_typec_res = {
a4422ff2
BD
132 .pdphy_res = &pm8150b_pdphy_res,
133 .port_res = &pm8150b_port_res,
134};
135
cf92b9df
DB
136static const struct pmic_typec_resources pmi632_typec_res = {
137 /* PD PHY not present */
138 .port_res = &pm8150b_port_res,
139};
140
a4422ff2
BD
141static const struct of_device_id qcom_pmic_typec_table[] = {
142 { .compatible = "qcom,pm8150b-typec", .data = &pm8150b_typec_res },
cf92b9df 143 { .compatible = "qcom,pmi632-typec", .data = &pmi632_typec_res },
a4422ff2
BD
144 { }
145};
146MODULE_DEVICE_TABLE(of, qcom_pmic_typec_table);
147
148static struct platform_driver qcom_pmic_typec_driver = {
149 .driver = {
150 .name = "qcom,pmic-typec",
151 .of_match_table = qcom_pmic_typec_table,
152 },
153 .probe = qcom_pmic_typec_probe,
922c0cb5 154 .remove_new = qcom_pmic_typec_remove,
a4422ff2
BD
155};
156
157module_platform_driver(qcom_pmic_typec_driver);
158
159MODULE_DESCRIPTION("QCOM PMIC USB Type-C Port Manager Driver");
160MODULE_LICENSE("GPL");