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 145 for (i = 0; i < MX31_AUDMUX_PORT7_SSI_PINS_7 + 1; i++) { 146 snprintf(buf, sizeof(buf), "ssi%lu", i); 147 debugfs_create_file(buf, 0444, audmux_debugfs_root, 148 (void *)i, &audmux_debugfs_fops); 149 } 150 } 151 152 static void audmux_debugfs_remove(void) 153 { 154 debugfs_remove_recursive(audmux_debugfs_root); 155 } 156 #else 157 static inline void audmux_debugfs_init(void) 158 { 159 } 160 161 static inline void audmux_debugfs_remove(void) 162 { 163 } 164 #endif 165 166 static enum imx_audmux_type { 167 IMX21_AUDMUX, 168 IMX31_AUDMUX, 169 } audmux_type; 170 171 static const struct platform_device_id imx_audmux_ids[] = { 172 { 173 .name = "imx21-audmux", 174 .driver_data = IMX21_AUDMUX, 175 }, { 176 .name = "imx31-audmux", 177 .driver_data = IMX31_AUDMUX, 178 }, { 179 /* sentinel */ 180 } 181 }; 182 MODULE_DEVICE_TABLE(platform, imx_audmux_ids); 183 184 static const struct of_device_id imx_audmux_dt_ids[] = { 185 { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], }, 186 { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], }, 187 { /* sentinel */ } 188 }; 189 MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids); 190 191 static const uint8_t port_mapping[] = { 192 0x0, 0x4, 0x8, 0x10, 0x14, 0x1c, 193 }; 194 195 int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr) 196 { 197 if (audmux_type != IMX21_AUDMUX) 198 return -EINVAL; 199 200 if (!audmux_base) 201 return -ENOSYS; 202 203 if (port >= ARRAY_SIZE(port_mapping)) 204 return -EINVAL; 205 206 writel(pcr, audmux_base + port_mapping[port]); 207 208 return 0; 209 } 210 EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port); 211 212 int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, 213 unsigned int pdcr) 214 { 215 int ret; 216 217 if (audmux_type != IMX31_AUDMUX) 218 return -EINVAL; 219 220 if (!audmux_base) 221 return -ENOSYS; 222 223 if (audmux_clk) { 224 ret = clk_prepare_enable(audmux_clk); 225 if (ret) 226 return ret; 227 } 228 229 writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port)); 230 writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port)); 231 232 if (audmux_clk) 233 clk_disable_unprepare(audmux_clk); 234 235 return 0; 236 } 237 EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port); 238 239 static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, 240 struct device_node *of_node) 241 { 242 struct device_node *child; 243 244 for_each_available_child_of_node(of_node, child) { 245 unsigned int port; 246 unsigned int ptcr = 0; 247 unsigned int pdcr = 0; 248 unsigned int pcr = 0; 249 unsigned int val; 250 int ret; 251 int i = 0; 252 253 ret = of_property_read_u32(child, "fsl,audmux-port", &port); 254 if (ret) { 255 dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node \"%pOF\"\n", 256 child); 257 continue; 258 } 259 if (!of_property_read_bool(child, "fsl,port-config")) { 260 dev_warn(&pdev->dev, "child node \"%pOF\" does not have property fsl,port-config\n", 261 child); 262 continue; 263 } 264 265 for (i = 0; (ret = of_property_read_u32_index(child, 266 "fsl,port-config", i, &val)) == 0; 267 ++i) { 268 if (audmux_type == IMX31_AUDMUX) { 269 if (i % 2) 270 pdcr |= val; 271 else 272 ptcr |= val; 273 } else { 274 pcr |= val; 275 } 276 } 277 278 if (ret != -EOVERFLOW) { 279 dev_err(&pdev->dev, "Failed to read u32 at index %d of child %pOF\n", 280 i, child); 281 continue; 282 } 283 284 if (audmux_type == IMX31_AUDMUX) { 285 if (i % 2) { 286 dev_err(&pdev->dev, "One pdcr value is missing in child node %pOF\n", 287 child); 288 continue; 289 } 290 imx_audmux_v2_configure_port(port, ptcr, pdcr); 291 } else { 292 imx_audmux_v1_configure_port(port, pcr); 293 } 294 } 295 296 return 0; 297 } 298 299 static int imx_audmux_probe(struct platform_device *pdev) 300 { 301 struct resource *res; 302 const struct of_device_id *of_id = 303 of_match_device(imx_audmux_dt_ids, &pdev->dev); 304 305 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 306 audmux_base = devm_ioremap_resource(&pdev->dev, res); 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 if (audmux_type == IMX31_AUDMUX) 321 audmux_debugfs_init(); 322 323 if (of_id) 324 imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node); 325 326 return 0; 327 } 328 329 static int imx_audmux_remove(struct platform_device *pdev) 330 { 331 if (audmux_type == IMX31_AUDMUX) 332 audmux_debugfs_remove(); 333 334 return 0; 335 } 336 337 static struct platform_driver imx_audmux_driver = { 338 .probe = imx_audmux_probe, 339 .remove = imx_audmux_remove, 340 .id_table = imx_audmux_ids, 341 .driver = { 342 .name = DRIVER_NAME, 343 .of_match_table = imx_audmux_dt_ids, 344 } 345 }; 346 347 static int __init imx_audmux_init(void) 348 { 349 return platform_driver_register(&imx_audmux_driver); 350 } 351 subsys_initcall(imx_audmux_init); 352 353 static void __exit imx_audmux_exit(void) 354 { 355 platform_driver_unregister(&imx_audmux_driver); 356 } 357 module_exit(imx_audmux_exit); 358 359 MODULE_DESCRIPTION("Freescale i.MX AUDMUX driver"); 360 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 361 MODULE_LICENSE("GPL v2"); 362 MODULE_ALIAS("platform:" DRIVER_NAME); 363