1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. 4 */ 5 6 #include <linux/delay.h> 7 #include <linux/err.h> 8 #include <linux/module.h> 9 #include <linux/of.h> 10 #include <linux/platform_device.h> 11 #include <linux/of_platform.h> 12 #include <linux/regmap.h> 13 #include <linux/spmi.h> 14 #include <linux/soc/qcom/qcom-pbs.h> 15 16 #define PBS_CLIENT_TRIG_CTL 0x42 17 #define PBS_CLIENT_SW_TRIG_BIT BIT(7) 18 #define PBS_CLIENT_SCRATCH1 0x50 19 #define PBS_CLIENT_SCRATCH2 0x51 20 #define PBS_CLIENT_SCRATCH2_ERROR 0xFF 21 22 #define RETRIES 2000 23 #define DELAY 1100 24 25 struct pbs_dev { 26 struct device *dev; 27 struct regmap *regmap; 28 struct mutex lock; 29 struct device_link *link; 30 31 u32 base; 32 }; 33 34 static int qcom_pbs_wait_for_ack(struct pbs_dev *pbs, u8 bit_pos) 35 { 36 unsigned int val; 37 int ret; 38 39 ret = regmap_read_poll_timeout(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 40 val, val & BIT(bit_pos), DELAY, DELAY * RETRIES); 41 42 if (ret < 0) { 43 dev_err(pbs->dev, "Timeout for PBS ACK/NACK for bit %u\n", bit_pos); 44 return -ETIMEDOUT; 45 } 46 47 if (val == PBS_CLIENT_SCRATCH2_ERROR) { 48 ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0); 49 dev_err(pbs->dev, "NACK from PBS for bit %u\n", bit_pos); 50 return -EINVAL; 51 } 52 53 dev_dbg(pbs->dev, "PBS sequence for bit %u executed!\n", bit_pos); 54 return 0; 55 } 56 57 /** 58 * qcom_pbs_trigger_event() - Trigger the PBS RAM sequence 59 * @pbs: Pointer to PBS device 60 * @bitmap: bitmap 61 * 62 * This function is used to trigger the PBS RAM sequence to be 63 * executed by the client driver. 64 * 65 * The PBS trigger sequence involves 66 * 1. setting the PBS sequence bit in PBS_CLIENT_SCRATCH1 67 * 2. Initiating the SW PBS trigger 68 * 3. Checking the equivalent bit in PBS_CLIENT_SCRATCH2 for the 69 * completion of the sequence. 70 * 4. If PBS_CLIENT_SCRATCH2 == 0xFF, the PBS sequence failed to execute 71 * 72 * Return: 0 on success, < 0 on failure 73 */ 74 int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap) 75 { 76 unsigned int val; 77 u16 bit_pos; 78 int ret; 79 80 if (WARN_ON(!bitmap)) 81 return -EINVAL; 82 83 if (IS_ERR_OR_NULL(pbs)) 84 return -EINVAL; 85 86 mutex_lock(&pbs->lock); 87 ret = regmap_read(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, &val); 88 if (ret < 0) 89 goto out; 90 91 if (val == PBS_CLIENT_SCRATCH2_ERROR) { 92 /* PBS error - clear SCRATCH2 register */ 93 ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0); 94 if (ret < 0) 95 goto out; 96 } 97 98 for (bit_pos = 0; bit_pos < 8; bit_pos++) { 99 if (!(bitmap & BIT(bit_pos))) 100 continue; 101 102 /* Clear the PBS sequence bit position */ 103 ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 104 BIT(bit_pos), 0); 105 if (ret < 0) 106 goto out_clear_scratch1; 107 108 /* Set the PBS sequence bit position */ 109 ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, 110 BIT(bit_pos), BIT(bit_pos)); 111 if (ret < 0) 112 goto out_clear_scratch1; 113 114 /* Initiate the SW trigger */ 115 ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_TRIG_CTL, 116 PBS_CLIENT_SW_TRIG_BIT, PBS_CLIENT_SW_TRIG_BIT); 117 if (ret < 0) 118 goto out_clear_scratch1; 119 120 ret = qcom_pbs_wait_for_ack(pbs, bit_pos); 121 if (ret < 0) 122 goto out_clear_scratch1; 123 124 /* Clear the PBS sequence bit position */ 125 regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, BIT(bit_pos), 0); 126 regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0); 127 } 128 129 out_clear_scratch1: 130 /* Clear all the requested bitmap */ 131 ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, bitmap, 0); 132 133 out: 134 mutex_unlock(&pbs->lock); 135 136 return ret; 137 } 138 EXPORT_SYMBOL_GPL(qcom_pbs_trigger_event); 139 140 /** 141 * get_pbs_client_device() - Get the PBS device used by client 142 * @dev: Client device 143 * 144 * This function is used to get the PBS device that is being 145 * used by the client. 146 * 147 * Return: pbs_dev on success, ERR_PTR on failure 148 */ 149 struct pbs_dev *get_pbs_client_device(struct device *dev) 150 { 151 struct device_node *pbs_dev_node; 152 struct platform_device *pdev; 153 struct pbs_dev *pbs; 154 155 pbs_dev_node = of_parse_phandle(dev->of_node, "qcom,pbs", 0); 156 if (!pbs_dev_node) { 157 dev_err(dev, "Missing qcom,pbs property\n"); 158 return ERR_PTR(-ENODEV); 159 } 160 161 pdev = of_find_device_by_node(pbs_dev_node); 162 if (!pdev) { 163 dev_err(dev, "Unable to find PBS dev_node\n"); 164 pbs = ERR_PTR(-EPROBE_DEFER); 165 goto out; 166 } 167 168 pbs = platform_get_drvdata(pdev); 169 if (!pbs) { 170 dev_err(dev, "Cannot get pbs instance from %s\n", dev_name(&pdev->dev)); 171 platform_device_put(pdev); 172 pbs = ERR_PTR(-EPROBE_DEFER); 173 goto out; 174 } 175 176 pbs->link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER); 177 if (!pbs->link) { 178 dev_err(&pdev->dev, "Failed to create device link to consumer %s\n", dev_name(dev)); 179 platform_device_put(pdev); 180 pbs = ERR_PTR(-EINVAL); 181 goto out; 182 } 183 184 out: 185 of_node_put(pbs_dev_node); 186 return pbs; 187 } 188 EXPORT_SYMBOL_GPL(get_pbs_client_device); 189 190 static int qcom_pbs_probe(struct platform_device *pdev) 191 { 192 struct pbs_dev *pbs; 193 u32 val; 194 int ret; 195 196 pbs = devm_kzalloc(&pdev->dev, sizeof(*pbs), GFP_KERNEL); 197 if (!pbs) 198 return -ENOMEM; 199 200 pbs->dev = &pdev->dev; 201 pbs->regmap = dev_get_regmap(pbs->dev->parent, NULL); 202 if (!pbs->regmap) { 203 dev_err(pbs->dev, "Couldn't get parent's regmap\n"); 204 return -EINVAL; 205 } 206 207 ret = device_property_read_u32(pbs->dev, "reg", &val); 208 if (ret < 0) { 209 dev_err(pbs->dev, "Couldn't find reg, ret = %d\n", ret); 210 return ret; 211 } 212 pbs->base = val; 213 mutex_init(&pbs->lock); 214 215 platform_set_drvdata(pdev, pbs); 216 217 return 0; 218 } 219 220 static const struct of_device_id qcom_pbs_match_table[] = { 221 { .compatible = "qcom,pbs" }, 222 {} 223 }; 224 MODULE_DEVICE_TABLE(of, qcom_pbs_match_table); 225 226 static struct platform_driver qcom_pbs_driver = { 227 .driver = { 228 .name = "qcom-pbs", 229 .of_match_table = qcom_pbs_match_table, 230 }, 231 .probe = qcom_pbs_probe, 232 }; 233 module_platform_driver(qcom_pbs_driver) 234 235 MODULE_DESCRIPTION("QCOM PBS DRIVER"); 236 MODULE_LICENSE("GPL"); 237