137931f02SIlya Yanok /*
237931f02SIlya Yanok * Texas Instruments DSPS platforms "glue layer"
337931f02SIlya Yanok *
437931f02SIlya Yanok * Copyright (C) 2012, by Texas Instruments
537931f02SIlya Yanok *
637931f02SIlya Yanok * Based on the am35x "glue layer" code.
737931f02SIlya Yanok *
837931f02SIlya Yanok * This file is part of the Inventra Controller Driver for Linux.
937931f02SIlya Yanok *
105b8031ccSTom Rini * SPDX-License-Identifier: GPL-2.0
1137931f02SIlya Yanok *
1237931f02SIlya Yanok * musb_dsps.c will be a common file for all the TI DSPS platforms
1337931f02SIlya Yanok * such as dm64x, dm36x, dm35x, da8x, am35x and ti81x.
1437931f02SIlya Yanok * For now only ti81x is using this and in future davinci.c, am35x.c
1537931f02SIlya Yanok * da8xx.c would be merged to this file after testing.
1637931f02SIlya Yanok */
1737931f02SIlya Yanok
1837931f02SIlya Yanok #ifndef __UBOOT__
1937931f02SIlya Yanok #include <linux/init.h>
2037931f02SIlya Yanok #include <linux/io.h>
2137931f02SIlya Yanok #include <linux/err.h>
2237931f02SIlya Yanok #include <linux/platform_device.h>
2337931f02SIlya Yanok #include <linux/dma-mapping.h>
2437931f02SIlya Yanok #include <linux/pm_runtime.h>
2537931f02SIlya Yanok #include <linux/module.h>
2637931f02SIlya Yanok
2737931f02SIlya Yanok #include <linux/of.h>
2837931f02SIlya Yanok #include <linux/of_device.h>
2937931f02SIlya Yanok #include <linux/of_address.h>
3037931f02SIlya Yanok
3137931f02SIlya Yanok #include <plat/usb.h>
3237931f02SIlya Yanok #else
3337931f02SIlya Yanok #include <common.h>
3437931f02SIlya Yanok #include <asm/omap_musb.h>
3537931f02SIlya Yanok #include "linux-compat.h"
3637931f02SIlya Yanok #endif
3737931f02SIlya Yanok
3837931f02SIlya Yanok #include "musb_core.h"
3937931f02SIlya Yanok
4037931f02SIlya Yanok /**
4137931f02SIlya Yanok * avoid using musb_readx()/musb_writex() as glue layer should not be
4237931f02SIlya Yanok * dependent on musb core layer symbols.
4337931f02SIlya Yanok */
dsps_readb(const void __iomem * addr,unsigned offset)4437931f02SIlya Yanok static inline u8 dsps_readb(const void __iomem *addr, unsigned offset)
4537931f02SIlya Yanok { return __raw_readb(addr + offset); }
4637931f02SIlya Yanok
dsps_readl(const void __iomem * addr,unsigned offset)4737931f02SIlya Yanok static inline u32 dsps_readl(const void __iomem *addr, unsigned offset)
4837931f02SIlya Yanok { return __raw_readl(addr + offset); }
4937931f02SIlya Yanok
dsps_writeb(void __iomem * addr,unsigned offset,u8 data)5037931f02SIlya Yanok static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data)
5137931f02SIlya Yanok { __raw_writeb(data, addr + offset); }
5237931f02SIlya Yanok
dsps_writel(void __iomem * addr,unsigned offset,u32 data)5337931f02SIlya Yanok static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data)
5437931f02SIlya Yanok { __raw_writel(data, addr + offset); }
5537931f02SIlya Yanok
5637931f02SIlya Yanok /**
5737931f02SIlya Yanok * DSPS musb wrapper register offset.
5837931f02SIlya Yanok * FIXME: This should be expanded to have all the wrapper registers from TI DSPS
5937931f02SIlya Yanok * musb ips.
6037931f02SIlya Yanok */
6137931f02SIlya Yanok struct dsps_musb_wrapper {
6237931f02SIlya Yanok u16 revision;
6337931f02SIlya Yanok u16 control;
6437931f02SIlya Yanok u16 status;
6537931f02SIlya Yanok u16 eoi;
6637931f02SIlya Yanok u16 epintr_set;
6737931f02SIlya Yanok u16 epintr_clear;
6837931f02SIlya Yanok u16 epintr_status;
6937931f02SIlya Yanok u16 coreintr_set;
7037931f02SIlya Yanok u16 coreintr_clear;
7137931f02SIlya Yanok u16 coreintr_status;
7237931f02SIlya Yanok u16 phy_utmi;
7337931f02SIlya Yanok u16 mode;
7437931f02SIlya Yanok
7537931f02SIlya Yanok /* bit positions for control */
7637931f02SIlya Yanok unsigned reset:5;
7737931f02SIlya Yanok
7837931f02SIlya Yanok /* bit positions for interrupt */
7937931f02SIlya Yanok unsigned usb_shift:5;
8037931f02SIlya Yanok u32 usb_mask;
8137931f02SIlya Yanok u32 usb_bitmap;
8237931f02SIlya Yanok unsigned drvvbus:5;
8337931f02SIlya Yanok
8437931f02SIlya Yanok unsigned txep_shift:5;
8537931f02SIlya Yanok u32 txep_mask;
8637931f02SIlya Yanok u32 txep_bitmap;
8737931f02SIlya Yanok
8837931f02SIlya Yanok unsigned rxep_shift:5;
8937931f02SIlya Yanok u32 rxep_mask;
9037931f02SIlya Yanok u32 rxep_bitmap;
9137931f02SIlya Yanok
9237931f02SIlya Yanok /* bit positions for phy_utmi */
9337931f02SIlya Yanok unsigned otg_disable:5;
9437931f02SIlya Yanok
9537931f02SIlya Yanok /* bit positions for mode */
9637931f02SIlya Yanok unsigned iddig:5;
9737931f02SIlya Yanok /* miscellaneous stuff */
9837931f02SIlya Yanok u32 musb_core_offset;
9937931f02SIlya Yanok u8 poll_seconds;
10037931f02SIlya Yanok };
10137931f02SIlya Yanok
10237931f02SIlya Yanok static const struct dsps_musb_wrapper ti81xx_driver_data __devinitconst = {
10337931f02SIlya Yanok .revision = 0x00,
10437931f02SIlya Yanok .control = 0x14,
10537931f02SIlya Yanok .status = 0x18,
10637931f02SIlya Yanok .eoi = 0x24,
10737931f02SIlya Yanok .epintr_set = 0x38,
10837931f02SIlya Yanok .epintr_clear = 0x40,
10937931f02SIlya Yanok .epintr_status = 0x30,
11037931f02SIlya Yanok .coreintr_set = 0x3c,
11137931f02SIlya Yanok .coreintr_clear = 0x44,
11237931f02SIlya Yanok .coreintr_status = 0x34,
11337931f02SIlya Yanok .phy_utmi = 0xe0,
11437931f02SIlya Yanok .mode = 0xe8,
11537931f02SIlya Yanok .reset = 0,
11637931f02SIlya Yanok .otg_disable = 21,
11737931f02SIlya Yanok .iddig = 8,
11837931f02SIlya Yanok .usb_shift = 0,
11937931f02SIlya Yanok .usb_mask = 0x1ff,
12037931f02SIlya Yanok .usb_bitmap = (0x1ff << 0),
12137931f02SIlya Yanok .drvvbus = 8,
12237931f02SIlya Yanok .txep_shift = 0,
12337931f02SIlya Yanok .txep_mask = 0xffff,
12437931f02SIlya Yanok .txep_bitmap = (0xffff << 0),
12537931f02SIlya Yanok .rxep_shift = 16,
12637931f02SIlya Yanok .rxep_mask = 0xfffe,
12737931f02SIlya Yanok .rxep_bitmap = (0xfffe << 16),
12837931f02SIlya Yanok .musb_core_offset = 0x400,
12937931f02SIlya Yanok .poll_seconds = 2,
13037931f02SIlya Yanok };
13137931f02SIlya Yanok
13237931f02SIlya Yanok /**
13337931f02SIlya Yanok * DSPS glue structure.
13437931f02SIlya Yanok */
13537931f02SIlya Yanok struct dsps_glue {
13637931f02SIlya Yanok struct device *dev;
13737931f02SIlya Yanok struct platform_device *musb; /* child musb pdev */
13837931f02SIlya Yanok const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
13937931f02SIlya Yanok struct timer_list timer; /* otg_workaround timer */
14037931f02SIlya Yanok };
14137931f02SIlya Yanok
14237931f02SIlya Yanok /**
14337931f02SIlya Yanok * dsps_musb_enable - enable interrupts
14437931f02SIlya Yanok */
14515837236SHans de Goede #ifndef __UBOOT__
dsps_musb_enable(struct musb * musb)14637931f02SIlya Yanok static void dsps_musb_enable(struct musb *musb)
14715837236SHans de Goede #else
14815837236SHans de Goede static int dsps_musb_enable(struct musb *musb)
14915837236SHans de Goede #endif
15037931f02SIlya Yanok {
15137931f02SIlya Yanok #ifndef __UBOOT__
15237931f02SIlya Yanok struct device *dev = musb->controller;
15337931f02SIlya Yanok struct platform_device *pdev = to_platform_device(dev->parent);
15437931f02SIlya Yanok struct dsps_glue *glue = platform_get_drvdata(pdev);
15537931f02SIlya Yanok const struct dsps_musb_wrapper *wrp = glue->wrp;
15637931f02SIlya Yanok #else
15737931f02SIlya Yanok const struct dsps_musb_wrapper *wrp = &ti81xx_driver_data;
15837931f02SIlya Yanok #endif
15937931f02SIlya Yanok void __iomem *reg_base = musb->ctrl_base;
16037931f02SIlya Yanok u32 epmask, coremask;
16137931f02SIlya Yanok
16237931f02SIlya Yanok /* Workaround: setup IRQs through both register sets. */
16337931f02SIlya Yanok epmask = ((musb->epmask & wrp->txep_mask) << wrp->txep_shift) |
16437931f02SIlya Yanok ((musb->epmask & wrp->rxep_mask) << wrp->rxep_shift);
16537931f02SIlya Yanok coremask = (wrp->usb_bitmap & ~MUSB_INTR_SOF);
16637931f02SIlya Yanok
16737931f02SIlya Yanok dsps_writel(reg_base, wrp->epintr_set, epmask);
16837931f02SIlya Yanok dsps_writel(reg_base, wrp->coreintr_set, coremask);
16937931f02SIlya Yanok /* Force the DRVVBUS IRQ so we can start polling for ID change. */
17037931f02SIlya Yanok #ifndef __UBOOT__
17137931f02SIlya Yanok if (is_otg_enabled(musb))
17237931f02SIlya Yanok dsps_writel(reg_base, wrp->coreintr_set,
17337931f02SIlya Yanok (1 << wrp->drvvbus) << wrp->usb_shift);
17415837236SHans de Goede #else
17515837236SHans de Goede return 0;
17637931f02SIlya Yanok #endif
17737931f02SIlya Yanok }
17837931f02SIlya Yanok
17937931f02SIlya Yanok /**
18037931f02SIlya Yanok * dsps_musb_disable - disable HDRC and flush interrupts
18137931f02SIlya Yanok */
dsps_musb_disable(struct musb * musb)18237931f02SIlya Yanok static void dsps_musb_disable(struct musb *musb)
18337931f02SIlya Yanok {
18437931f02SIlya Yanok #ifndef __UBOOT__
18537931f02SIlya Yanok struct device *dev = musb->controller;
18637931f02SIlya Yanok struct platform_device *pdev = to_platform_device(dev->parent);
18737931f02SIlya Yanok struct dsps_glue *glue = platform_get_drvdata(pdev);
18837931f02SIlya Yanok const struct dsps_musb_wrapper *wrp = glue->wrp;
18937931f02SIlya Yanok void __iomem *reg_base = musb->ctrl_base;
19037931f02SIlya Yanok
19137931f02SIlya Yanok dsps_writel(reg_base, wrp->coreintr_clear, wrp->usb_bitmap);
19237931f02SIlya Yanok dsps_writel(reg_base, wrp->epintr_clear,
19337931f02SIlya Yanok wrp->txep_bitmap | wrp->rxep_bitmap);
19437931f02SIlya Yanok dsps_writeb(musb->mregs, MUSB_DEVCTL, 0);
19537931f02SIlya Yanok dsps_writel(reg_base, wrp->eoi, 0);
19637931f02SIlya Yanok #endif
19737931f02SIlya Yanok }
19837931f02SIlya Yanok
19937931f02SIlya Yanok #ifndef __UBOOT__
otg_timer(unsigned long _musb)20037931f02SIlya Yanok static void otg_timer(unsigned long _musb)
20137931f02SIlya Yanok {
20237931f02SIlya Yanok struct musb *musb = (void *)_musb;
20337931f02SIlya Yanok void __iomem *mregs = musb->mregs;
20437931f02SIlya Yanok struct device *dev = musb->controller;
20537931f02SIlya Yanok struct platform_device *pdev = to_platform_device(dev->parent);
20637931f02SIlya Yanok struct dsps_glue *glue = platform_get_drvdata(pdev);
20737931f02SIlya Yanok const struct dsps_musb_wrapper *wrp = glue->wrp;
20837931f02SIlya Yanok u8 devctl;
20937931f02SIlya Yanok unsigned long flags;
21037931f02SIlya Yanok
21137931f02SIlya Yanok /*
21237931f02SIlya Yanok * We poll because DSPS IP's won't expose several OTG-critical
21337931f02SIlya Yanok * status change events (from the transceiver) otherwise.
21437931f02SIlya Yanok */
21537931f02SIlya Yanok devctl = dsps_readb(mregs, MUSB_DEVCTL);
21637931f02SIlya Yanok dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
21737931f02SIlya Yanok otg_state_string(musb->xceiv->state));
21837931f02SIlya Yanok
21937931f02SIlya Yanok spin_lock_irqsave(&musb->lock, flags);
22037931f02SIlya Yanok switch (musb->xceiv->state) {
22137931f02SIlya Yanok case OTG_STATE_A_WAIT_BCON:
22237931f02SIlya Yanok devctl &= ~MUSB_DEVCTL_SESSION;
22337931f02SIlya Yanok dsps_writeb(musb->mregs, MUSB_DEVCTL, devctl);
22437931f02SIlya Yanok
22537931f02SIlya Yanok devctl = dsps_readb(musb->mregs, MUSB_DEVCTL);
22637931f02SIlya Yanok if (devctl & MUSB_DEVCTL_BDEVICE) {
22737931f02SIlya Yanok musb->xceiv->state = OTG_STATE_B_IDLE;
22837931f02SIlya Yanok MUSB_DEV_MODE(musb);
22937931f02SIlya Yanok } else {
23037931f02SIlya Yanok musb->xceiv->state = OTG_STATE_A_IDLE;
23137931f02SIlya Yanok MUSB_HST_MODE(musb);
23237931f02SIlya Yanok }
23337931f02SIlya Yanok break;
23437931f02SIlya Yanok case OTG_STATE_A_WAIT_VFALL:
23537931f02SIlya Yanok musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
23637931f02SIlya Yanok dsps_writel(musb->ctrl_base, wrp->coreintr_set,
23737931f02SIlya Yanok MUSB_INTR_VBUSERROR << wrp->usb_shift);
23837931f02SIlya Yanok break;
23937931f02SIlya Yanok case OTG_STATE_B_IDLE:
24037931f02SIlya Yanok if (!is_peripheral_enabled(musb))
24137931f02SIlya Yanok break;
24237931f02SIlya Yanok
24337931f02SIlya Yanok devctl = dsps_readb(mregs, MUSB_DEVCTL);
24437931f02SIlya Yanok if (devctl & MUSB_DEVCTL_BDEVICE)
24537931f02SIlya Yanok mod_timer(&glue->timer,
24637931f02SIlya Yanok jiffies + wrp->poll_seconds * HZ);
24737931f02SIlya Yanok else
24837931f02SIlya Yanok musb->xceiv->state = OTG_STATE_A_IDLE;
24937931f02SIlya Yanok break;
25037931f02SIlya Yanok default:
25137931f02SIlya Yanok break;
25237931f02SIlya Yanok }
25337931f02SIlya Yanok spin_unlock_irqrestore(&musb->lock, flags);
25437931f02SIlya Yanok }
25537931f02SIlya Yanok
dsps_musb_try_idle(struct musb * musb,unsigned long timeout)25637931f02SIlya Yanok static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
25737931f02SIlya Yanok {
25837931f02SIlya Yanok struct device *dev = musb->controller;
25937931f02SIlya Yanok struct platform_device *pdev = to_platform_device(dev->parent);
26037931f02SIlya Yanok struct dsps_glue *glue = platform_get_drvdata(pdev);
26137931f02SIlya Yanok static unsigned long last_timer;
26237931f02SIlya Yanok
26337931f02SIlya Yanok if (!is_otg_enabled(musb))
26437931f02SIlya Yanok return;
26537931f02SIlya Yanok
26637931f02SIlya Yanok if (timeout == 0)
26737931f02SIlya Yanok timeout = jiffies + msecs_to_jiffies(3);
26837931f02SIlya Yanok
26937931f02SIlya Yanok /* Never idle if active, or when VBUS timeout is not set as host */
27037931f02SIlya Yanok if (musb->is_active || (musb->a_wait_bcon == 0 &&
27137931f02SIlya Yanok musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
27237931f02SIlya Yanok dev_dbg(musb->controller, "%s active, deleting timer\n",
27337931f02SIlya Yanok otg_state_string(musb->xceiv->state));
27437931f02SIlya Yanok del_timer(&glue->timer);
27537931f02SIlya Yanok last_timer = jiffies;
27637931f02SIlya Yanok return;
27737931f02SIlya Yanok }
27837931f02SIlya Yanok
27937931f02SIlya Yanok if (time_after(last_timer, timeout) && timer_pending(&glue->timer)) {
28037931f02SIlya Yanok dev_dbg(musb->controller,
28137931f02SIlya Yanok "Longer idle timer already pending, ignoring...\n");
28237931f02SIlya Yanok return;
28337931f02SIlya Yanok }
28437931f02SIlya Yanok last_timer = timeout;
28537931f02SIlya Yanok
28637931f02SIlya Yanok dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
28737931f02SIlya Yanok otg_state_string(musb->xceiv->state),
28837931f02SIlya Yanok jiffies_to_msecs(timeout - jiffies));
28937931f02SIlya Yanok mod_timer(&glue->timer, timeout);
29037931f02SIlya Yanok }
29137931f02SIlya Yanok #endif
29237931f02SIlya Yanok
dsps_interrupt(int irq,void * hci)29337931f02SIlya Yanok static irqreturn_t dsps_interrupt(int irq, void *hci)
29437931f02SIlya Yanok {
29537931f02SIlya Yanok struct musb *musb = hci;
29637931f02SIlya Yanok void __iomem *reg_base = musb->ctrl_base;
29737931f02SIlya Yanok #ifndef __UBOOT__
29837931f02SIlya Yanok struct device *dev = musb->controller;
29937931f02SIlya Yanok struct platform_device *pdev = to_platform_device(dev->parent);
30037931f02SIlya Yanok struct dsps_glue *glue = platform_get_drvdata(pdev);
30137931f02SIlya Yanok const struct dsps_musb_wrapper *wrp = glue->wrp;
30237931f02SIlya Yanok #else
30337931f02SIlya Yanok const struct dsps_musb_wrapper *wrp = &ti81xx_driver_data;
30437931f02SIlya Yanok #endif
30537931f02SIlya Yanok unsigned long flags;
30637931f02SIlya Yanok irqreturn_t ret = IRQ_NONE;
30737931f02SIlya Yanok u32 epintr, usbintr;
30837931f02SIlya Yanok
30937931f02SIlya Yanok spin_lock_irqsave(&musb->lock, flags);
31037931f02SIlya Yanok
31137931f02SIlya Yanok /* Get endpoint interrupts */
31237931f02SIlya Yanok epintr = dsps_readl(reg_base, wrp->epintr_status);
31337931f02SIlya Yanok musb->int_rx = (epintr & wrp->rxep_bitmap) >> wrp->rxep_shift;
31437931f02SIlya Yanok musb->int_tx = (epintr & wrp->txep_bitmap) >> wrp->txep_shift;
31537931f02SIlya Yanok
31637931f02SIlya Yanok if (epintr)
31737931f02SIlya Yanok dsps_writel(reg_base, wrp->epintr_status, epintr);
31837931f02SIlya Yanok
31937931f02SIlya Yanok /* Get usb core interrupts */
32037931f02SIlya Yanok usbintr = dsps_readl(reg_base, wrp->coreintr_status);
32137931f02SIlya Yanok if (!usbintr && !epintr)
32237931f02SIlya Yanok goto eoi;
32337931f02SIlya Yanok
32437931f02SIlya Yanok musb->int_usb = (usbintr & wrp->usb_bitmap) >> wrp->usb_shift;
32537931f02SIlya Yanok if (usbintr)
32637931f02SIlya Yanok dsps_writel(reg_base, wrp->coreintr_status, usbintr);
32737931f02SIlya Yanok
32837931f02SIlya Yanok dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n",
32937931f02SIlya Yanok usbintr, epintr);
33037931f02SIlya Yanok #ifndef __UBOOT__
33137931f02SIlya Yanok /*
33237931f02SIlya Yanok * DRVVBUS IRQs are the only proxy we have (a very poor one!) for
33337931f02SIlya Yanok * DSPS IP's missing ID change IRQ. We need an ID change IRQ to
33437931f02SIlya Yanok * switch appropriately between halves of the OTG state machine.
33537931f02SIlya Yanok * Managing DEVCTL.SESSION per Mentor docs requires that we know its
33637931f02SIlya Yanok * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
33737931f02SIlya Yanok * Also, DRVVBUS pulses for SRP (but not at 5V) ...
33837931f02SIlya Yanok */
33937931f02SIlya Yanok if ((usbintr & MUSB_INTR_BABBLE) && is_host_enabled(musb))
34037931f02SIlya Yanok pr_info("CAUTION: musb: Babble Interrupt Occured\n");
34137931f02SIlya Yanok
34237931f02SIlya Yanok if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
34337931f02SIlya Yanok int drvvbus = dsps_readl(reg_base, wrp->status);
34437931f02SIlya Yanok void __iomem *mregs = musb->mregs;
34537931f02SIlya Yanok u8 devctl = dsps_readb(mregs, MUSB_DEVCTL);
34637931f02SIlya Yanok int err;
34737931f02SIlya Yanok
34837931f02SIlya Yanok err = is_host_enabled(musb) && (musb->int_usb &
34937931f02SIlya Yanok MUSB_INTR_VBUSERROR);
35037931f02SIlya Yanok if (err) {
35137931f02SIlya Yanok /*
35237931f02SIlya Yanok * The Mentor core doesn't debounce VBUS as needed
35337931f02SIlya Yanok * to cope with device connect current spikes. This
35437931f02SIlya Yanok * means it's not uncommon for bus-powered devices
35537931f02SIlya Yanok * to get VBUS errors during enumeration.
35637931f02SIlya Yanok *
35737931f02SIlya Yanok * This is a workaround, but newer RTL from Mentor
35837931f02SIlya Yanok * seems to allow a better one: "re"-starting sessions
35937931f02SIlya Yanok * without waiting for VBUS to stop registering in
36037931f02SIlya Yanok * devctl.
36137931f02SIlya Yanok */
36237931f02SIlya Yanok musb->int_usb &= ~MUSB_INTR_VBUSERROR;
36337931f02SIlya Yanok musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
36437931f02SIlya Yanok mod_timer(&glue->timer,
36537931f02SIlya Yanok jiffies + wrp->poll_seconds * HZ);
36637931f02SIlya Yanok WARNING("VBUS error workaround (delay coming)\n");
36737931f02SIlya Yanok } else if (is_host_enabled(musb) && drvvbus) {
36837931f02SIlya Yanok musb->is_active = 1;
36937931f02SIlya Yanok MUSB_HST_MODE(musb);
37037931f02SIlya Yanok musb->xceiv->otg->default_a = 1;
37137931f02SIlya Yanok musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
37237931f02SIlya Yanok del_timer(&glue->timer);
37337931f02SIlya Yanok } else {
37437931f02SIlya Yanok musb->is_active = 0;
37537931f02SIlya Yanok MUSB_DEV_MODE(musb);
37637931f02SIlya Yanok musb->xceiv->otg->default_a = 0;
37737931f02SIlya Yanok musb->xceiv->state = OTG_STATE_B_IDLE;
37837931f02SIlya Yanok }
37937931f02SIlya Yanok
38037931f02SIlya Yanok /* NOTE: this must complete power-on within 100 ms. */
38137931f02SIlya Yanok dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
38237931f02SIlya Yanok drvvbus ? "on" : "off",
38337931f02SIlya Yanok otg_state_string(musb->xceiv->state),
38437931f02SIlya Yanok err ? " ERROR" : "",
38537931f02SIlya Yanok devctl);
38637931f02SIlya Yanok ret = IRQ_HANDLED;
38737931f02SIlya Yanok }
38837931f02SIlya Yanok #endif
38937931f02SIlya Yanok
39037931f02SIlya Yanok if (musb->int_tx || musb->int_rx || musb->int_usb)
39137931f02SIlya Yanok ret |= musb_interrupt(musb);
39237931f02SIlya Yanok
39337931f02SIlya Yanok eoi:
39437931f02SIlya Yanok /* EOI needs to be written for the IRQ to be re-asserted. */
39537931f02SIlya Yanok if (ret == IRQ_HANDLED || epintr || usbintr)
39637931f02SIlya Yanok dsps_writel(reg_base, wrp->eoi, 1);
39737931f02SIlya Yanok
39837931f02SIlya Yanok #ifndef __UBOOT__
39937931f02SIlya Yanok /* Poll for ID change */
40037931f02SIlya Yanok if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE)
40137931f02SIlya Yanok mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
40237931f02SIlya Yanok #endif
40337931f02SIlya Yanok
40437931f02SIlya Yanok spin_unlock_irqrestore(&musb->lock, flags);
40537931f02SIlya Yanok
40637931f02SIlya Yanok return ret;
40737931f02SIlya Yanok }
40837931f02SIlya Yanok
dsps_musb_init(struct musb * musb)40937931f02SIlya Yanok static int dsps_musb_init(struct musb *musb)
41037931f02SIlya Yanok {
41137931f02SIlya Yanok #ifndef __UBOOT__
41237931f02SIlya Yanok struct device *dev = musb->controller;
41337931f02SIlya Yanok struct musb_hdrc_platform_data *plat = dev->platform_data;
41437931f02SIlya Yanok struct platform_device *pdev = to_platform_device(dev->parent);
41537931f02SIlya Yanok struct dsps_glue *glue = platform_get_drvdata(pdev);
41637931f02SIlya Yanok const struct dsps_musb_wrapper *wrp = glue->wrp;
41737931f02SIlya Yanok struct omap_musb_board_data *data = plat->board_data;
41837931f02SIlya Yanok #else
41937931f02SIlya Yanok struct omap_musb_board_data *data =
42037931f02SIlya Yanok (struct omap_musb_board_data *)musb->controller;
42137931f02SIlya Yanok const struct dsps_musb_wrapper *wrp = &ti81xx_driver_data;
42237931f02SIlya Yanok #endif
42337931f02SIlya Yanok void __iomem *reg_base = musb->ctrl_base;
42437931f02SIlya Yanok u32 rev, val;
42537931f02SIlya Yanok int status;
42637931f02SIlya Yanok
42737931f02SIlya Yanok /* mentor core register starts at offset of 0x400 from musb base */
42837931f02SIlya Yanok musb->mregs += wrp->musb_core_offset;
42937931f02SIlya Yanok
43037931f02SIlya Yanok #ifndef __UBOOT__
43137931f02SIlya Yanok /* NOP driver needs change if supporting dual instance */
43237931f02SIlya Yanok usb_nop_xceiv_register();
43337931f02SIlya Yanok musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
43437931f02SIlya Yanok if (IS_ERR_OR_NULL(musb->xceiv))
43537931f02SIlya Yanok return -ENODEV;
43637931f02SIlya Yanok #endif
43737931f02SIlya Yanok
43837931f02SIlya Yanok /* Returns zero if e.g. not clocked */
43937931f02SIlya Yanok rev = dsps_readl(reg_base, wrp->revision);
44037931f02SIlya Yanok if (!rev) {
44137931f02SIlya Yanok status = -ENODEV;
44237931f02SIlya Yanok goto err0;
44337931f02SIlya Yanok }
44437931f02SIlya Yanok
44537931f02SIlya Yanok #ifndef __UBOOT__
44637931f02SIlya Yanok if (is_host_enabled(musb))
44737931f02SIlya Yanok setup_timer(&glue->timer, otg_timer, (unsigned long) musb);
44837931f02SIlya Yanok #endif
44937931f02SIlya Yanok
45037931f02SIlya Yanok /* Reset the musb */
45137931f02SIlya Yanok dsps_writel(reg_base, wrp->control, (1 << wrp->reset));
45237931f02SIlya Yanok
45337931f02SIlya Yanok /* Start the on-chip PHY and its PLL. */
45437931f02SIlya Yanok if (data->set_phy_power)
455*1cac34ceSMugunthan V N data->set_phy_power(data->dev, 1);
45637931f02SIlya Yanok
45737931f02SIlya Yanok musb->isr = dsps_interrupt;
45837931f02SIlya Yanok
45937931f02SIlya Yanok /* reset the otgdisable bit, needed for host mode to work */
46037931f02SIlya Yanok val = dsps_readl(reg_base, wrp->phy_utmi);
46137931f02SIlya Yanok val &= ~(1 << wrp->otg_disable);
46237931f02SIlya Yanok dsps_writel(musb->ctrl_base, wrp->phy_utmi, val);
46337931f02SIlya Yanok
46437931f02SIlya Yanok /* clear level interrupt */
46537931f02SIlya Yanok dsps_writel(reg_base, wrp->eoi, 0);
46637931f02SIlya Yanok
46737931f02SIlya Yanok return 0;
46837931f02SIlya Yanok err0:
46937931f02SIlya Yanok #ifndef __UBOOT__
47037931f02SIlya Yanok usb_put_phy(musb->xceiv);
47137931f02SIlya Yanok usb_nop_xceiv_unregister();
47237931f02SIlya Yanok #endif
47337931f02SIlya Yanok return status;
47437931f02SIlya Yanok }
47537931f02SIlya Yanok
dsps_musb_exit(struct musb * musb)47637931f02SIlya Yanok static int dsps_musb_exit(struct musb *musb)
47737931f02SIlya Yanok {
47837931f02SIlya Yanok #ifndef __UBOOT__
47937931f02SIlya Yanok struct device *dev = musb->controller;
48037931f02SIlya Yanok struct musb_hdrc_platform_data *plat = dev->platform_data;
48137931f02SIlya Yanok struct omap_musb_board_data *data = plat->board_data;
48237931f02SIlya Yanok struct platform_device *pdev = to_platform_device(dev->parent);
48337931f02SIlya Yanok struct dsps_glue *glue = platform_get_drvdata(pdev);
48437931f02SIlya Yanok #else
48537931f02SIlya Yanok struct omap_musb_board_data *data =
48637931f02SIlya Yanok (struct omap_musb_board_data *)musb->controller;
48737931f02SIlya Yanok #endif
48837931f02SIlya Yanok
48937931f02SIlya Yanok #ifndef __UBOOT__
49037931f02SIlya Yanok if (is_host_enabled(musb))
49137931f02SIlya Yanok del_timer_sync(&glue->timer);
49237931f02SIlya Yanok #endif
49337931f02SIlya Yanok
49437931f02SIlya Yanok /* Shutdown the on-chip PHY and its PLL. */
49537931f02SIlya Yanok if (data->set_phy_power)
496*1cac34ceSMugunthan V N data->set_phy_power(data->dev, 0);
49737931f02SIlya Yanok
49837931f02SIlya Yanok #ifndef __UBOOT__
49937931f02SIlya Yanok /* NOP driver needs change if supporting dual instance */
50037931f02SIlya Yanok usb_put_phy(musb->xceiv);
50137931f02SIlya Yanok usb_nop_xceiv_unregister();
50237931f02SIlya Yanok #endif
50337931f02SIlya Yanok
50437931f02SIlya Yanok return 0;
50537931f02SIlya Yanok }
50637931f02SIlya Yanok
50737931f02SIlya Yanok #ifndef __UBOOT__
50837931f02SIlya Yanok static struct musb_platform_ops dsps_ops = {
50937931f02SIlya Yanok #else
51037931f02SIlya Yanok struct musb_platform_ops musb_dsps_ops = {
51137931f02SIlya Yanok #endif
51237931f02SIlya Yanok .init = dsps_musb_init,
51337931f02SIlya Yanok .exit = dsps_musb_exit,
51437931f02SIlya Yanok
51537931f02SIlya Yanok .enable = dsps_musb_enable,
51637931f02SIlya Yanok .disable = dsps_musb_disable,
51737931f02SIlya Yanok
51837931f02SIlya Yanok #ifndef __UBOOT__
51937931f02SIlya Yanok .try_idle = dsps_musb_try_idle,
52037931f02SIlya Yanok #endif
52137931f02SIlya Yanok };
52237931f02SIlya Yanok
52337931f02SIlya Yanok #ifndef __UBOOT__
52437931f02SIlya Yanok static u64 musb_dmamask = DMA_BIT_MASK(32);
52537931f02SIlya Yanok #endif
52637931f02SIlya Yanok
52737931f02SIlya Yanok #ifndef __UBOOT__
dsps_create_musb_pdev(struct dsps_glue * glue,u8 id)52837931f02SIlya Yanok static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id)
52937931f02SIlya Yanok {
53037931f02SIlya Yanok struct device *dev = glue->dev;
53137931f02SIlya Yanok struct platform_device *pdev = to_platform_device(dev);
53237931f02SIlya Yanok struct musb_hdrc_platform_data *pdata = dev->platform_data;
53337931f02SIlya Yanok struct platform_device *musb;
53437931f02SIlya Yanok struct resource *res;
53537931f02SIlya Yanok struct resource resources[2];
53637931f02SIlya Yanok char res_name[10];
53737931f02SIlya Yanok int ret;
53837931f02SIlya Yanok
53937931f02SIlya Yanok /* get memory resource */
54037931f02SIlya Yanok sprintf(res_name, "musb%d", id);
54137931f02SIlya Yanok res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
54237931f02SIlya Yanok if (!res) {
54337931f02SIlya Yanok dev_err(dev, "%s get mem resource failed\n", res_name);
54437931f02SIlya Yanok ret = -ENODEV;
54537931f02SIlya Yanok goto err0;
54637931f02SIlya Yanok }
54737931f02SIlya Yanok res->parent = NULL;
54837931f02SIlya Yanok resources[0] = *res;
54937931f02SIlya Yanok
55037931f02SIlya Yanok /* get irq resource */
55137931f02SIlya Yanok sprintf(res_name, "musb%d-irq", id);
55237931f02SIlya Yanok res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
55337931f02SIlya Yanok if (!res) {
55437931f02SIlya Yanok dev_err(dev, "%s get irq resource failed\n", res_name);
55537931f02SIlya Yanok ret = -ENODEV;
55637931f02SIlya Yanok goto err0;
55737931f02SIlya Yanok }
55837931f02SIlya Yanok res->parent = NULL;
55937931f02SIlya Yanok resources[1] = *res;
56037931f02SIlya Yanok resources[1].name = "mc";
56137931f02SIlya Yanok
56237931f02SIlya Yanok /* allocate the child platform device */
56337931f02SIlya Yanok musb = platform_device_alloc("musb-hdrc", -1);
56437931f02SIlya Yanok if (!musb) {
56537931f02SIlya Yanok dev_err(dev, "failed to allocate musb device\n");
56637931f02SIlya Yanok ret = -ENOMEM;
56737931f02SIlya Yanok goto err0;
56837931f02SIlya Yanok }
56937931f02SIlya Yanok
57037931f02SIlya Yanok musb->dev.parent = dev;
57137931f02SIlya Yanok musb->dev.dma_mask = &musb_dmamask;
57237931f02SIlya Yanok musb->dev.coherent_dma_mask = musb_dmamask;
57337931f02SIlya Yanok
57437931f02SIlya Yanok glue->musb = musb;
57537931f02SIlya Yanok
57637931f02SIlya Yanok pdata->platform_ops = &dsps_ops;
57737931f02SIlya Yanok
57837931f02SIlya Yanok ret = platform_device_add_resources(musb, resources, 2);
57937931f02SIlya Yanok if (ret) {
58037931f02SIlya Yanok dev_err(dev, "failed to add resources\n");
58137931f02SIlya Yanok goto err1;
58237931f02SIlya Yanok }
58337931f02SIlya Yanok
58437931f02SIlya Yanok ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
58537931f02SIlya Yanok if (ret) {
58637931f02SIlya Yanok dev_err(dev, "failed to add platform_data\n");
58737931f02SIlya Yanok goto err1;
58837931f02SIlya Yanok }
58937931f02SIlya Yanok
59037931f02SIlya Yanok ret = platform_device_add(musb);
59137931f02SIlya Yanok if (ret) {
59237931f02SIlya Yanok dev_err(dev, "failed to register musb device\n");
59337931f02SIlya Yanok goto err1;
59437931f02SIlya Yanok }
59537931f02SIlya Yanok
59637931f02SIlya Yanok return 0;
59737931f02SIlya Yanok
59837931f02SIlya Yanok err1:
59937931f02SIlya Yanok platform_device_put(musb);
60037931f02SIlya Yanok err0:
60137931f02SIlya Yanok return ret;
60237931f02SIlya Yanok }
60337931f02SIlya Yanok
dsps_delete_musb_pdev(struct dsps_glue * glue)60437931f02SIlya Yanok static void __devexit dsps_delete_musb_pdev(struct dsps_glue *glue)
60537931f02SIlya Yanok {
60637931f02SIlya Yanok platform_device_del(glue->musb);
60737931f02SIlya Yanok platform_device_put(glue->musb);
60837931f02SIlya Yanok }
60937931f02SIlya Yanok
dsps_probe(struct platform_device * pdev)61037931f02SIlya Yanok static int __devinit dsps_probe(struct platform_device *pdev)
61137931f02SIlya Yanok {
61237931f02SIlya Yanok const struct platform_device_id *id = platform_get_device_id(pdev);
61337931f02SIlya Yanok const struct dsps_musb_wrapper *wrp =
61437931f02SIlya Yanok (struct dsps_musb_wrapper *)id->driver_data;
61537931f02SIlya Yanok struct dsps_glue *glue;
61637931f02SIlya Yanok struct resource *iomem;
61737931f02SIlya Yanok int ret;
61837931f02SIlya Yanok
61937931f02SIlya Yanok /* allocate glue */
62037931f02SIlya Yanok glue = kzalloc(sizeof(*glue), GFP_KERNEL);
62137931f02SIlya Yanok if (!glue) {
62237931f02SIlya Yanok dev_err(&pdev->dev, "unable to allocate glue memory\n");
62337931f02SIlya Yanok ret = -ENOMEM;
62437931f02SIlya Yanok goto err0;
62537931f02SIlya Yanok }
62637931f02SIlya Yanok
62737931f02SIlya Yanok /* get memory resource */
62837931f02SIlya Yanok iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
62937931f02SIlya Yanok if (!iomem) {
63062a3b7ddSRobert P. J. Day dev_err(&pdev->dev, "failed to get usbss mem resource\n");
63137931f02SIlya Yanok ret = -ENODEV;
63237931f02SIlya Yanok goto err1;
63337931f02SIlya Yanok }
63437931f02SIlya Yanok
63537931f02SIlya Yanok glue->dev = &pdev->dev;
63637931f02SIlya Yanok
63737931f02SIlya Yanok glue->wrp = kmemdup(wrp, sizeof(*wrp), GFP_KERNEL);
63837931f02SIlya Yanok if (!glue->wrp) {
63937931f02SIlya Yanok dev_err(&pdev->dev, "failed to duplicate wrapper struct memory\n");
64037931f02SIlya Yanok ret = -ENOMEM;
64137931f02SIlya Yanok goto err1;
64237931f02SIlya Yanok }
64337931f02SIlya Yanok platform_set_drvdata(pdev, glue);
64437931f02SIlya Yanok
64537931f02SIlya Yanok /* enable the usbss clocks */
64637931f02SIlya Yanok pm_runtime_enable(&pdev->dev);
64737931f02SIlya Yanok
64837931f02SIlya Yanok ret = pm_runtime_get_sync(&pdev->dev);
64937931f02SIlya Yanok if (ret < 0) {
65037931f02SIlya Yanok dev_err(&pdev->dev, "pm_runtime_get_sync FAILED");
65137931f02SIlya Yanok goto err2;
65237931f02SIlya Yanok }
65337931f02SIlya Yanok
65437931f02SIlya Yanok /* create the child platform device for first instances of musb */
65537931f02SIlya Yanok ret = dsps_create_musb_pdev(glue, 0);
65637931f02SIlya Yanok if (ret != 0) {
65737931f02SIlya Yanok dev_err(&pdev->dev, "failed to create child pdev\n");
65837931f02SIlya Yanok goto err3;
65937931f02SIlya Yanok }
66037931f02SIlya Yanok
66137931f02SIlya Yanok return 0;
66237931f02SIlya Yanok
66337931f02SIlya Yanok err3:
66437931f02SIlya Yanok pm_runtime_put(&pdev->dev);
66537931f02SIlya Yanok err2:
66637931f02SIlya Yanok pm_runtime_disable(&pdev->dev);
66737931f02SIlya Yanok kfree(glue->wrp);
66837931f02SIlya Yanok err1:
66937931f02SIlya Yanok kfree(glue);
67037931f02SIlya Yanok err0:
67137931f02SIlya Yanok return ret;
67237931f02SIlya Yanok }
dsps_remove(struct platform_device * pdev)67337931f02SIlya Yanok static int __devexit dsps_remove(struct platform_device *pdev)
67437931f02SIlya Yanok {
67537931f02SIlya Yanok struct dsps_glue *glue = platform_get_drvdata(pdev);
67637931f02SIlya Yanok
67737931f02SIlya Yanok /* delete the child platform device */
67837931f02SIlya Yanok dsps_delete_musb_pdev(glue);
67937931f02SIlya Yanok
68037931f02SIlya Yanok /* disable usbss clocks */
68137931f02SIlya Yanok pm_runtime_put(&pdev->dev);
68237931f02SIlya Yanok pm_runtime_disable(&pdev->dev);
68337931f02SIlya Yanok kfree(glue->wrp);
68437931f02SIlya Yanok kfree(glue);
68537931f02SIlya Yanok return 0;
68637931f02SIlya Yanok }
68737931f02SIlya Yanok
68837931f02SIlya Yanok #ifdef CONFIG_PM_SLEEP
dsps_suspend(struct device * dev)68937931f02SIlya Yanok static int dsps_suspend(struct device *dev)
69037931f02SIlya Yanok {
69137931f02SIlya Yanok struct musb_hdrc_platform_data *plat = dev->platform_data;
69237931f02SIlya Yanok struct omap_musb_board_data *data = plat->board_data;
69337931f02SIlya Yanok
69437931f02SIlya Yanok /* Shutdown the on-chip PHY and its PLL. */
69537931f02SIlya Yanok if (data->set_phy_power)
696*1cac34ceSMugunthan V N data->set_phy_power(data->dev, 0);
69737931f02SIlya Yanok
69837931f02SIlya Yanok return 0;
69937931f02SIlya Yanok }
70037931f02SIlya Yanok
dsps_resume(struct device * dev)70137931f02SIlya Yanok static int dsps_resume(struct device *dev)
70237931f02SIlya Yanok {
70337931f02SIlya Yanok struct musb_hdrc_platform_data *plat = dev->platform_data;
70437931f02SIlya Yanok struct omap_musb_board_data *data = plat->board_data;
70537931f02SIlya Yanok
70637931f02SIlya Yanok /* Start the on-chip PHY and its PLL. */
70737931f02SIlya Yanok if (data->set_phy_power)
708*1cac34ceSMugunthan V N data->set_phy_power(data->dev, 1);
70937931f02SIlya Yanok
71037931f02SIlya Yanok return 0;
71137931f02SIlya Yanok }
71237931f02SIlya Yanok #endif
71337931f02SIlya Yanok
71437931f02SIlya Yanok static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume);
71537931f02SIlya Yanok #endif
71637931f02SIlya Yanok
71737931f02SIlya Yanok #ifndef __UBOOT__
71837931f02SIlya Yanok static const struct platform_device_id musb_dsps_id_table[] __devinitconst = {
71937931f02SIlya Yanok {
72037931f02SIlya Yanok .name = "musb-ti81xx",
72137931f02SIlya Yanok .driver_data = (kernel_ulong_t) &ti81xx_driver_data,
72237931f02SIlya Yanok },
72337931f02SIlya Yanok { }, /* Terminating Entry */
72437931f02SIlya Yanok };
72537931f02SIlya Yanok MODULE_DEVICE_TABLE(platform, musb_dsps_id_table);
72637931f02SIlya Yanok
72737931f02SIlya Yanok static const struct of_device_id musb_dsps_of_match[] __devinitconst = {
72837931f02SIlya Yanok { .compatible = "musb-ti81xx", },
72937931f02SIlya Yanok { .compatible = "ti,ti81xx-musb", },
73037931f02SIlya Yanok { .compatible = "ti,am335x-musb", },
73137931f02SIlya Yanok { },
73237931f02SIlya Yanok };
73337931f02SIlya Yanok MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
73437931f02SIlya Yanok
73537931f02SIlya Yanok static struct platform_driver dsps_usbss_driver = {
73637931f02SIlya Yanok .probe = dsps_probe,
73737931f02SIlya Yanok .remove = __devexit_p(dsps_remove),
73837931f02SIlya Yanok .driver = {
73937931f02SIlya Yanok .name = "musb-dsps",
74037931f02SIlya Yanok .pm = &dsps_pm_ops,
74137931f02SIlya Yanok .of_match_table = musb_dsps_of_match,
74237931f02SIlya Yanok },
74337931f02SIlya Yanok .id_table = musb_dsps_id_table,
74437931f02SIlya Yanok };
74537931f02SIlya Yanok
74637931f02SIlya Yanok MODULE_DESCRIPTION("TI DSPS MUSB Glue Layer");
74737931f02SIlya Yanok MODULE_AUTHOR("Ravi B <ravibabu@ti.com>");
74837931f02SIlya Yanok MODULE_AUTHOR("Ajay Kumar Gupta <ajay.gupta@ti.com>");
74937931f02SIlya Yanok MODULE_LICENSE("GPL v2");
75037931f02SIlya Yanok
dsps_init(void)75137931f02SIlya Yanok static int __init dsps_init(void)
75237931f02SIlya Yanok {
75337931f02SIlya Yanok return platform_driver_register(&dsps_usbss_driver);
75437931f02SIlya Yanok }
75537931f02SIlya Yanok subsys_initcall(dsps_init);
75637931f02SIlya Yanok
dsps_exit(void)75737931f02SIlya Yanok static void __exit dsps_exit(void)
75837931f02SIlya Yanok {
75937931f02SIlya Yanok platform_driver_unregister(&dsps_usbss_driver);
76037931f02SIlya Yanok }
76137931f02SIlya Yanok module_exit(dsps_exit);
76237931f02SIlya Yanok #endif
763