1/* 2 * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> 3 * 4 * Initial development of this code was funded by 5 * Phytec Messtechnik GmbH, http://www.phytec.de 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 */ 17 18#include <linux/module.h> 19#include <linux/err.h> 20#include <linux/io.h> 21#include <linux/clk.h> 22#include <linux/debugfs.h> 23#include <linux/slab.h> 24#include <mach/audmux.h> 25#include <mach/hardware.h> 26 27static struct clk *audmux_clk; 28static void __iomem *audmux_base; 29 30#define MXC_AUDMUX_V2_PTCR(x) ((x) * 8) 31#define MXC_AUDMUX_V2_PDCR(x) ((x) * 8 + 4) 32 33#ifdef CONFIG_DEBUG_FS 34static struct dentry *audmux_debugfs_root; 35 36static int audmux_open_file(struct inode *inode, struct file *file) 37{ 38 file->private_data = inode->i_private; 39 return 0; 40} 41 42/* There is an annoying discontinuity in the SSI numbering with regard 43 * to the Linux number of the devices */ 44static const char *audmux_port_string(int port) 45{ 46 switch (port) { 47 case MX31_AUDMUX_PORT1_SSI0: 48 return "imx-ssi.0"; 49 case MX31_AUDMUX_PORT2_SSI1: 50 return "imx-ssi.1"; 51 case MX31_AUDMUX_PORT3_SSI_PINS_3: 52 return "SSI3"; 53 case MX31_AUDMUX_PORT4_SSI_PINS_4: 54 return "SSI4"; 55 case MX31_AUDMUX_PORT5_SSI_PINS_5: 56 return "SSI5"; 57 case MX31_AUDMUX_PORT6_SSI_PINS_6: 58 return "SSI6"; 59 default: 60 return "UNKNOWN"; 61 } 62} 63 64static ssize_t audmux_read_file(struct file *file, char __user *user_buf, 65 size_t count, loff_t *ppos) 66{ 67 ssize_t ret; 68 char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 69 int port = (int)file->private_data; 70 u32 pdcr, ptcr; 71 72 if (!buf) 73 return -ENOMEM; 74 75 if (audmux_clk) 76 clk_enable(audmux_clk); 77 78 ptcr = readl(audmux_base + MXC_AUDMUX_V2_PTCR(port)); 79 pdcr = readl(audmux_base + MXC_AUDMUX_V2_PDCR(port)); 80 81 if (audmux_clk) 82 clk_disable(audmux_clk); 83 84 ret = snprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n", 85 pdcr, ptcr); 86 87 if (ptcr & MXC_AUDMUX_V2_PTCR_TFSDIR) 88 ret += snprintf(buf + ret, PAGE_SIZE - ret, 89 "TxFS output from %s, ", 90 audmux_port_string((ptcr >> 27) & 0x7)); 91 else 92 ret += snprintf(buf + ret, PAGE_SIZE - ret, 93 "TxFS input, "); 94 95 if (ptcr & MXC_AUDMUX_V2_PTCR_TCLKDIR) 96 ret += snprintf(buf + ret, PAGE_SIZE - ret, 97 "TxClk output from %s", 98 audmux_port_string((ptcr >> 22) & 0x7)); 99 else 100 ret += snprintf(buf + ret, PAGE_SIZE - ret, 101 "TxClk input"); 102 103 ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n"); 104 105 if (ptcr & MXC_AUDMUX_V2_PTCR_SYN) { 106 ret += snprintf(buf + ret, PAGE_SIZE - ret, 107 "Port is symmetric"); 108 } else { 109 if (ptcr & MXC_AUDMUX_V2_PTCR_RFSDIR) 110 ret += snprintf(buf + ret, PAGE_SIZE - ret, 111 "RxFS output from %s, ", 112 audmux_port_string((ptcr >> 17) & 0x7)); 113 else 114 ret += snprintf(buf + ret, PAGE_SIZE - ret, 115 "RxFS input, "); 116 117 if (ptcr & MXC_AUDMUX_V2_PTCR_RCLKDIR) 118 ret += snprintf(buf + ret, PAGE_SIZE - ret, 119 "RxClk output from %s", 120 audmux_port_string((ptcr >> 12) & 0x7)); 121 else 122 ret += snprintf(buf + ret, PAGE_SIZE - ret, 123 "RxClk input"); 124 } 125 126 ret += snprintf(buf + ret, PAGE_SIZE - ret, 127 "\nData received from %s\n", 128 audmux_port_string((pdcr >> 13) & 0x7)); 129 130 ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); 131 132 kfree(buf); 133 134 return ret; 135} 136 137static const struct file_operations audmux_debugfs_fops = { 138 .open = audmux_open_file, 139 .read = audmux_read_file, 140}; 141 142static void audmux_debugfs_init(void) 143{ 144 int i; 145 char buf[20]; 146 147 audmux_debugfs_root = debugfs_create_dir("audmux", NULL); 148 if (!audmux_debugfs_root) { 149 pr_warning("Failed to create AUDMUX debugfs root\n"); 150 return; 151 } 152 153 for (i = 1; i < 8; i++) { 154 snprintf(buf, sizeof(buf), "ssi%d", i); 155 if (!debugfs_create_file(buf, 0444, audmux_debugfs_root, 156 (void *)i, &audmux_debugfs_fops)) 157 pr_warning("Failed to create AUDMUX port %d debugfs file\n", 158 i); 159 } 160} 161#else 162static inline void audmux_debugfs_init(void) 163{ 164} 165#endif 166 167int mxc_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, 168 unsigned int pdcr) 169{ 170 if (!audmux_base) 171 return -ENOSYS; 172 173 if (audmux_clk) 174 clk_enable(audmux_clk); 175 176 writel(ptcr, audmux_base + MXC_AUDMUX_V2_PTCR(port)); 177 writel(pdcr, audmux_base + MXC_AUDMUX_V2_PDCR(port)); 178 179 if (audmux_clk) 180 clk_disable(audmux_clk); 181 182 return 0; 183} 184EXPORT_SYMBOL_GPL(mxc_audmux_v2_configure_port); 185 186static int mxc_audmux_v2_init(void) 187{ 188 int ret; 189 190#if defined(CONFIG_ARCH_MX3) 191 if (cpu_is_mx31()) 192 audmux_base = MX31_IO_ADDRESS(MX31_AUDMUX_BASE_ADDR); 193 194 else if (cpu_is_mx35()) { 195 audmux_clk = clk_get(NULL, "audmux"); 196 if (IS_ERR(audmux_clk)) { 197 ret = PTR_ERR(audmux_clk); 198 printk(KERN_ERR "%s: cannot get clock: %d\n", __func__, 199 ret); 200 return ret; 201 } 202 audmux_base = MX35_IO_ADDRESS(MX35_AUDMUX_BASE_ADDR); 203 } 204#endif 205#if defined(CONFIG_ARCH_MX25) 206 if (cpu_is_mx25()) { 207 audmux_clk = clk_get(NULL, "audmux"); 208 if (IS_ERR(audmux_clk)) { 209 ret = PTR_ERR(audmux_clk); 210 printk(KERN_ERR "%s: cannot get clock: %d\n", __func__, 211 ret); 212 return ret; 213 } 214 audmux_base = MX25_IO_ADDRESS(MX25_AUDMUX_BASE_ADDR); 215 } 216#endif 217 audmux_debugfs_init(); 218 219 return 0; 220} 221 222postcore_initcall(mxc_audmux_v2_init); 223