1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // Copyright 2012 Freescale Semiconductor, Inc. 4 // Copyright 2012 Linaro Ltd. 5 // Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> 6 // 7 // Initial development of this code was funded by 8 // Phytec Messtechnik GmbH, https://www.phytec.de 9 10 #include <linux/clk.h> 11 #include <linux/debugfs.h> 12 #include <linux/err.h> 13 #include <linux/io.h> 14 #include <linux/module.h> 15 #include <linux/of.h> 16 #include <linux/of_device.h> 17 #include <linux/platform_device.h> 18 #include <linux/slab.h> 19 20 #include "imx-audmux.h" 21 22 #define DRIVER_NAME "imx-audmux" 23 24 static struct clk *audmux_clk; 25 static void __iomem *audmux_base; 26 static u32 *regcache; 27 static u32 reg_max; 28 29 #define IMX_AUDMUX_V2_PTCR(x) ((x) * 8) 30 #define IMX_AUDMUX_V2_PDCR(x) ((x) * 8 + 4) 31 32 #ifdef CONFIG_DEBUG_FS 33 static struct dentry *audmux_debugfs_root; 34 35 /* There is an annoying discontinuity in the SSI numbering with regard 36 * to the Linux number of the devices */ 37 static const char *audmux_port_string(int port) 38 { 39 switch (port) { 40 case MX31_AUDMUX_PORT1_SSI0: 41 return "imx-ssi.0"; 42 case MX31_AUDMUX_PORT2_SSI1: 43 return "imx-ssi.1"; 44 case MX31_AUDMUX_PORT3_SSI_PINS_3: 45 return "SSI3"; 46 case MX31_AUDMUX_PORT4_SSI_PINS_4: 47 return "SSI4"; 48 case MX31_AUDMUX_PORT5_SSI_PINS_5: 49 return "SSI5"; 50 case MX31_AUDMUX_PORT6_SSI_PINS_6: 51 return "SSI6"; 52 default: 53 return "UNKNOWN"; 54 } 55 } 56 57 static ssize_t audmux_read_file(struct file *file, char __user *user_buf, 58 size_t count, loff_t *ppos) 59 { 60 ssize_t ret; 61 char *buf; 62 uintptr_t port = (uintptr_t)file->private_data; 63 u32 pdcr, ptcr; 64 65 if (audmux_clk) { 66 ret = clk_prepare_enable(audmux_clk); 67 if (ret) 68 return ret; 69 } 70 71 ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port)); 72 pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port)); 73 74 if (audmux_clk) 75 clk_disable_unprepare(audmux_clk); 76 77 buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 78 if (!buf) 79 return -ENOMEM; 80 81 ret = scnprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n", 82 pdcr, ptcr); 83 84 if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR) 85 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 86 "TxFS output from %s, ", 87 audmux_port_string((ptcr >> 27) & 0x7)); 88 else 89 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 90 "TxFS input, "); 91 92 if (ptcr & IMX_AUDMUX_V2_PTCR_TCLKDIR) 93 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 94 "TxClk output from %s", 95 audmux_port_string((ptcr >> 22) & 0x7)); 96 else 97 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 98 "TxClk input"); 99 100 ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n"); 101 102 if (ptcr & IMX_AUDMUX_V2_PTCR_SYN) { 103 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 104 "Port is symmetric"); 105 } else { 106 if (ptcr & IMX_AUDMUX_V2_PTCR_RFSDIR) 107 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 108 "RxFS output from %s, ", 109 audmux_port_string((ptcr >> 17) & 0x7)); 110 else 111 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 112 "RxFS input, "); 113 114 if (ptcr & IMX_AUDMUX_V2_PTCR_RCLKDIR) 115 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 116 "RxClk output from %s", 117 audmux_port_string((ptcr >> 12) & 0x7)); 118 else 119 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 120 "RxClk input"); 121 } 122 123 ret += scnprintf(buf + ret, PAGE_SIZE - ret, 124 "\nData received from %s\n", 125 audmux_port_string((pdcr >> 13) & 0x7)); 126 127 ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); 128 129 kfree(buf); 130 131 return ret; 132 } 133 134 static const struct file_operations audmux_debugfs_fops = { 135 .open = simple_open, 136 .read = audmux_read_file, 137 .llseek = default_llseek, 138 }; 139 140 static void audmux_debugfs_init(void) 141 { 142 uintptr_t i; 143 char buf[20]; 144 145 audmux_debugfs_root = debugfs_create_dir("audmux", NULL); 146 147 for (i = 0; i < MX31_AUDMUX_PORT7_SSI_PINS_7 + 1; i++) { 148 snprintf(buf, sizeof(buf), "ssi%lu", i); 149 debugfs_create_file(buf, 0444, audmux_debugfs_root, 150 (void *)i, &audmux_debugfs_fops); 151 } 152 } 153 154 static void audmux_debugfs_remove(void) 155 { 156 debugfs_remove_recursive(audmux_debugfs_root); 157 } 158 #else 159 static inline void audmux_debugfs_init(void) 160 { 161 } 162 163 static inline void audmux_debugfs_remove(void) 164 { 165 } 166 #endif 167 168 static enum imx_audmux_type { 169 IMX21_AUDMUX, 170 IMX31_AUDMUX, 171 } audmux_type; 172 173 static const struct platform_device_id imx_audmux_ids[] = { 174 { 175 .name = "imx21-audmux", 176 .driver_data = IMX21_AUDMUX, 177 }, { 178 .name = "imx31-audmux", 179 .driver_data = IMX31_AUDMUX, 180 }, { 181 /* sentinel */ 182 } 183 }; 184 MODULE_DEVICE_TABLE(platform, imx_audmux_ids); 185 186 static const struct of_device_id imx_audmux_dt_ids[] = { 187 { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], }, 188 { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], }, 189 { /* sentinel */ } 190 }; 191 MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids); 192 193 static const uint8_t port_mapping[] = { 194 0x0, 0x4, 0x8, 0x10, 0x14, 0x1c, 195 }; 196 197 int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr) 198 { 199 if (audmux_type != IMX21_AUDMUX) 200 return -EINVAL; 201 202 if (!audmux_base) 203 return -ENOSYS; 204 205 if (port >= ARRAY_SIZE(port_mapping)) 206 return -EINVAL; 207 208 writel(pcr, audmux_base + port_mapping[port]); 209 210 return 0; 211 } 212 EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port); 213 214 int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, 215 unsigned int pdcr) 216 { 217 int ret; 218 219 if (audmux_type != IMX31_AUDMUX) 220 return -EINVAL; 221 222 if (!audmux_base) 223 return -ENOSYS; 224 225 if (audmux_clk) { 226 ret = clk_prepare_enable(audmux_clk); 227 if (ret) 228 return ret; 229 } 230 231 writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port)); 232 writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port)); 233 234 if (audmux_clk) 235 clk_disable_unprepare(audmux_clk); 236 237 return 0; 238 } 239 EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port); 240 241 static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, 242 struct device_node *of_node) 243 { 244 struct device_node *child; 245 246 for_each_available_child_of_node(of_node, child) { 247 unsigned int port; 248 unsigned int ptcr = 0; 249 unsigned int pdcr = 0; 250 unsigned int pcr = 0; 251 unsigned int val; 252 int ret; 253 int i = 0; 254 255 ret = of_property_read_u32(child, "fsl,audmux-port", &port); 256 if (ret) { 257 dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node \"%pOF\"\n", 258 child); 259 continue; 260 } 261 if (!of_property_read_bool(child, "fsl,port-config")) { 262 dev_warn(&pdev->dev, "child node \"%pOF\" does not have property fsl,port-config\n", 263 child); 264 continue; 265 } 266 267 for (i = 0; (ret = of_property_read_u32_index(child, 268 "fsl,port-config", i, &val)) == 0; 269 ++i) { 270 if (audmux_type == IMX31_AUDMUX) { 271 if (i % 2) 272 pdcr |= val; 273 else 274 ptcr |= val; 275 } else { 276 pcr |= val; 277 } 278 } 279 280 if (ret != -EOVERFLOW) { 281 dev_err(&pdev->dev, "Failed to read u32 at index %d of child %pOF\n", 282 i, child); 283 continue; 284 } 285 286 if (audmux_type == IMX31_AUDMUX) { 287 if (i % 2) { 288 dev_err(&pdev->dev, "One pdcr value is missing in child node %pOF\n", 289 child); 290 continue; 291 } 292 imx_audmux_v2_configure_port(port, ptcr, pdcr); 293 } else { 294 imx_audmux_v1_configure_port(port, pcr); 295 } 296 } 297 298 return 0; 299 } 300 301 static int imx_audmux_probe(struct platform_device *pdev) 302 { 303 const struct of_device_id *of_id = 304 of_match_device(imx_audmux_dt_ids, &pdev->dev); 305 306 audmux_base = devm_platform_ioremap_resource(pdev, 0); 307 if (IS_ERR(audmux_base)) 308 return PTR_ERR(audmux_base); 309 310 audmux_clk = devm_clk_get(&pdev->dev, "audmux"); 311 if (IS_ERR(audmux_clk)) { 312 dev_dbg(&pdev->dev, "cannot get clock: %ld\n", 313 PTR_ERR(audmux_clk)); 314 audmux_clk = NULL; 315 } 316 317 if (of_id) 318 pdev->id_entry = of_id->data; 319 audmux_type = pdev->id_entry->driver_data; 320 321 switch (audmux_type) { 322 case IMX31_AUDMUX: 323 audmux_debugfs_init(); 324 reg_max = 14; 325 break; 326 case IMX21_AUDMUX: 327 reg_max = 6; 328 break; 329 default: 330 dev_err(&pdev->dev, "unsupported version!\n"); 331 return -EINVAL; 332 } 333 334 regcache = devm_kzalloc(&pdev->dev, sizeof(u32) * reg_max, GFP_KERNEL); 335 if (!regcache) 336 return -ENOMEM; 337 338 if (of_id) 339 imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node); 340 341 return 0; 342 } 343 344 static int imx_audmux_remove(struct platform_device *pdev) 345 { 346 if (audmux_type == IMX31_AUDMUX) 347 audmux_debugfs_remove(); 348 349 return 0; 350 } 351 352 #ifdef CONFIG_PM_SLEEP 353 static int imx_audmux_suspend(struct device *dev) 354 { 355 int i; 356 357 clk_prepare_enable(audmux_clk); 358 359 for (i = 0; i < reg_max; i++) 360 regcache[i] = readl(audmux_base + i * 4); 361 362 clk_disable_unprepare(audmux_clk); 363 364 return 0; 365 } 366 367 static int imx_audmux_resume(struct device *dev) 368 { 369 int i; 370 371 clk_prepare_enable(audmux_clk); 372 373 for (i = 0; i < reg_max; i++) 374 writel(regcache[i], audmux_base + i * 4); 375 376 clk_disable_unprepare(audmux_clk); 377 378 return 0; 379 } 380 #endif /* CONFIG_PM_SLEEP */ 381 382 static const struct dev_pm_ops imx_audmux_pm = { 383 SET_SYSTEM_SLEEP_PM_OPS(imx_audmux_suspend, imx_audmux_resume) 384 }; 385 386 static struct platform_driver imx_audmux_driver = { 387 .probe = imx_audmux_probe, 388 .remove = imx_audmux_remove, 389 .id_table = imx_audmux_ids, 390 .driver = { 391 .name = DRIVER_NAME, 392 .pm = &imx_audmux_pm, 393 .of_match_table = imx_audmux_dt_ids, 394 } 395 }; 396 397 static int __init imx_audmux_init(void) 398 { 399 return platform_driver_register(&imx_audmux_driver); 400 } 401 subsys_initcall(imx_audmux_init); 402 403 static void __exit imx_audmux_exit(void) 404 { 405 platform_driver_unregister(&imx_audmux_driver); 406 } 407 module_exit(imx_audmux_exit); 408 409 MODULE_DESCRIPTION("Freescale i.MX AUDMUX driver"); 410 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 411 MODULE_LICENSE("GPL v2"); 412 MODULE_ALIAS("platform:" DRIVER_NAME); 413