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