xref: /linux/drivers/mfd/mcp-sa11x0.c (revision dcea83adc666061864b82c96e059dffe7268b512)
15e742ad6SRussell King /*
25e742ad6SRussell King  *  linux/drivers/mfd/mcp-sa11x0.c
35e742ad6SRussell King  *
45e742ad6SRussell King  *  Copyright (C) 2001-2005 Russell King
55e742ad6SRussell King  *
65e742ad6SRussell King  * This program is free software; you can redistribute it and/or modify
75e742ad6SRussell King  * it under the terms of the GNU General Public License as published by
85e742ad6SRussell King  * the Free Software Foundation; either version 2 of the License.
95e742ad6SRussell King  *
105e742ad6SRussell King  *  SA11x0 MCP (Multimedia Communications Port) driver.
115e742ad6SRussell King  *
125e742ad6SRussell King  *  MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
135e742ad6SRussell King  */
145e742ad6SRussell King #include <linux/module.h>
155e742ad6SRussell King #include <linux/init.h>
165e742ad6SRussell King #include <linux/errno.h>
175e742ad6SRussell King #include <linux/kernel.h>
185e742ad6SRussell King #include <linux/delay.h>
195e742ad6SRussell King #include <linux/spinlock.h>
205e742ad6SRussell King #include <linux/slab.h>
21d052d1beSRussell King #include <linux/platform_device.h>
225e742ad6SRussell King 
23*dcea83adSRussell King #include <mach/dma.h>
24a09e64fbSRussell King #include <mach/hardware.h>
255e742ad6SRussell King #include <asm/mach-types.h>
265e742ad6SRussell King #include <asm/system.h>
27a09e64fbSRussell King #include <mach/mcp.h>
285e742ad6SRussell King 
29a09e64fbSRussell King #include <mach/assabet.h>
305e742ad6SRussell King 
315e742ad6SRussell King #include "mcp.h"
325e742ad6SRussell King 
335e742ad6SRussell King struct mcp_sa11x0 {
345e742ad6SRussell King 	u32	mccr0;
355e742ad6SRussell King 	u32	mccr1;
365e742ad6SRussell King };
375e742ad6SRussell King 
385e742ad6SRussell King #define priv(mcp)	((struct mcp_sa11x0 *)mcp_priv(mcp))
395e742ad6SRussell King 
405e742ad6SRussell King static void
415e742ad6SRussell King mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
425e742ad6SRussell King {
435e742ad6SRussell King 	unsigned int mccr0;
445e742ad6SRussell King 
455e742ad6SRussell King 	divisor /= 32;
465e742ad6SRussell King 
475e742ad6SRussell King 	mccr0 = Ser4MCCR0 & ~0x00007f00;
485e742ad6SRussell King 	mccr0 |= divisor << 8;
495e742ad6SRussell King 	Ser4MCCR0 = mccr0;
505e742ad6SRussell King }
515e742ad6SRussell King 
525e742ad6SRussell King static void
535e742ad6SRussell King mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
545e742ad6SRussell King {
555e742ad6SRussell King 	unsigned int mccr0;
565e742ad6SRussell King 
575e742ad6SRussell King 	divisor /= 32;
585e742ad6SRussell King 
595e742ad6SRussell King 	mccr0 = Ser4MCCR0 & ~0x0000007f;
605e742ad6SRussell King 	mccr0 |= divisor;
615e742ad6SRussell King 	Ser4MCCR0 = mccr0;
625e742ad6SRussell King }
635e742ad6SRussell King 
645e742ad6SRussell King /*
655e742ad6SRussell King  * Write data to the device.  The bit should be set after 3 subframe
665e742ad6SRussell King  * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
675e742ad6SRussell King  * We really should try doing something more productive while we
685e742ad6SRussell King  * wait.
695e742ad6SRussell King  */
705e742ad6SRussell King static void
715e742ad6SRussell King mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
725e742ad6SRussell King {
735e742ad6SRussell King 	int ret = -ETIME;
745e742ad6SRussell King 	int i;
755e742ad6SRussell King 
765e742ad6SRussell King 	Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);
775e742ad6SRussell King 
785e742ad6SRussell King 	for (i = 0; i < 2; i++) {
795e742ad6SRussell King 		udelay(mcp->rw_timeout);
805e742ad6SRussell King 		if (Ser4MCSR & MCSR_CWC) {
815e742ad6SRussell King 			ret = 0;
825e742ad6SRussell King 			break;
835e742ad6SRussell King 		}
845e742ad6SRussell King 	}
855e742ad6SRussell King 
865e742ad6SRussell King 	if (ret < 0)
875e742ad6SRussell King 		printk(KERN_WARNING "mcp: write timed out\n");
885e742ad6SRussell King }
895e742ad6SRussell King 
905e742ad6SRussell King /*
915e742ad6SRussell King  * Read data from the device.  The bit should be set after 3 subframe
925e742ad6SRussell King  * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
935e742ad6SRussell King  * We really should try doing something more productive while we
945e742ad6SRussell King  * wait.
955e742ad6SRussell King  */
965e742ad6SRussell King static unsigned int
975e742ad6SRussell King mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
985e742ad6SRussell King {
995e742ad6SRussell King 	int ret = -ETIME;
1005e742ad6SRussell King 	int i;
1015e742ad6SRussell King 
1025e742ad6SRussell King 	Ser4MCDR2 = reg << 17 | MCDR2_Rd;
1035e742ad6SRussell King 
1045e742ad6SRussell King 	for (i = 0; i < 2; i++) {
1055e742ad6SRussell King 		udelay(mcp->rw_timeout);
1065e742ad6SRussell King 		if (Ser4MCSR & MCSR_CRC) {
1075e742ad6SRussell King 			ret = Ser4MCDR2 & 0xffff;
1085e742ad6SRussell King 			break;
1095e742ad6SRussell King 		}
1105e742ad6SRussell King 	}
1115e742ad6SRussell King 
1125e742ad6SRussell King 	if (ret < 0)
1135e742ad6SRussell King 		printk(KERN_WARNING "mcp: read timed out\n");
1145e742ad6SRussell King 
1155e742ad6SRussell King 	return ret;
1165e742ad6SRussell King }
1175e742ad6SRussell King 
1185e742ad6SRussell King static void mcp_sa11x0_enable(struct mcp *mcp)
1195e742ad6SRussell King {
1205e742ad6SRussell King 	Ser4MCSR = -1;
1215e742ad6SRussell King 	Ser4MCCR0 |= MCCR0_MCE;
1225e742ad6SRussell King }
1235e742ad6SRussell King 
1245e742ad6SRussell King static void mcp_sa11x0_disable(struct mcp *mcp)
1255e742ad6SRussell King {
1265e742ad6SRussell King 	Ser4MCCR0 &= ~MCCR0_MCE;
1275e742ad6SRussell King }
1285e742ad6SRussell King 
1295e742ad6SRussell King /*
1305e742ad6SRussell King  * Our methods.
1315e742ad6SRussell King  */
1325e742ad6SRussell King static struct mcp_ops mcp_sa11x0 = {
1335e742ad6SRussell King 	.set_telecom_divisor	= mcp_sa11x0_set_telecom_divisor,
1345e742ad6SRussell King 	.set_audio_divisor	= mcp_sa11x0_set_audio_divisor,
1355e742ad6SRussell King 	.reg_write		= mcp_sa11x0_write,
1365e742ad6SRussell King 	.reg_read		= mcp_sa11x0_read,
1375e742ad6SRussell King 	.enable			= mcp_sa11x0_enable,
1385e742ad6SRussell King 	.disable		= mcp_sa11x0_disable,
1395e742ad6SRussell King };
1405e742ad6SRussell King 
1413ae5eaecSRussell King static int mcp_sa11x0_probe(struct platform_device *pdev)
1425e742ad6SRussell King {
143323cdfc1SRussell King 	struct mcp_plat_data *data = pdev->dev.platform_data;
1445e742ad6SRussell King 	struct mcp *mcp;
1455e742ad6SRussell King 	int ret;
1465e742ad6SRussell King 
147323cdfc1SRussell King 	if (!data)
1485e742ad6SRussell King 		return -ENODEV;
1495e742ad6SRussell King 
1505e742ad6SRussell King 	if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))
1515e742ad6SRussell King 		return -EBUSY;
1525e742ad6SRussell King 
1535e742ad6SRussell King 	mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0));
1545e742ad6SRussell King 	if (!mcp) {
1555e742ad6SRussell King 		ret = -ENOMEM;
1565e742ad6SRussell King 		goto release;
1575e742ad6SRussell King 	}
1585e742ad6SRussell King 
1595e742ad6SRussell King 	mcp->owner		= THIS_MODULE;
1605e742ad6SRussell King 	mcp->ops		= &mcp_sa11x0;
161323cdfc1SRussell King 	mcp->sclk_rate		= data->sclk_rate;
1625e742ad6SRussell King 	mcp->dma_audio_rd	= DMA_Ser4MCP0Rd;
1635e742ad6SRussell King 	mcp->dma_audio_wr	= DMA_Ser4MCP0Wr;
1645e742ad6SRussell King 	mcp->dma_telco_rd	= DMA_Ser4MCP1Rd;
1655e742ad6SRussell King 	mcp->dma_telco_wr	= DMA_Ser4MCP1Wr;
1665e742ad6SRussell King 
1673ae5eaecSRussell King 	platform_set_drvdata(pdev, mcp);
1685e742ad6SRussell King 
1695e742ad6SRussell King 	if (machine_is_assabet()) {
1705e742ad6SRussell King 		ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
1715e742ad6SRussell King 	}
1725e742ad6SRussell King 
1735e742ad6SRussell King 	/*
1745e742ad6SRussell King 	 * Setup the PPC unit correctly.
1755e742ad6SRussell King 	 */
1765e742ad6SRussell King 	PPDR &= ~PPC_RXD4;
1775e742ad6SRussell King 	PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
1785e742ad6SRussell King 	PSDR |= PPC_RXD4;
1795e742ad6SRussell King 	PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
1805e742ad6SRussell King 	PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
1815e742ad6SRussell King 
182323cdfc1SRussell King 	/*
183323cdfc1SRussell King 	 * Initialise device.  Note that we initially
184323cdfc1SRussell King 	 * set the sampling rate to minimum.
185323cdfc1SRussell King 	 */
1865e742ad6SRussell King 	Ser4MCSR = -1;
187323cdfc1SRussell King 	Ser4MCCR1 = data->mccr1;
188323cdfc1SRussell King 	Ser4MCCR0 = data->mccr0 | 0x7f7f;
1895e742ad6SRussell King 
1905e742ad6SRussell King 	/*
1915e742ad6SRussell King 	 * Calculate the read/write timeout (us) from the bit clock
1925e742ad6SRussell King 	 * rate.  This is the period for 3 64-bit frames.  Always
1935e742ad6SRussell King 	 * round this time up.
1945e742ad6SRussell King 	 */
1955e742ad6SRussell King 	mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
1965e742ad6SRussell King 			  mcp->sclk_rate;
1975e742ad6SRussell King 
1985e742ad6SRussell King 	ret = mcp_host_register(mcp);
1995e742ad6SRussell King 	if (ret == 0)
2005e742ad6SRussell King 		goto out;
2015e742ad6SRussell King 
2025e742ad6SRussell King  release:
2035e742ad6SRussell King 	release_mem_region(0x80060000, 0x60);
2043ae5eaecSRussell King 	platform_set_drvdata(pdev, NULL);
2055e742ad6SRussell King 
2065e742ad6SRussell King  out:
2075e742ad6SRussell King 	return ret;
2085e742ad6SRussell King }
2095e742ad6SRussell King 
2103ae5eaecSRussell King static int mcp_sa11x0_remove(struct platform_device *dev)
2115e742ad6SRussell King {
2123ae5eaecSRussell King 	struct mcp *mcp = platform_get_drvdata(dev);
2135e742ad6SRussell King 
2143ae5eaecSRussell King 	platform_set_drvdata(dev, NULL);
2155e742ad6SRussell King 	mcp_host_unregister(mcp);
2165e742ad6SRussell King 	release_mem_region(0x80060000, 0x60);
2175e742ad6SRussell King 
2185e742ad6SRussell King 	return 0;
2195e742ad6SRussell King }
2205e742ad6SRussell King 
2213ae5eaecSRussell King static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state)
2225e742ad6SRussell King {
2233ae5eaecSRussell King 	struct mcp *mcp = platform_get_drvdata(dev);
2245e742ad6SRussell King 
2255e742ad6SRussell King 	priv(mcp)->mccr0 = Ser4MCCR0;
2265e742ad6SRussell King 	priv(mcp)->mccr1 = Ser4MCCR1;
2275e742ad6SRussell King 	Ser4MCCR0 &= ~MCCR0_MCE;
2289480e307SRussell King 
2295e742ad6SRussell King 	return 0;
2305e742ad6SRussell King }
2315e742ad6SRussell King 
2323ae5eaecSRussell King static int mcp_sa11x0_resume(struct platform_device *dev)
2335e742ad6SRussell King {
2343ae5eaecSRussell King 	struct mcp *mcp = platform_get_drvdata(dev);
2355e742ad6SRussell King 
2365e742ad6SRussell King 	Ser4MCCR1 = priv(mcp)->mccr1;
2375e742ad6SRussell King 	Ser4MCCR0 = priv(mcp)->mccr0;
2389480e307SRussell King 
2395e742ad6SRussell King 	return 0;
2405e742ad6SRussell King }
2415e742ad6SRussell King 
2425e742ad6SRussell King /*
2435e742ad6SRussell King  * The driver for the SA11x0 MCP port.
2445e742ad6SRussell King  */
2454f46d6e7SKay Sievers MODULE_ALIAS("platform:sa11x0-mcp");
2464f46d6e7SKay Sievers 
2473ae5eaecSRussell King static struct platform_driver mcp_sa11x0_driver = {
2485e742ad6SRussell King 	.probe		= mcp_sa11x0_probe,
2495e742ad6SRussell King 	.remove		= mcp_sa11x0_remove,
2505e742ad6SRussell King 	.suspend	= mcp_sa11x0_suspend,
2515e742ad6SRussell King 	.resume		= mcp_sa11x0_resume,
2523ae5eaecSRussell King 	.driver		= {
2533ae5eaecSRussell King 		.name	= "sa11x0-mcp",
2543ae5eaecSRussell King 	},
2555e742ad6SRussell King };
2565e742ad6SRussell King 
2575e742ad6SRussell King /*
2585e742ad6SRussell King  * This needs re-working
2595e742ad6SRussell King  */
2605e742ad6SRussell King static int __init mcp_sa11x0_init(void)
2615e742ad6SRussell King {
2623ae5eaecSRussell King 	return platform_driver_register(&mcp_sa11x0_driver);
2635e742ad6SRussell King }
2645e742ad6SRussell King 
2655e742ad6SRussell King static void __exit mcp_sa11x0_exit(void)
2665e742ad6SRussell King {
2673ae5eaecSRussell King 	platform_driver_unregister(&mcp_sa11x0_driver);
2685e742ad6SRussell King }
2695e742ad6SRussell King 
2705e742ad6SRussell King module_init(mcp_sa11x0_init);
2715e742ad6SRussell King module_exit(mcp_sa11x0_exit);
2725e742ad6SRussell King 
2735e742ad6SRussell King MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
2745e742ad6SRussell King MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
2755e742ad6SRussell King MODULE_LICENSE("GPL");
276