*
* Revision history:
* 22nd Aug 2003 Initial version.
- *
+ * 20th Dec 2004 Added ssp_config for changing port config without
+ * closing the port.
+ * 4th Aug 2005 Added option to disable irq handler registration and
+ * cleaned up irq and clock detection.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/init.h>
+#include <linux/mutex.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/arch/ssp.h>
#include <asm/arch/pxa-regs.h>
+#define PXA_SSP_PORTS 3
+
+struct ssp_info_ {
+ int irq;
+ u32 clock;
+};
+
+/*
+ * SSP port clock and IRQ settings
+ */
+static const struct ssp_info_ ssp_info[PXA_SSP_PORTS] = {
+#if defined (CONFIG_PXA27x)
+ {IRQ_SSP, CKEN23_SSP1},
+ {IRQ_SSP2, CKEN3_SSP2},
+ {IRQ_SSP3, CKEN4_SSP3},
+#else
+ {IRQ_SSP, CKEN3_SSP},
+ {IRQ_NSSP, CKEN9_NSSP},
+ {IRQ_ASSP, CKEN10_ASSP},
+#endif
+};
+
+static DEFINE_MUTEX(mutex);
+static int use_count[PXA_SSP_PORTS] = {0, 0, 0};
+
static irqreturn_t ssp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct ssp_dev *dev = (struct ssp_dev*) dev_id;
SSCR0_P(dev->port) = ssp->cr0;
}
+/**
+ * ssp_config - configure SSP port settings
+ * @mode: port operating mode
+ * @flags: port config flags
+ * @psp_flags: port PSP config flags
+ * @speed: port speed
+ *
+ * Port MUST be disabled by ssp_disable before making any config changes.
+ */
+int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32 psp_flags, u32 speed)
+{
+ dev->mode = mode;
+ dev->flags = flags;
+ dev->psp_flags = psp_flags;
+ dev->speed = speed;
+
+ /* set up port type, speed, port settings */
+ SSCR0_P(dev->port) = (dev->speed | dev->mode);
+ SSCR1_P(dev->port) = dev->flags;
+ SSPSP_P(dev->port) = dev->psp_flags;
+
+ return 0;
+}
+
/**
* ssp_init - setup the SSP port
*
* %-EBUSY if the resources are already in use
* %0 on success
*/
-int ssp_init(struct ssp_dev *dev, u32 port, u32 mode, u32 flags, u32 psp_flags,
- u32 speed)
+int ssp_init(struct ssp_dev *dev, u32 port, u32 init_flags)
{
- int ret, irq;
+ int ret;
- if (!request_mem_region(__PREG(SSCR0_P(port)), 0x2c, "SSP")) {
+ if (port > PXA_SSP_PORTS || port == 0)
+ return -ENODEV;
+
+ mutex_lock(&mutex);
+ if (use_count[port - 1]) {
+ mutex_unlock(&mutex);
return -EBUSY;
}
+ use_count[port - 1]++;
- switch (port) {
- case 1:
- irq = IRQ_SSP;
- break;
-#if defined (CONFIG_PXA27x)
- case 2:
- irq = IRQ_SSP2;
- break;
- case 3:
- irq = IRQ_SSP3;
- break;
-#else
- case 2:
- irq = IRQ_NSSP;
- break;
- case 3:
- irq = IRQ_ASSP;
- break;
-#endif
- default:
- return -ENODEV;
+ if (!request_mem_region(__PREG(SSCR0_P(port)), 0x2c, "SSP")) {
+ use_count[port - 1]--;
+ mutex_unlock(&mutex);
+ return -EBUSY;
}
-
dev->port = port;
- dev->mode = mode;
- dev->flags = flags;
- dev->psp_flags = psp_flags;
- dev->speed = speed;
- /* set up port type, speed, port settings */
- SSCR0_P(dev->port) = (dev->speed | dev->mode);
- SSCR1_P(dev->port) = dev->flags;
- SSPSP_P(dev->port) = dev->psp_flags;
-
- ret = request_irq(irq, ssp_interrupt, 0, "SSP", dev);
- if (ret)
- goto out_region;
+ /* do we need to get irq */
+ if (!(init_flags & SSP_NO_IRQ)) {
+ ret = request_irq(ssp_info[port-1].irq, ssp_interrupt,
+ 0, "SSP", dev);
+ if (ret)
+ goto out_region;
+ dev->irq = ssp_info[port-1].irq;
+ } else
+ dev->irq = 0;
/* turn on SSP port clock */
- switch (dev->port) {
-#if defined (CONFIG_PXA27x)
- case 1:
- pxa_set_cken(CKEN23_SSP1, 1);
- break;
- case 2:
- pxa_set_cken(CKEN3_SSP2, 1);
- break;
- case 3:
- pxa_set_cken(CKEN4_SSP3, 1);
- break;
-#else
- case 1:
- pxa_set_cken(CKEN3_SSP, 1);
- break;
- case 2:
- pxa_set_cken(CKEN9_NSSP, 1);
- break;
- case 3:
- pxa_set_cken(CKEN10_ASSP, 1);
- break;
-#endif
- }
-
+ pxa_set_cken(ssp_info[port-1].clock, 1);
+ mutex_unlock(&mutex);
return 0;
out_region:
- release_mem_region(__PREG(SSCR0_P(dev->port)), 0x2c);
+ release_mem_region(__PREG(SSCR0_P(port)), 0x2c);
+ use_count[port - 1]--;
+ mutex_unlock(&mutex);
return ret;
}
*/
void ssp_exit(struct ssp_dev *dev)
{
- int irq;
-
+ mutex_lock(&mutex);
SSCR0_P(dev->port) &= ~SSCR0_SSE;
- /* find irq, save power and turn off SSP port clock */
- switch (dev->port) {
-#if defined (CONFIG_PXA27x)
- case 1:
- irq = IRQ_SSP;
- pxa_set_cken(CKEN23_SSP1, 0);
- break;
- case 2:
- irq = IRQ_SSP2;
- pxa_set_cken(CKEN3_SSP2, 0);
- break;
- case 3:
- irq = IRQ_SSP3;
- pxa_set_cken(CKEN4_SSP3, 0);
- break;
-#else
- case 1:
- irq = IRQ_SSP;
- pxa_set_cken(CKEN3_SSP, 0);
- break;
- case 2:
- irq = IRQ_NSSP;
- pxa_set_cken(CKEN9_NSSP, 0);
- break;
- case 3:
- irq = IRQ_ASSP;
- pxa_set_cken(CKEN10_ASSP, 0);
- break;
-#endif
- default:
- printk(KERN_WARNING "SSP: tried to close invalid port\n");
- return;
+ if (dev->port > PXA_SSP_PORTS || dev->port == 0) {
+ printk(KERN_WARNING "SSP: tried to close invalid port\n");
+ return;
}
- free_irq(irq, dev);
+ pxa_set_cken(ssp_info[dev->port-1].clock, 0);
+ if (dev->irq)
+ free_irq(dev->irq, dev);
release_mem_region(__PREG(SSCR0_P(dev->port)), 0x2c);
+ use_count[dev->port - 1]--;
+ mutex_unlock(&mutex);
}
EXPORT_SYMBOL(ssp_write_word);
EXPORT_SYMBOL(ssp_restore_state);
EXPORT_SYMBOL(ssp_init);
EXPORT_SYMBOL(ssp_exit);
+EXPORT_SYMBOL(ssp_config);
+
+MODULE_DESCRIPTION("PXA SSP driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
+