···11+#22+# Makefile for multifunction miscellaneous devices33+#44+55+obj-$(CONFIG_MCP) += mcp-core.o66+obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
+255
drivers/mfd/mcp-core.c
···11+/*22+ * linux/drivers/mfd/mcp-core.c33+ *44+ * Copyright (C) 2001 Russell King55+ *66+ * This program is free software; you can redistribute it and/or modify77+ * it under the terms of the GNU General Public License as published by88+ * the Free Software Foundation; either version 2 of the License.99+ *1010+ * Generic MCP (Multimedia Communications Port) layer. All MCP locking1111+ * is solely held within this file.1212+ */1313+#include <linux/module.h>1414+#include <linux/init.h>1515+#include <linux/errno.h>1616+#include <linux/smp.h>1717+#include <linux/device.h>1818+1919+#include <asm/dma.h>2020+#include <asm/system.h>2121+2222+#include "mcp.h"2323+2424+#define to_mcp(d) container_of(d, struct mcp, attached_device)2525+#define to_mcp_driver(d) container_of(d, struct mcp_driver, drv)2626+2727+static int mcp_bus_match(struct device *dev, struct device_driver *drv)2828+{2929+ return 1;3030+}3131+3232+static int mcp_bus_probe(struct device *dev)3333+{3434+ struct mcp *mcp = to_mcp(dev);3535+ struct mcp_driver *drv = to_mcp_driver(dev->driver);3636+3737+ return drv->probe(mcp);3838+}3939+4040+static int mcp_bus_remove(struct device *dev)4141+{4242+ struct mcp *mcp = to_mcp(dev);4343+ struct mcp_driver *drv = to_mcp_driver(dev->driver);4444+4545+ drv->remove(mcp);4646+ return 0;4747+}4848+4949+static int mcp_bus_suspend(struct device *dev, pm_message_t state)5050+{5151+ struct mcp *mcp = to_mcp(dev);5252+ int ret = 0;5353+5454+ if (dev->driver) {5555+ struct mcp_driver *drv = to_mcp_driver(dev->driver);5656+5757+ ret = drv->suspend(mcp, state);5858+ }5959+ return ret;6060+}6161+6262+static int mcp_bus_resume(struct device *dev)6363+{6464+ struct mcp *mcp = to_mcp(dev);6565+ int ret = 0;6666+6767+ if (dev->driver) {6868+ struct mcp_driver *drv = to_mcp_driver(dev->driver);6969+7070+ ret = drv->resume(mcp);7171+ }7272+ return ret;7373+}7474+7575+static struct bus_type mcp_bus_type = {7676+ .name = "mcp",7777+ .match = mcp_bus_match,7878+ .suspend = mcp_bus_suspend,7979+ .resume = mcp_bus_resume,8080+};8181+8282+/**8383+ * mcp_set_telecom_divisor - set the telecom divisor8484+ * @mcp: MCP interface structure8585+ * @div: SIB clock divisor8686+ *8787+ * Set the telecom divisor on the MCP interface. The resulting8888+ * sample rate is SIBCLOCK/div.8989+ */9090+void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div)9191+{9292+ spin_lock_irq(&mcp->lock);9393+ mcp->ops->set_telecom_divisor(mcp, div);9494+ spin_unlock_irq(&mcp->lock);9595+}9696+EXPORT_SYMBOL(mcp_set_telecom_divisor);9797+9898+/**9999+ * mcp_set_audio_divisor - set the audio divisor100100+ * @mcp: MCP interface structure101101+ * @div: SIB clock divisor102102+ *103103+ * Set the audio divisor on the MCP interface.104104+ */105105+void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div)106106+{107107+ spin_lock_irq(&mcp->lock);108108+ mcp->ops->set_audio_divisor(mcp, div);109109+ spin_unlock_irq(&mcp->lock);110110+}111111+EXPORT_SYMBOL(mcp_set_audio_divisor);112112+113113+/**114114+ * mcp_reg_write - write a device register115115+ * @mcp: MCP interface structure116116+ * @reg: 4-bit register index117117+ * @val: 16-bit data value118118+ *119119+ * Write a device register. The MCP interface must be enabled120120+ * to prevent this function hanging.121121+ */122122+void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)123123+{124124+ unsigned long flags;125125+126126+ spin_lock_irqsave(&mcp->lock, flags);127127+ mcp->ops->reg_write(mcp, reg, val);128128+ spin_unlock_irqrestore(&mcp->lock, flags);129129+}130130+EXPORT_SYMBOL(mcp_reg_write);131131+132132+/**133133+ * mcp_reg_read - read a device register134134+ * @mcp: MCP interface structure135135+ * @reg: 4-bit register index136136+ *137137+ * Read a device register and return its value. The MCP interface138138+ * must be enabled to prevent this function hanging.139139+ */140140+unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)141141+{142142+ unsigned long flags;143143+ unsigned int val;144144+145145+ spin_lock_irqsave(&mcp->lock, flags);146146+ val = mcp->ops->reg_read(mcp, reg);147147+ spin_unlock_irqrestore(&mcp->lock, flags);148148+149149+ return val;150150+}151151+EXPORT_SYMBOL(mcp_reg_read);152152+153153+/**154154+ * mcp_enable - enable the MCP interface155155+ * @mcp: MCP interface to enable156156+ *157157+ * Enable the MCP interface. Each call to mcp_enable will need158158+ * a corresponding call to mcp_disable to disable the interface.159159+ */160160+void mcp_enable(struct mcp *mcp)161161+{162162+ spin_lock_irq(&mcp->lock);163163+ if (mcp->use_count++ == 0)164164+ mcp->ops->enable(mcp);165165+ spin_unlock_irq(&mcp->lock);166166+}167167+EXPORT_SYMBOL(mcp_enable);168168+169169+/**170170+ * mcp_disable - disable the MCP interface171171+ * @mcp: MCP interface to disable172172+ *173173+ * Disable the MCP interface. The MCP interface will only be174174+ * disabled once the number of calls to mcp_enable matches the175175+ * number of calls to mcp_disable.176176+ */177177+void mcp_disable(struct mcp *mcp)178178+{179179+ unsigned long flags;180180+181181+ spin_lock_irqsave(&mcp->lock, flags);182182+ if (--mcp->use_count == 0)183183+ mcp->ops->disable(mcp);184184+ spin_unlock_irqrestore(&mcp->lock, flags);185185+}186186+EXPORT_SYMBOL(mcp_disable);187187+188188+static void mcp_release(struct device *dev)189189+{190190+ struct mcp *mcp = container_of(dev, struct mcp, attached_device);191191+192192+ kfree(mcp);193193+}194194+195195+struct mcp *mcp_host_alloc(struct device *parent, size_t size)196196+{197197+ struct mcp *mcp;198198+199199+ mcp = kmalloc(sizeof(struct mcp) + size, GFP_KERNEL);200200+ if (mcp) {201201+ memset(mcp, 0, sizeof(struct mcp) + size);202202+ spin_lock_init(&mcp->lock);203203+ mcp->attached_device.parent = parent;204204+ mcp->attached_device.bus = &mcp_bus_type;205205+ mcp->attached_device.dma_mask = parent->dma_mask;206206+ mcp->attached_device.release = mcp_release;207207+ }208208+ return mcp;209209+}210210+EXPORT_SYMBOL(mcp_host_alloc);211211+212212+int mcp_host_register(struct mcp *mcp)213213+{214214+ strcpy(mcp->attached_device.bus_id, "mcp0");215215+ return device_register(&mcp->attached_device);216216+}217217+EXPORT_SYMBOL(mcp_host_register);218218+219219+void mcp_host_unregister(struct mcp *mcp)220220+{221221+ device_unregister(&mcp->attached_device);222222+}223223+EXPORT_SYMBOL(mcp_host_unregister);224224+225225+int mcp_driver_register(struct mcp_driver *mcpdrv)226226+{227227+ mcpdrv->drv.bus = &mcp_bus_type;228228+ mcpdrv->drv.probe = mcp_bus_probe;229229+ mcpdrv->drv.remove = mcp_bus_remove;230230+ return driver_register(&mcpdrv->drv);231231+}232232+EXPORT_SYMBOL(mcp_driver_register);233233+234234+void mcp_driver_unregister(struct mcp_driver *mcpdrv)235235+{236236+ driver_unregister(&mcpdrv->drv);237237+}238238+EXPORT_SYMBOL(mcp_driver_unregister);239239+240240+static int __init mcp_init(void)241241+{242242+ return bus_register(&mcp_bus_type);243243+}244244+245245+static void __exit mcp_exit(void)246246+{247247+ bus_unregister(&mcp_bus_type);248248+}249249+250250+module_init(mcp_init);251251+module_exit(mcp_exit);252252+253253+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");254254+MODULE_DESCRIPTION("Core multimedia communications port driver");255255+MODULE_LICENSE("GPL");
+275
drivers/mfd/mcp-sa11x0.c
···11+/*22+ * linux/drivers/mfd/mcp-sa11x0.c33+ *44+ * Copyright (C) 2001-2005 Russell King55+ *66+ * This program is free software; you can redistribute it and/or modify77+ * it under the terms of the GNU General Public License as published by88+ * the Free Software Foundation; either version 2 of the License.99+ *1010+ * SA11x0 MCP (Multimedia Communications Port) driver.1111+ *1212+ * MCP read/write timeouts from Jordi Colomer, rehacked by rmk.1313+ */1414+#include <linux/module.h>1515+#include <linux/init.h>1616+#include <linux/errno.h>1717+#include <linux/kernel.h>1818+#include <linux/delay.h>1919+#include <linux/spinlock.h>2020+#include <linux/slab.h>2121+#include <linux/device.h>2222+2323+#include <asm/dma.h>2424+#include <asm/hardware.h>2525+#include <asm/mach-types.h>2626+#include <asm/system.h>2727+#include <asm/arch/mcp.h>2828+2929+#include <asm/arch/assabet.h>3030+3131+#include "mcp.h"3232+3333+struct mcp_sa11x0 {3434+ u32 mccr0;3535+ u32 mccr1;3636+};3737+3838+#define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp))3939+4040+static void4141+mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)4242+{4343+ unsigned int mccr0;4444+4545+ divisor /= 32;4646+4747+ mccr0 = Ser4MCCR0 & ~0x00007f00;4848+ mccr0 |= divisor << 8;4949+ Ser4MCCR0 = mccr0;5050+}5151+5252+static void5353+mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)5454+{5555+ unsigned int mccr0;5656+5757+ divisor /= 32;5858+5959+ mccr0 = Ser4MCCR0 & ~0x0000007f;6060+ mccr0 |= divisor;6161+ Ser4MCCR0 = mccr0;6262+}6363+6464+/*6565+ * Write data to the device. The bit should be set after 3 subframe6666+ * times (each frame is 64 clocks). We wait a maximum of 6 subframes.6767+ * We really should try doing something more productive while we6868+ * wait.6969+ */7070+static void7171+mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)7272+{7373+ int ret = -ETIME;7474+ int i;7575+7676+ Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);7777+7878+ for (i = 0; i < 2; i++) {7979+ udelay(mcp->rw_timeout);8080+ if (Ser4MCSR & MCSR_CWC) {8181+ ret = 0;8282+ break;8383+ }8484+ }8585+8686+ if (ret < 0)8787+ printk(KERN_WARNING "mcp: write timed out\n");8888+}8989+9090+/*9191+ * Read data from the device. The bit should be set after 3 subframe9292+ * times (each frame is 64 clocks). We wait a maximum of 6 subframes.9393+ * We really should try doing something more productive while we9494+ * wait.9595+ */9696+static unsigned int9797+mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)9898+{9999+ int ret = -ETIME;100100+ int i;101101+102102+ Ser4MCDR2 = reg << 17 | MCDR2_Rd;103103+104104+ for (i = 0; i < 2; i++) {105105+ udelay(mcp->rw_timeout);106106+ if (Ser4MCSR & MCSR_CRC) {107107+ ret = Ser4MCDR2 & 0xffff;108108+ break;109109+ }110110+ }111111+112112+ if (ret < 0)113113+ printk(KERN_WARNING "mcp: read timed out\n");114114+115115+ return ret;116116+}117117+118118+static void mcp_sa11x0_enable(struct mcp *mcp)119119+{120120+ Ser4MCSR = -1;121121+ Ser4MCCR0 |= MCCR0_MCE;122122+}123123+124124+static void mcp_sa11x0_disable(struct mcp *mcp)125125+{126126+ Ser4MCCR0 &= ~MCCR0_MCE;127127+}128128+129129+/*130130+ * Our methods.131131+ */132132+static struct mcp_ops mcp_sa11x0 = {133133+ .set_telecom_divisor = mcp_sa11x0_set_telecom_divisor,134134+ .set_audio_divisor = mcp_sa11x0_set_audio_divisor,135135+ .reg_write = mcp_sa11x0_write,136136+ .reg_read = mcp_sa11x0_read,137137+ .enable = mcp_sa11x0_enable,138138+ .disable = mcp_sa11x0_disable,139139+};140140+141141+static int mcp_sa11x0_probe(struct device *dev)142142+{143143+ struct platform_device *pdev = to_platform_device(dev);144144+ struct mcp_plat_data *data = pdev->dev.platform_data;145145+ struct mcp *mcp;146146+ int ret;147147+148148+ if (!data)149149+ return -ENODEV;150150+151151+ if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))152152+ return -EBUSY;153153+154154+ mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0));155155+ if (!mcp) {156156+ ret = -ENOMEM;157157+ goto release;158158+ }159159+160160+ mcp->owner = THIS_MODULE;161161+ mcp->ops = &mcp_sa11x0;162162+ mcp->sclk_rate = data->sclk_rate;163163+ mcp->dma_audio_rd = DMA_Ser4MCP0Rd;164164+ mcp->dma_audio_wr = DMA_Ser4MCP0Wr;165165+ mcp->dma_telco_rd = DMA_Ser4MCP1Rd;166166+ mcp->dma_telco_wr = DMA_Ser4MCP1Wr;167167+168168+ dev_set_drvdata(dev, mcp);169169+170170+ if (machine_is_assabet()) {171171+ ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);172172+ }173173+174174+ /*175175+ * Setup the PPC unit correctly.176176+ */177177+ PPDR &= ~PPC_RXD4;178178+ PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;179179+ PSDR |= PPC_RXD4;180180+ PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);181181+ PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);182182+183183+ /*184184+ * Initialise device. Note that we initially185185+ * set the sampling rate to minimum.186186+ */187187+ Ser4MCSR = -1;188188+ Ser4MCCR1 = data->mccr1;189189+ Ser4MCCR0 = data->mccr0 | 0x7f7f;190190+191191+ /*192192+ * Calculate the read/write timeout (us) from the bit clock193193+ * rate. This is the period for 3 64-bit frames. Always194194+ * round this time up.195195+ */196196+ mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /197197+ mcp->sclk_rate;198198+199199+ ret = mcp_host_register(mcp);200200+ if (ret == 0)201201+ goto out;202202+203203+ release:204204+ release_mem_region(0x80060000, 0x60);205205+ dev_set_drvdata(dev, NULL);206206+207207+ out:208208+ return ret;209209+}210210+211211+static int mcp_sa11x0_remove(struct device *dev)212212+{213213+ struct mcp *mcp = dev_get_drvdata(dev);214214+215215+ dev_set_drvdata(dev, NULL);216216+ mcp_host_unregister(mcp);217217+ release_mem_region(0x80060000, 0x60);218218+219219+ return 0;220220+}221221+222222+static int mcp_sa11x0_suspend(struct device *dev, pm_message_t state, u32 level)223223+{224224+ struct mcp *mcp = dev_get_drvdata(dev);225225+226226+ if (level == SUSPEND_DISABLE) {227227+ priv(mcp)->mccr0 = Ser4MCCR0;228228+ priv(mcp)->mccr1 = Ser4MCCR1;229229+ Ser4MCCR0 &= ~MCCR0_MCE;230230+ }231231+ return 0;232232+}233233+234234+static int mcp_sa11x0_resume(struct device *dev, u32 level)235235+{236236+ struct mcp *mcp = dev_get_drvdata(dev);237237+238238+ if (level == RESUME_RESTORE_STATE) {239239+ Ser4MCCR1 = priv(mcp)->mccr1;240240+ Ser4MCCR0 = priv(mcp)->mccr0;241241+ }242242+ return 0;243243+}244244+245245+/*246246+ * The driver for the SA11x0 MCP port.247247+ */248248+static struct device_driver mcp_sa11x0_driver = {249249+ .name = "sa11x0-mcp",250250+ .bus = &platform_bus_type,251251+ .probe = mcp_sa11x0_probe,252252+ .remove = mcp_sa11x0_remove,253253+ .suspend = mcp_sa11x0_suspend,254254+ .resume = mcp_sa11x0_resume,255255+};256256+257257+/*258258+ * This needs re-working259259+ */260260+static int __init mcp_sa11x0_init(void)261261+{262262+ return driver_register(&mcp_sa11x0_driver);263263+}264264+265265+static void __exit mcp_sa11x0_exit(void)266266+{267267+ driver_unregister(&mcp_sa11x0_driver);268268+}269269+270270+module_init(mcp_sa11x0_init);271271+module_exit(mcp_sa11x0_exit);272272+273273+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");274274+MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");275275+MODULE_LICENSE("GPL");
+66
drivers/mfd/mcp.h
···11+/*22+ * linux/drivers/mfd/mcp.h33+ *44+ * Copyright (C) 2001 Russell King, All Rights Reserved.55+ *66+ * This program is free software; you can redistribute it and/or modify77+ * it under the terms of the GNU General Public License as published by88+ * the Free Software Foundation; either version 2 of the License.99+ */1010+#ifndef MCP_H1111+#define MCP_H1212+1313+struct mcp_ops;1414+1515+struct mcp {1616+ struct module *owner;1717+ struct mcp_ops *ops;1818+ spinlock_t lock;1919+ int use_count;2020+ unsigned int sclk_rate;2121+ unsigned int rw_timeout;2222+ dma_device_t dma_audio_rd;2323+ dma_device_t dma_audio_wr;2424+ dma_device_t dma_telco_rd;2525+ dma_device_t dma_telco_wr;2626+ struct device attached_device;2727+};2828+2929+struct mcp_ops {3030+ void (*set_telecom_divisor)(struct mcp *, unsigned int);3131+ void (*set_audio_divisor)(struct mcp *, unsigned int);3232+ void (*reg_write)(struct mcp *, unsigned int, unsigned int);3333+ unsigned int (*reg_read)(struct mcp *, unsigned int);3434+ void (*enable)(struct mcp *);3535+ void (*disable)(struct mcp *);3636+};3737+3838+void mcp_set_telecom_divisor(struct mcp *, unsigned int);3939+void mcp_set_audio_divisor(struct mcp *, unsigned int);4040+void mcp_reg_write(struct mcp *, unsigned int, unsigned int);4141+unsigned int mcp_reg_read(struct mcp *, unsigned int);4242+void mcp_enable(struct mcp *);4343+void mcp_disable(struct mcp *);4444+#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate)4545+4646+struct mcp *mcp_host_alloc(struct device *, size_t);4747+int mcp_host_register(struct mcp *);4848+void mcp_host_unregister(struct mcp *);4949+5050+struct mcp_driver {5151+ struct device_driver drv;5252+ int (*probe)(struct mcp *);5353+ void (*remove)(struct mcp *);5454+ int (*suspend)(struct mcp *, pm_message_t);5555+ int (*resume)(struct mcp *);5656+};5757+5858+int mcp_driver_register(struct mcp_driver *);5959+void mcp_driver_unregister(struct mcp_driver *);6060+6161+#define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device)6262+#define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d)6363+6464+#define mcp_priv(mcp) ((void *)((mcp)+1))6565+6666+#endif
+21
include/asm-arm/arch-sa1100/mcp.h
···11+/*22+ * linux/include/asm-arm/arch-sa1100/mcp.h33+ *44+ * Copyright (C) 2005 Russell King.55+ *66+ * This program is free software; you can redistribute it and/or modify77+ * it under the terms of the GNU General Public License version 2 as88+ * published by the Free Software Foundation.99+ */1010+#ifndef __ASM_ARM_ARCH_MCP_H1111+#define __ASM_ARM_ARCH_MCP_H1212+1313+#include <linux/types.h>1414+1515+struct mcp_plat_data {1616+ u32 mccr0;1717+ u32 mccr1;1818+ unsigned int sclk_rate;1919+};2020+2121+#endif