1 /* 2 * Qualcomm Technologies HIDMA DMA engine Management interface 3 * 4 * Copyright (c) 2015, The Linux Foundation. All rights reserved. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 and 8 * only version 2 as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16 #include <linux/dmaengine.h> 17 #include <linux/acpi.h> 18 #include <linux/of.h> 19 #include <linux/property.h> 20 #include <linux/interrupt.h> 21 #include <linux/platform_device.h> 22 #include <linux/module.h> 23 #include <linux/uaccess.h> 24 #include <linux/slab.h> 25 #include <linux/pm_runtime.h> 26 #include <linux/bitops.h> 27 28 #include "hidma_mgmt.h" 29 30 #define HIDMA_QOS_N_OFFSET 0x300 31 #define HIDMA_CFG_OFFSET 0x400 32 #define HIDMA_MAX_BUS_REQ_LEN_OFFSET 0x41C 33 #define HIDMA_MAX_XACTIONS_OFFSET 0x420 34 #define HIDMA_HW_VERSION_OFFSET 0x424 35 #define HIDMA_CHRESET_TIMEOUT_OFFSET 0x418 36 37 #define HIDMA_MAX_WR_XACTIONS_MASK GENMASK(4, 0) 38 #define HIDMA_MAX_RD_XACTIONS_MASK GENMASK(4, 0) 39 #define HIDMA_WEIGHT_MASK GENMASK(6, 0) 40 #define HIDMA_MAX_BUS_REQ_LEN_MASK GENMASK(15, 0) 41 #define HIDMA_CHRESET_TIMEOUT_MASK GENMASK(19, 0) 42 43 #define HIDMA_MAX_WR_XACTIONS_BIT_POS 16 44 #define HIDMA_MAX_BUS_WR_REQ_BIT_POS 16 45 #define HIDMA_WRR_BIT_POS 8 46 #define HIDMA_PRIORITY_BIT_POS 15 47 48 #define HIDMA_AUTOSUSPEND_TIMEOUT 2000 49 #define HIDMA_MAX_CHANNEL_WEIGHT 15 50 51 int hidma_mgmt_setup(struct hidma_mgmt_dev *mgmtdev) 52 { 53 unsigned int i; 54 u32 val; 55 56 if (!is_power_of_2(mgmtdev->max_write_request) || 57 (mgmtdev->max_write_request < 128) || 58 (mgmtdev->max_write_request > 1024)) { 59 dev_err(&mgmtdev->pdev->dev, "invalid write request %d\n", 60 mgmtdev->max_write_request); 61 return -EINVAL; 62 } 63 64 if (!is_power_of_2(mgmtdev->max_read_request) || 65 (mgmtdev->max_read_request < 128) || 66 (mgmtdev->max_read_request > 1024)) { 67 dev_err(&mgmtdev->pdev->dev, "invalid read request %d\n", 68 mgmtdev->max_read_request); 69 return -EINVAL; 70 } 71 72 if (mgmtdev->max_wr_xactions > HIDMA_MAX_WR_XACTIONS_MASK) { 73 dev_err(&mgmtdev->pdev->dev, 74 "max_wr_xactions cannot be bigger than %ld\n", 75 HIDMA_MAX_WR_XACTIONS_MASK); 76 return -EINVAL; 77 } 78 79 if (mgmtdev->max_rd_xactions > HIDMA_MAX_RD_XACTIONS_MASK) { 80 dev_err(&mgmtdev->pdev->dev, 81 "max_rd_xactions cannot be bigger than %ld\n", 82 HIDMA_MAX_RD_XACTIONS_MASK); 83 return -EINVAL; 84 } 85 86 for (i = 0; i < mgmtdev->dma_channels; i++) { 87 if (mgmtdev->priority[i] > 1) { 88 dev_err(&mgmtdev->pdev->dev, 89 "priority can be 0 or 1\n"); 90 return -EINVAL; 91 } 92 93 if (mgmtdev->weight[i] > HIDMA_MAX_CHANNEL_WEIGHT) { 94 dev_err(&mgmtdev->pdev->dev, 95 "max value of weight can be %d.\n", 96 HIDMA_MAX_CHANNEL_WEIGHT); 97 return -EINVAL; 98 } 99 100 /* weight needs to be at least one */ 101 if (mgmtdev->weight[i] == 0) 102 mgmtdev->weight[i] = 1; 103 } 104 105 pm_runtime_get_sync(&mgmtdev->pdev->dev); 106 val = readl(mgmtdev->virtaddr + HIDMA_MAX_BUS_REQ_LEN_OFFSET); 107 val &= ~(HIDMA_MAX_BUS_REQ_LEN_MASK << HIDMA_MAX_BUS_WR_REQ_BIT_POS); 108 val |= mgmtdev->max_write_request << HIDMA_MAX_BUS_WR_REQ_BIT_POS; 109 val &= ~HIDMA_MAX_BUS_REQ_LEN_MASK; 110 val |= mgmtdev->max_read_request; 111 writel(val, mgmtdev->virtaddr + HIDMA_MAX_BUS_REQ_LEN_OFFSET); 112 113 val = readl(mgmtdev->virtaddr + HIDMA_MAX_XACTIONS_OFFSET); 114 val &= ~(HIDMA_MAX_WR_XACTIONS_MASK << HIDMA_MAX_WR_XACTIONS_BIT_POS); 115 val |= mgmtdev->max_wr_xactions << HIDMA_MAX_WR_XACTIONS_BIT_POS; 116 val &= ~HIDMA_MAX_RD_XACTIONS_MASK; 117 val |= mgmtdev->max_rd_xactions; 118 writel(val, mgmtdev->virtaddr + HIDMA_MAX_XACTIONS_OFFSET); 119 120 mgmtdev->hw_version = 121 readl(mgmtdev->virtaddr + HIDMA_HW_VERSION_OFFSET); 122 mgmtdev->hw_version_major = (mgmtdev->hw_version >> 28) & 0xF; 123 mgmtdev->hw_version_minor = (mgmtdev->hw_version >> 16) & 0xF; 124 125 for (i = 0; i < mgmtdev->dma_channels; i++) { 126 u32 weight = mgmtdev->weight[i]; 127 u32 priority = mgmtdev->priority[i]; 128 129 val = readl(mgmtdev->virtaddr + HIDMA_QOS_N_OFFSET + (4 * i)); 130 val &= ~(1 << HIDMA_PRIORITY_BIT_POS); 131 val |= (priority & 0x1) << HIDMA_PRIORITY_BIT_POS; 132 val &= ~(HIDMA_WEIGHT_MASK << HIDMA_WRR_BIT_POS); 133 val |= (weight & HIDMA_WEIGHT_MASK) << HIDMA_WRR_BIT_POS; 134 writel(val, mgmtdev->virtaddr + HIDMA_QOS_N_OFFSET + (4 * i)); 135 } 136 137 val = readl(mgmtdev->virtaddr + HIDMA_CHRESET_TIMEOUT_OFFSET); 138 val &= ~HIDMA_CHRESET_TIMEOUT_MASK; 139 val |= mgmtdev->chreset_timeout_cycles & HIDMA_CHRESET_TIMEOUT_MASK; 140 writel(val, mgmtdev->virtaddr + HIDMA_CHRESET_TIMEOUT_OFFSET); 141 142 pm_runtime_mark_last_busy(&mgmtdev->pdev->dev); 143 pm_runtime_put_autosuspend(&mgmtdev->pdev->dev); 144 return 0; 145 } 146 EXPORT_SYMBOL_GPL(hidma_mgmt_setup); 147 148 static int hidma_mgmt_probe(struct platform_device *pdev) 149 { 150 struct hidma_mgmt_dev *mgmtdev; 151 struct resource *res; 152 void __iomem *virtaddr; 153 int irq; 154 int rc; 155 u32 val; 156 157 pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT); 158 pm_runtime_use_autosuspend(&pdev->dev); 159 pm_runtime_set_active(&pdev->dev); 160 pm_runtime_enable(&pdev->dev); 161 pm_runtime_get_sync(&pdev->dev); 162 163 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 164 virtaddr = devm_ioremap_resource(&pdev->dev, res); 165 if (IS_ERR(virtaddr)) { 166 rc = -ENOMEM; 167 goto out; 168 } 169 170 irq = platform_get_irq(pdev, 0); 171 if (irq < 0) { 172 dev_err(&pdev->dev, "irq resources not found\n"); 173 rc = irq; 174 goto out; 175 } 176 177 mgmtdev = devm_kzalloc(&pdev->dev, sizeof(*mgmtdev), GFP_KERNEL); 178 if (!mgmtdev) { 179 rc = -ENOMEM; 180 goto out; 181 } 182 183 mgmtdev->pdev = pdev; 184 mgmtdev->addrsize = resource_size(res); 185 mgmtdev->virtaddr = virtaddr; 186 187 rc = device_property_read_u32(&pdev->dev, "dma-channels", 188 &mgmtdev->dma_channels); 189 if (rc) { 190 dev_err(&pdev->dev, "number of channels missing\n"); 191 goto out; 192 } 193 194 rc = device_property_read_u32(&pdev->dev, 195 "channel-reset-timeout-cycles", 196 &mgmtdev->chreset_timeout_cycles); 197 if (rc) { 198 dev_err(&pdev->dev, "channel reset timeout missing\n"); 199 goto out; 200 } 201 202 rc = device_property_read_u32(&pdev->dev, "max-write-burst-bytes", 203 &mgmtdev->max_write_request); 204 if (rc) { 205 dev_err(&pdev->dev, "max-write-burst-bytes missing\n"); 206 goto out; 207 } 208 209 rc = device_property_read_u32(&pdev->dev, "max-read-burst-bytes", 210 &mgmtdev->max_read_request); 211 if (rc) { 212 dev_err(&pdev->dev, "max-read-burst-bytes missing\n"); 213 goto out; 214 } 215 216 rc = device_property_read_u32(&pdev->dev, "max-write-transactions", 217 &mgmtdev->max_wr_xactions); 218 if (rc) { 219 dev_err(&pdev->dev, "max-write-transactions missing\n"); 220 goto out; 221 } 222 223 rc = device_property_read_u32(&pdev->dev, "max-read-transactions", 224 &mgmtdev->max_rd_xactions); 225 if (rc) { 226 dev_err(&pdev->dev, "max-read-transactions missing\n"); 227 goto out; 228 } 229 230 mgmtdev->priority = devm_kcalloc(&pdev->dev, 231 mgmtdev->dma_channels, 232 sizeof(*mgmtdev->priority), 233 GFP_KERNEL); 234 if (!mgmtdev->priority) { 235 rc = -ENOMEM; 236 goto out; 237 } 238 239 mgmtdev->weight = devm_kcalloc(&pdev->dev, 240 mgmtdev->dma_channels, 241 sizeof(*mgmtdev->weight), GFP_KERNEL); 242 if (!mgmtdev->weight) { 243 rc = -ENOMEM; 244 goto out; 245 } 246 247 rc = hidma_mgmt_setup(mgmtdev); 248 if (rc) { 249 dev_err(&pdev->dev, "setup failed\n"); 250 goto out; 251 } 252 253 /* start the HW */ 254 val = readl(mgmtdev->virtaddr + HIDMA_CFG_OFFSET); 255 val |= 1; 256 writel(val, mgmtdev->virtaddr + HIDMA_CFG_OFFSET); 257 258 rc = hidma_mgmt_init_sys(mgmtdev); 259 if (rc) { 260 dev_err(&pdev->dev, "sysfs setup failed\n"); 261 goto out; 262 } 263 264 dev_info(&pdev->dev, 265 "HW rev: %d.%d @ %pa with %d physical channels\n", 266 mgmtdev->hw_version_major, mgmtdev->hw_version_minor, 267 &res->start, mgmtdev->dma_channels); 268 269 platform_set_drvdata(pdev, mgmtdev); 270 pm_runtime_mark_last_busy(&pdev->dev); 271 pm_runtime_put_autosuspend(&pdev->dev); 272 return 0; 273 out: 274 pm_runtime_put_sync_suspend(&pdev->dev); 275 pm_runtime_disable(&pdev->dev); 276 return rc; 277 } 278 279 #if IS_ENABLED(CONFIG_ACPI) 280 static const struct acpi_device_id hidma_mgmt_acpi_ids[] = { 281 {"QCOM8060"}, 282 {}, 283 }; 284 #endif 285 286 static const struct of_device_id hidma_mgmt_match[] = { 287 {.compatible = "qcom,hidma-mgmt-1.0",}, 288 {}, 289 }; 290 MODULE_DEVICE_TABLE(of, hidma_mgmt_match); 291 292 static struct platform_driver hidma_mgmt_driver = { 293 .probe = hidma_mgmt_probe, 294 .driver = { 295 .name = "hidma-mgmt", 296 .of_match_table = hidma_mgmt_match, 297 .acpi_match_table = ACPI_PTR(hidma_mgmt_acpi_ids), 298 }, 299 }; 300 301 module_platform_driver(hidma_mgmt_driver); 302 MODULE_LICENSE("GPL v2"); 303