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> 11 #include <linux/of.h> 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> 20 21 #include <drm/bridge/aux-bridge.h> 22 23 #include "qcom_pmic_typec.h" 24 #include "qcom_pmic_typec_pdphy.h" 25 #include "qcom_pmic_typec_port.h" 26 27 struct pmic_typec_resources { 28 const struct pmic_typec_pdphy_resources *pdphy_res; 29 const struct pmic_typec_port_resources *port_res; 30 }; 31 32 static int qcom_pmic_typec_init(struct tcpc_dev *tcpc) 33 { 34 return 0; 35 } 36 37 static 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; 44 struct device *bridge_dev; 45 u32 base; 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; 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 65 ret = of_property_read_u32_index(np, "reg", 0, &base); 66 if (ret) 67 return ret; 68 69 ret = qcom_pmic_typec_port_probe(pdev, tcpm, 70 res->port_res, regmap, base); 71 if (ret) 72 return ret; 73 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 } 88 89 platform_set_drvdata(pdev, tcpm); 90 91 tcpm->tcpc.fwnode = device_get_named_child_node(tcpm->dev, "connector"); 92 if (!tcpm->tcpc.fwnode) 93 return -EINVAL; 94 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 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 105 ret = tcpm->port_start(tcpm, tcpm->tcpm_port); 106 if (ret) 107 goto fwnode_remove; 108 109 ret = tcpm->pdphy_start(tcpm, tcpm->tcpm_port); 110 if (ret) 111 goto fwnode_remove; 112 113 return 0; 114 115 fwnode_remove: 116 fwnode_remove_software_node(tcpm->tcpc.fwnode); 117 118 return ret; 119 } 120 121 static void qcom_pmic_typec_remove(struct platform_device *pdev) 122 { 123 struct pmic_typec *tcpm = platform_get_drvdata(pdev); 124 125 tcpm->pdphy_stop(tcpm); 126 tcpm->port_stop(tcpm); 127 tcpm_unregister_port(tcpm->tcpm_port); 128 fwnode_remove_software_node(tcpm->tcpc.fwnode); 129 } 130 131 static const struct pmic_typec_resources pm8150b_typec_res = { 132 .pdphy_res = &pm8150b_pdphy_res, 133 .port_res = &pm8150b_port_res, 134 }; 135 136 static const struct pmic_typec_resources pmi632_typec_res = { 137 /* PD PHY not present */ 138 .port_res = &pm8150b_port_res, 139 }; 140 141 static const struct of_device_id qcom_pmic_typec_table[] = { 142 { .compatible = "qcom,pm8150b-typec", .data = &pm8150b_typec_res }, 143 { .compatible = "qcom,pmi632-typec", .data = &pmi632_typec_res }, 144 { } 145 }; 146 MODULE_DEVICE_TABLE(of, qcom_pmic_typec_table); 147 148 static 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, 154 .remove_new = qcom_pmic_typec_remove, 155 }; 156 157 module_platform_driver(qcom_pmic_typec_driver); 158 159 MODULE_DESCRIPTION("QCOM PMIC USB Type-C Port Manager Driver"); 160 MODULE_LICENSE("GPL"); 161