1 /* 2 * Copyright 2012 Freescale Semiconductor, Inc. 3 * Copyright 2012 Linaro Ltd. 4 * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> 5 * 6 * Initial development of this code was funded by 7 * Phytec Messtechnik GmbH, http://www.phytec.de 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 */ 19 20 #include <linux/clk.h> 21 #include <linux/debugfs.h> 22 #include <linux/err.h> 23 #include <linux/io.h> 24 #include <linux/module.h> 25 #include <linux/of.h> 26 #include <linux/of_device.h> 27 #include <linux/platform_device.h> 28 #include <linux/slab.h> 29 #include <linux/pinctrl/consumer.h> 30 31 #include "imx-audmux.h" 32 33 #define DRIVER_NAME "imx-audmux" 34 35 static struct clk *audmux_clk; 36 static void __iomem *audmux_base; 37 38 #define IMX_AUDMUX_V2_PTCR(x) ((x) * 8) 39 #define IMX_AUDMUX_V2_PDCR(x) ((x) * 8 + 4) 40 41 #ifdef CONFIG_DEBUG_FS 42 static struct dentry *audmux_debugfs_root; 43 44 /* There is an annoying discontinuity in the SSI numbering with regard 45 * to the Linux number of the devices */ 46 static const char *audmux_port_string(int port) 47 { 48 switch (port) { 49 case MX31_AUDMUX_PORT1_SSI0: 50 return "imx-ssi.0"; 51 case MX31_AUDMUX_PORT2_SSI1: 52 return "imx-ssi.1"; 53 case MX31_AUDMUX_PORT3_SSI_PINS_3: 54 return "SSI3"; 55 case MX31_AUDMUX_PORT4_SSI_PINS_4: 56 return "SSI4"; 57 case MX31_AUDMUX_PORT5_SSI_PINS_5: 58 return "SSI5"; 59 case MX31_AUDMUX_PORT6_SSI_PINS_6: 60 return "SSI6"; 61 default: 62 return "UNKNOWN"; 63 } 64 } 65 66 static ssize_t audmux_read_file(struct file *file, char __user *user_buf, 67 size_t count, loff_t *ppos) 68 { 69 ssize_t ret; 70 char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 71 int port = (int)file->private_data; 72 u32 pdcr, ptcr; 73 74 if (!buf) 75 return -ENOMEM; 76 77 if (audmux_clk) 78 clk_prepare_enable(audmux_clk); 79 80 ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port)); 81 pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port)); 82 83 if (audmux_clk) 84 clk_disable_unprepare(audmux_clk); 85 86 ret = snprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n", 87 pdcr, ptcr); 88 89 if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR) 90 ret += snprintf(buf + ret, PAGE_SIZE - ret, 91 "TxFS output from %s, ", 92 audmux_port_string((ptcr >> 27) & 0x7)); 93 else 94 ret += snprintf(buf + ret, PAGE_SIZE - ret, 95 "TxFS input, "); 96 97 if (ptcr & IMX_AUDMUX_V2_PTCR_TCLKDIR) 98 ret += snprintf(buf + ret, PAGE_SIZE - ret, 99 "TxClk output from %s", 100 audmux_port_string((ptcr >> 22) & 0x7)); 101 else 102 ret += snprintf(buf + ret, PAGE_SIZE - ret, 103 "TxClk input"); 104 105 ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n"); 106 107 if (ptcr & IMX_AUDMUX_V2_PTCR_SYN) { 108 ret += snprintf(buf + ret, PAGE_SIZE - ret, 109 "Port is symmetric"); 110 } else { 111 if (ptcr & IMX_AUDMUX_V2_PTCR_RFSDIR) 112 ret += snprintf(buf + ret, PAGE_SIZE - ret, 113 "RxFS output from %s, ", 114 audmux_port_string((ptcr >> 17) & 0x7)); 115 else 116 ret += snprintf(buf + ret, PAGE_SIZE - ret, 117 "RxFS input, "); 118 119 if (ptcr & IMX_AUDMUX_V2_PTCR_RCLKDIR) 120 ret += snprintf(buf + ret, PAGE_SIZE - ret, 121 "RxClk output from %s", 122 audmux_port_string((ptcr >> 12) & 0x7)); 123 else 124 ret += snprintf(buf + ret, PAGE_SIZE - ret, 125 "RxClk input"); 126 } 127 128 ret += snprintf(buf + ret, PAGE_SIZE - ret, 129 "\nData received from %s\n", 130 audmux_port_string((pdcr >> 13) & 0x7)); 131 132 ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); 133 134 kfree(buf); 135 136 return ret; 137 } 138 139 static const struct file_operations audmux_debugfs_fops = { 140 .open = simple_open, 141 .read = audmux_read_file, 142 .llseek = default_llseek, 143 }; 144 145 static void __init audmux_debugfs_init(void) 146 { 147 int i; 148 char buf[20]; 149 150 audmux_debugfs_root = debugfs_create_dir("audmux", NULL); 151 if (!audmux_debugfs_root) { 152 pr_warning("Failed to create AUDMUX debugfs root\n"); 153 return; 154 } 155 156 for (i = 0; i < MX31_AUDMUX_PORT7_SSI_PINS_7 + 1; i++) { 157 snprintf(buf, sizeof(buf), "ssi%d", i); 158 if (!debugfs_create_file(buf, 0444, audmux_debugfs_root, 159 (void *)i, &audmux_debugfs_fops)) 160 pr_warning("Failed to create AUDMUX port %d debugfs file\n", 161 i); 162 } 163 } 164 165 static void audmux_debugfs_remove(void) 166 { 167 debugfs_remove_recursive(audmux_debugfs_root); 168 } 169 #else 170 static inline void audmux_debugfs_init(void) 171 { 172 } 173 174 static inline void audmux_debugfs_remove(void) 175 { 176 } 177 #endif 178 179 enum imx_audmux_type { 180 IMX21_AUDMUX, 181 IMX31_AUDMUX, 182 } audmux_type; 183 184 static struct platform_device_id imx_audmux_ids[] = { 185 { 186 .name = "imx21-audmux", 187 .driver_data = IMX21_AUDMUX, 188 }, { 189 .name = "imx31-audmux", 190 .driver_data = IMX31_AUDMUX, 191 }, { 192 /* sentinel */ 193 } 194 }; 195 MODULE_DEVICE_TABLE(platform, imx_audmux_ids); 196 197 static const struct of_device_id imx_audmux_dt_ids[] = { 198 { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], }, 199 { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], }, 200 { /* sentinel */ } 201 }; 202 MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids); 203 204 static const uint8_t port_mapping[] = { 205 0x0, 0x4, 0x8, 0x10, 0x14, 0x1c, 206 }; 207 208 int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr) 209 { 210 if (audmux_type != IMX21_AUDMUX) 211 return -EINVAL; 212 213 if (!audmux_base) 214 return -ENOSYS; 215 216 if (port >= ARRAY_SIZE(port_mapping)) 217 return -EINVAL; 218 219 writel(pcr, audmux_base + port_mapping[port]); 220 221 return 0; 222 } 223 EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port); 224 225 int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, 226 unsigned int pdcr) 227 { 228 if (audmux_type != IMX31_AUDMUX) 229 return -EINVAL; 230 231 if (!audmux_base) 232 return -ENOSYS; 233 234 if (audmux_clk) 235 clk_prepare_enable(audmux_clk); 236 237 writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port)); 238 writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port)); 239 240 if (audmux_clk) 241 clk_disable_unprepare(audmux_clk); 242 243 return 0; 244 } 245 EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port); 246 247 static int imx_audmux_probe(struct platform_device *pdev) 248 { 249 struct resource *res; 250 struct pinctrl *pinctrl; 251 const struct of_device_id *of_id = 252 of_match_device(imx_audmux_dt_ids, &pdev->dev); 253 254 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 255 audmux_base = devm_request_and_ioremap(&pdev->dev, res); 256 if (!audmux_base) 257 return -EADDRNOTAVAIL; 258 259 pinctrl = devm_pinctrl_get_select_default(&pdev->dev); 260 if (IS_ERR(pinctrl)) { 261 dev_err(&pdev->dev, "setup pinctrl failed!"); 262 return PTR_ERR(pinctrl); 263 } 264 265 audmux_clk = clk_get(&pdev->dev, "audmux"); 266 if (IS_ERR(audmux_clk)) { 267 dev_dbg(&pdev->dev, "cannot get clock: %ld\n", 268 PTR_ERR(audmux_clk)); 269 audmux_clk = NULL; 270 } 271 272 if (of_id) 273 pdev->id_entry = of_id->data; 274 audmux_type = pdev->id_entry->driver_data; 275 if (audmux_type == IMX31_AUDMUX) 276 audmux_debugfs_init(); 277 278 return 0; 279 } 280 281 static int imx_audmux_remove(struct platform_device *pdev) 282 { 283 if (audmux_type == IMX31_AUDMUX) 284 audmux_debugfs_remove(); 285 clk_put(audmux_clk); 286 287 return 0; 288 } 289 290 static struct platform_driver imx_audmux_driver = { 291 .probe = imx_audmux_probe, 292 .remove = imx_audmux_remove, 293 .id_table = imx_audmux_ids, 294 .driver = { 295 .name = DRIVER_NAME, 296 .owner = THIS_MODULE, 297 .of_match_table = imx_audmux_dt_ids, 298 } 299 }; 300 301 static int __init imx_audmux_init(void) 302 { 303 return platform_driver_register(&imx_audmux_driver); 304 } 305 subsys_initcall(imx_audmux_init); 306 307 static void __exit imx_audmux_exit(void) 308 { 309 platform_driver_unregister(&imx_audmux_driver); 310 } 311 module_exit(imx_audmux_exit); 312 313 MODULE_DESCRIPTION("Freescale i.MX AUDMUX driver"); 314 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 315 MODULE_LICENSE("GPL v2"); 316 MODULE_ALIAS("platform:" DRIVER_NAME); 317