1 /* 2 * linux/drivers/mfd/mcp-sa11x0.c 3 * 4 * Copyright (C) 2001-2005 Russell King 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License. 9 * 10 * SA11x0 MCP (Multimedia Communications Port) driver. 11 * 12 * MCP read/write timeouts from Jordi Colomer, rehacked by rmk. 13 */ 14 #include <linux/module.h> 15 #include <linux/init.h> 16 #include <linux/errno.h> 17 #include <linux/kernel.h> 18 #include <linux/delay.h> 19 #include <linux/spinlock.h> 20 #include <linux/platform_device.h> 21 #include <linux/mfd/mcp.h> 22 #include <linux/io.h> 23 24 #include <mach/dma.h> 25 #include <mach/hardware.h> 26 #include <asm/mach-types.h> 27 #include <asm/system.h> 28 #include <mach/mcp.h> 29 30 /* Register offsets */ 31 #define MCCR0 0x00 32 #define MCDR0 0x08 33 #define MCDR1 0x0C 34 #define MCDR2 0x10 35 #define MCSR 0x18 36 #define MCCR1 0x00 37 38 struct mcp_sa11x0 { 39 u32 mccr0; 40 u32 mccr1; 41 unsigned char *mccr0_base; 42 unsigned char *mccr1_base; 43 }; 44 45 #define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) 46 47 static void 48 mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) 49 { 50 struct mcp_sa11x0 *priv = priv(mcp); 51 52 divisor /= 32; 53 54 priv->mccr0 &= ~0x00007f00; 55 priv->mccr0 |= divisor << 8; 56 __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); 57 } 58 59 static void 60 mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) 61 { 62 struct mcp_sa11x0 *priv = priv(mcp); 63 64 divisor /= 32; 65 66 priv->mccr0 &= ~0x0000007f; 67 priv->mccr0 |= divisor; 68 __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); 69 } 70 71 /* 72 * Write data to the device. The bit should be set after 3 subframe 73 * times (each frame is 64 clocks). We wait a maximum of 6 subframes. 74 * We really should try doing something more productive while we 75 * wait. 76 */ 77 static void 78 mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) 79 { 80 int ret = -ETIME; 81 int i; 82 u32 mcpreg; 83 struct mcp_sa11x0 *priv = priv(mcp); 84 85 mcpreg = reg << 17 | MCDR2_Wr | (val & 0xffff); 86 __raw_writel(mcpreg, priv->mccr0_base + MCDR2); 87 88 for (i = 0; i < 2; i++) { 89 udelay(mcp->rw_timeout); 90 mcpreg = __raw_readl(priv->mccr0_base + MCSR); 91 if (mcpreg & MCSR_CWC) { 92 ret = 0; 93 break; 94 } 95 } 96 97 if (ret < 0) 98 printk(KERN_WARNING "mcp: write timed out\n"); 99 } 100 101 /* 102 * Read data from the device. The bit should be set after 3 subframe 103 * times (each frame is 64 clocks). We wait a maximum of 6 subframes. 104 * We really should try doing something more productive while we 105 * wait. 106 */ 107 static unsigned int 108 mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) 109 { 110 int ret = -ETIME; 111 int i; 112 u32 mcpreg; 113 struct mcp_sa11x0 *priv = priv(mcp); 114 115 mcpreg = reg << 17 | MCDR2_Rd; 116 __raw_writel(mcpreg, priv->mccr0_base + MCDR2); 117 118 for (i = 0; i < 2; i++) { 119 udelay(mcp->rw_timeout); 120 mcpreg = __raw_readl(priv->mccr0_base + MCSR); 121 if (mcpreg & MCSR_CRC) { 122 ret = __raw_readl(priv->mccr0_base + MCDR2) 123 & 0xffff; 124 break; 125 } 126 } 127 128 if (ret < 0) 129 printk(KERN_WARNING "mcp: read timed out\n"); 130 131 return ret; 132 } 133 134 static void mcp_sa11x0_enable(struct mcp *mcp) 135 { 136 struct mcp_sa11x0 *priv = priv(mcp); 137 138 __raw_writel(-1, priv->mccr0_base + MCSR); 139 priv->mccr0 |= MCCR0_MCE; 140 __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); 141 } 142 143 static void mcp_sa11x0_disable(struct mcp *mcp) 144 { 145 struct mcp_sa11x0 *priv = priv(mcp); 146 147 priv->mccr0 &= ~MCCR0_MCE; 148 __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); 149 } 150 151 /* 152 * Our methods. 153 */ 154 static struct mcp_ops mcp_sa11x0 = { 155 .set_telecom_divisor = mcp_sa11x0_set_telecom_divisor, 156 .set_audio_divisor = mcp_sa11x0_set_audio_divisor, 157 .reg_write = mcp_sa11x0_write, 158 .reg_read = mcp_sa11x0_read, 159 .enable = mcp_sa11x0_enable, 160 .disable = mcp_sa11x0_disable, 161 }; 162 163 static int mcp_sa11x0_probe(struct platform_device *pdev) 164 { 165 struct mcp_plat_data *data = pdev->dev.platform_data; 166 struct mcp *mcp; 167 int ret; 168 struct mcp_sa11x0 *priv; 169 struct resource *res_mem0, *res_mem1; 170 u32 size0, size1; 171 172 if (!data) 173 return -ENODEV; 174 175 if (!data->codec) 176 return -ENODEV; 177 178 res_mem0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); 179 if (!res_mem0) 180 return -ENODEV; 181 size0 = res_mem0->end - res_mem0->start + 1; 182 183 res_mem1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); 184 if (!res_mem1) 185 return -ENODEV; 186 size1 = res_mem1->end - res_mem1->start + 1; 187 188 if (!request_mem_region(res_mem0->start, size0, "sa11x0-mcp")) 189 return -EBUSY; 190 191 if (!request_mem_region(res_mem1->start, size1, "sa11x0-mcp")) { 192 ret = -EBUSY; 193 goto release; 194 } 195 196 mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0)); 197 if (!mcp) { 198 ret = -ENOMEM; 199 goto release2; 200 } 201 202 priv = priv(mcp); 203 204 mcp->owner = THIS_MODULE; 205 mcp->ops = &mcp_sa11x0; 206 mcp->sclk_rate = data->sclk_rate; 207 mcp->dma_audio_rd = DDAR_DevAdd(res_mem0->start + MCDR0) 208 + DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev; 209 mcp->dma_audio_wr = DDAR_DevAdd(res_mem0->start + MCDR0) 210 + DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev; 211 mcp->dma_telco_rd = DDAR_DevAdd(res_mem0->start + MCDR1) 212 + DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev; 213 mcp->dma_telco_wr = DDAR_DevAdd(res_mem0->start + MCDR1) 214 + DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev; 215 mcp->codec = data->codec; 216 217 platform_set_drvdata(pdev, mcp); 218 219 /* 220 * Initialise device. Note that we initially 221 * set the sampling rate to minimum. 222 */ 223 priv->mccr0_base = ioremap(res_mem0->start, size0); 224 priv->mccr1_base = ioremap(res_mem1->start, size1); 225 226 __raw_writel(-1, priv->mccr0_base + MCSR); 227 priv->mccr1 = data->mccr1; 228 priv->mccr0 = data->mccr0 | 0x7f7f; 229 __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); 230 __raw_writel(priv->mccr1, priv->mccr1_base + MCCR1); 231 232 /* 233 * Calculate the read/write timeout (us) from the bit clock 234 * rate. This is the period for 3 64-bit frames. Always 235 * round this time up. 236 */ 237 mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / 238 mcp->sclk_rate; 239 240 ret = mcp_host_register(mcp, data->codec_pdata); 241 if (ret == 0) 242 goto out; 243 244 release2: 245 release_mem_region(res_mem1->start, size1); 246 release: 247 release_mem_region(res_mem0->start, size0); 248 platform_set_drvdata(pdev, NULL); 249 250 out: 251 return ret; 252 } 253 254 static int mcp_sa11x0_remove(struct platform_device *pdev) 255 { 256 struct mcp *mcp = platform_get_drvdata(pdev); 257 struct mcp_sa11x0 *priv = priv(mcp); 258 struct resource *res_mem; 259 u32 size; 260 261 platform_set_drvdata(pdev, NULL); 262 mcp_host_unregister(mcp); 263 264 res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 265 if (res_mem) { 266 size = res_mem->end - res_mem->start + 1; 267 release_mem_region(res_mem->start, size); 268 } 269 res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 270 if (res_mem) { 271 size = res_mem->end - res_mem->start + 1; 272 release_mem_region(res_mem->start, size); 273 } 274 iounmap(priv->mccr0_base); 275 iounmap(priv->mccr1_base); 276 return 0; 277 } 278 279 static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) 280 { 281 struct mcp *mcp = platform_get_drvdata(dev); 282 struct mcp_sa11x0 *priv = priv(mcp); 283 u32 mccr0; 284 285 mccr0 = priv->mccr0 & ~MCCR0_MCE; 286 __raw_writel(mccr0, priv->mccr0_base + MCCR0); 287 288 return 0; 289 } 290 291 static int mcp_sa11x0_resume(struct platform_device *dev) 292 { 293 struct mcp *mcp = platform_get_drvdata(dev); 294 struct mcp_sa11x0 *priv = priv(mcp); 295 296 __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); 297 __raw_writel(priv->mccr1, priv->mccr1_base + MCCR1); 298 299 return 0; 300 } 301 302 /* 303 * The driver for the SA11x0 MCP port. 304 */ 305 MODULE_ALIAS("platform:sa11x0-mcp"); 306 307 static struct platform_driver mcp_sa11x0_driver = { 308 .probe = mcp_sa11x0_probe, 309 .remove = mcp_sa11x0_remove, 310 .suspend = mcp_sa11x0_suspend, 311 .resume = mcp_sa11x0_resume, 312 .driver = { 313 .name = "sa11x0-mcp", 314 .owner = THIS_MODULE, 315 }, 316 }; 317 318 /* 319 * This needs re-working 320 */ 321 module_platform_driver(mcp_sa11x0_driver); 322 323 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); 324 MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); 325 MODULE_LICENSE("GPL"); 326