cregit-Linux how code gets into the kernel

Release 4.7 drivers/usb/chipidea/usbmisc_imx.c

/*
 * Copyright 2012 Freescale Semiconductor, Inc.
 *
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>

#include "ci_hdrc_imx.h"


#define MX25_USB_PHY_CTRL_OFFSET	0x08

#define MX25_BM_EXTERNAL_VBUS_DIVIDER	BIT(23)


#define MX25_EHCI_INTERFACE_SINGLE_UNI	(2 << 0)

#define MX25_EHCI_INTERFACE_DIFF_UNI	(0 << 0)

#define MX25_EHCI_INTERFACE_MASK	(0xf)


#define MX25_OTG_SIC_SHIFT		29

#define MX25_OTG_SIC_MASK		(0x3 << MX25_OTG_SIC_SHIFT)

#define MX25_OTG_PM_BIT			BIT(24)

#define MX25_OTG_PP_BIT			BIT(11)

#define MX25_OTG_OCPOL_BIT		BIT(3)


#define MX25_H1_SIC_SHIFT		21

#define MX25_H1_SIC_MASK		(0x3 << MX25_H1_SIC_SHIFT)

#define MX25_H1_PP_BIT			BIT(18)

#define MX25_H1_PM_BIT			BIT(16)

#define MX25_H1_IPPUE_UP_BIT		BIT(7)

#define MX25_H1_IPPUE_DOWN_BIT		BIT(6)

#define MX25_H1_TLL_BIT			BIT(5)

#define MX25_H1_USBTE_BIT		BIT(4)

#define MX25_H1_OCPOL_BIT		BIT(2)


#define MX27_H1_PM_BIT			BIT(8)

#define MX27_H2_PM_BIT			BIT(16)

#define MX27_OTG_PM_BIT			BIT(24)


#define MX53_USB_OTG_PHY_CTRL_0_OFFSET	0x08

#define MX53_USB_OTG_PHY_CTRL_1_OFFSET	0x0c

#define MX53_USB_UH2_CTRL_OFFSET	0x14

#define MX53_USB_UH3_CTRL_OFFSET	0x18

#define MX53_BM_OVER_CUR_DIS_H1		BIT(5)

#define MX53_BM_OVER_CUR_DIS_OTG	BIT(8)

#define MX53_BM_OVER_CUR_DIS_UHx	BIT(30)

#define MX53_USB_PHYCTRL1_PLLDIV_MASK	0x3

#define MX53_USB_PLL_DIV_24_MHZ		0x01


#define MX6_BM_NON_BURST_SETTING	BIT(1)

#define MX6_BM_OVER_CUR_DIS		BIT(7)

#define MX6_BM_WAKEUP_ENABLE		BIT(10)

#define MX6_BM_ID_WAKEUP		BIT(16)

#define MX6_BM_VBUS_WAKEUP		BIT(17)

#define MX6SX_BM_DPDM_WAKEUP_EN		BIT(29)

#define MX6_BM_WAKEUP_INTR		BIT(31)

#define MX6_USB_OTG1_PHY_CTRL		0x18
/* For imx6dql, it is host-only controller, for later imx6, it is otg's */

#define MX6_USB_OTG2_PHY_CTRL		0x1c

#define MX6SX_USB_VBUS_WAKEUP_SOURCE(v)	(v << 8)

#define MX6SX_USB_VBUS_WAKEUP_SOURCE_VBUS	MX6SX_USB_VBUS_WAKEUP_SOURCE(0)

#define MX6SX_USB_VBUS_WAKEUP_SOURCE_AVALID	MX6SX_USB_VBUS_WAKEUP_SOURCE(1)

#define MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID	MX6SX_USB_VBUS_WAKEUP_SOURCE(2)

#define MX6SX_USB_VBUS_WAKEUP_SOURCE_SESS_END	MX6SX_USB_VBUS_WAKEUP_SOURCE(3)


#define VF610_OVER_CUR_DIS		BIT(7)


#define MX7D_USBNC_USB_CTRL2		0x4

#define MX7D_USB_VBUS_WAKEUP_SOURCE_MASK	0x3

#define MX7D_USB_VBUS_WAKEUP_SOURCE(v)		(v << 0)

#define MX7D_USB_VBUS_WAKEUP_SOURCE_VBUS	MX7D_USB_VBUS_WAKEUP_SOURCE(0)

#define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID	MX7D_USB_VBUS_WAKEUP_SOURCE(1)

#define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID	MX7D_USB_VBUS_WAKEUP_SOURCE(2)

#define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END	MX7D_USB_VBUS_WAKEUP_SOURCE(3)


struct usbmisc_ops {
	/* It's called once when probe a usb device */
	
int (*init)(struct imx_usbmisc_data *data);
	/* It's called once after adding a usb device */
	
int (*post)(struct imx_usbmisc_data *data);
	/* It's called when we need to enable/disable usb wakeup */
	
int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled);
};


struct imx_usbmisc {
	
void __iomem *base;
	
spinlock_t lock;
	
const struct usbmisc_ops *ops;
};


static int usbmisc_imx25_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 val = 0; if (data->index > 1) return -EINVAL; spin_lock_irqsave(&usbmisc->lock, flags); switch (data->index) { case 0: val = readl(usbmisc->base); val &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT); val |= (MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT; val |= (MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT); writel(val, usbmisc->base); break; case 1: val = readl(usbmisc->base); val &= ~(MX25_H1_SIC_MASK | MX25_H1_PP_BIT | MX25_H1_IPPUE_UP_BIT); val |= (MX25_EHCI_INTERFACE_SINGLE_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT; val |= (MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT | MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT); writel(val, usbmisc->base); break; } spin_unlock_irqrestore(&usbmisc->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
denis cariklidenis carikli16993.37%150.00%
stefan agnerstefan agner126.63%150.00%
Total181100.00%2100.00%


static int usbmisc_imx25_post(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); void __iomem *reg; unsigned long flags; u32 val; if (data->index > 2) return -EINVAL; if (data->evdo) { spin_lock_irqsave(&usbmisc->lock, flags); reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET; val = readl(reg); writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg); spin_unlock_irqrestore(&usbmisc->lock, flags); usleep_range(5000, 10000); /* needed to stabilize voltage */ } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
michael grzeschikmichael grzeschik8072.73%125.00%
stefan agnerstefan agner1210.91%125.00%
sascha hauersascha hauer109.09%125.00%
fabio estevamfabio estevam87.27%125.00%
Total110100.00%4100.00%


static int usbmisc_imx27_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 val; switch (data->index) { case 0: val = MX27_OTG_PM_BIT; break; case 1: val = MX27_H1_PM_BIT; break; case 2: val = MX27_H2_PM_BIT; break; default: return -EINVAL; } spin_lock_irqsave(&usbmisc->lock, flags); if (data->disable_oc) val = readl(usbmisc->base) | val; else val = readl(usbmisc->base) & ~val; writel(val, usbmisc->base); spin_unlock_irqrestore(&usbmisc->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
alexander shiyanalexander shiyan11790.70%150.00%
stefan agnerstefan agner129.30%150.00%
Total129100.00%2100.00%


static int usbmisc_imx53_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); void __iomem *reg = NULL; unsigned long flags; u32 val = 0; if (data->index > 3) return -EINVAL; /* Select a 24 MHz reference clock for the PHY */ val = readl(usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET); val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK; val |= MX53_USB_PLL_DIV_24_MHZ; writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET); if (data->disable_oc) { spin_lock_irqsave(&usbmisc->lock, flags); switch (data->index) { case 0: reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET; val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG; break; case 1: reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET; val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1; break; case 2: reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET; val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx; break; case 3: reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET; val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx; break; } if (reg && val) writel(val, reg); spin_unlock_irqrestore(&usbmisc->lock, flags); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
michael grzeschikmichael grzeschik16474.89%120.00%
fabio estevamfabio estevam3214.61%240.00%
stefan agnerstefan agner125.48%120.00%
sascha hauersascha hauer115.02%120.00%
Total219100.00%5100.00%


static int usbmisc_imx6q_set_wakeup (struct imx_usbmisc_data *data, bool enabled) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 val; u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP); int ret = 0; if (data->index > 3) return -EINVAL; spin_lock_irqsave(&usbmisc->lock, flags); val = readl(usbmisc->base + data->index * 4); if (enabled) { val |= wakeup_setting; writel(val, usbmisc->base + data->index * 4); } else { if (val & MX6_BM_WAKEUP_INTR) pr_debug("wakeup int at ci_hdrc.%d\n", data->index); val &= ~wakeup_setting; writel(val, usbmisc->base + data->index * 4); } spin_unlock_irqrestore(&usbmisc->lock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
peter chenpeter chen162100.00%1100.00%
Total162100.00%1100.00%


static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 reg; if (data->index > 3) return -EINVAL; spin_lock_irqsave(&usbmisc->lock, flags); if (data->disable_oc) { reg = readl(usbmisc->base + data->index * 4); writel(reg | MX6_BM_OVER_CUR_DIS, usbmisc->base + data->index * 4); } /* SoC non-burst setting */ reg = readl(usbmisc->base + data->index * 4); writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base + data->index * 4); spin_unlock_irqrestore(&usbmisc->lock, flags); usbmisc_imx6q_set_wakeup(data, false); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
richard zhaorichard zhao7551.72%120.00%
peter chenpeter chen4631.72%240.00%
stefan agnerstefan agner128.28%120.00%
sascha hauersascha hauer128.28%120.00%
Total145100.00%5100.00%


static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data) { void __iomem *reg = NULL; unsigned long flags; struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); u32 val; usbmisc_imx6q_init(data); if (data->index == 0 || data->index == 1) { reg = usbmisc->base + MX6_USB_OTG1_PHY_CTRL + data->index * 4; spin_lock_irqsave(&usbmisc->lock, flags); /* Set vbus wakeup source as bvalid */ val = readl(reg); writel(val | MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID, reg); /* * Disable dp/dm wakeup in device mode when vbus is * not there. */ val = readl(usbmisc->base + data->index * 4); writel(val & ~MX6SX_BM_DPDM_WAKEUP_EN, usbmisc->base + data->index * 4); spin_unlock_irqrestore(&usbmisc->lock, flags); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
peter chenpeter chen14599.32%150.00%
fengguang wufengguang wu10.68%150.00%
Total146100.00%2100.00%


static int usbmisc_vf610_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); u32 reg; /* * Vybrid only has one misc register set, but in two different * areas. These is reflected in two instances of this driver. */ if (data->index >= 1) return -EINVAL; if (data->disable_oc) { reg = readl(usbmisc->base); writel(reg | VF610_OVER_CUR_DIS, usbmisc->base); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
stefan agnerstefan agner70100.00%1100.00%
Total70100.00%1100.00%


static int usbmisc_imx7d_set_wakeup (struct imx_usbmisc_data *data, bool enabled) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 val; u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP); spin_lock_irqsave(&usbmisc->lock, flags); val = readl(usbmisc->base); if (enabled) { writel(val | wakeup_setting, usbmisc->base); } else { if (val & MX6_BM_WAKEUP_INTR) dev_dbg(data->dev, "wakeup int\n"); writel(val & ~wakeup_setting, usbmisc->base); } spin_unlock_irqrestore(&usbmisc->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
peter chenpeter chen123100.00%1100.00%
Total123100.00%1100.00%


static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 reg; if (data->index >= 1) return -EINVAL; spin_lock_irqsave(&usbmisc->lock, flags); if (data->disable_oc) { reg = readl(usbmisc->base); writel(reg | MX6_BM_OVER_CUR_DIS, usbmisc->base); } reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID, usbmisc->base + MX7D_USBNC_USB_CTRL2); spin_unlock_irqrestore(&usbmisc->lock, flags); usbmisc_imx7d_set_wakeup(data, false); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
peter chenpeter chen129100.00%1100.00%
Total129100.00%1100.00%

static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, }; static const struct usbmisc_ops imx27_usbmisc_ops = { .init = usbmisc_imx27_init, }; static const struct usbmisc_ops imx53_usbmisc_ops = { .init = usbmisc_imx53_init, }; static const struct usbmisc_ops imx6q_usbmisc_ops = { .set_wakeup = usbmisc_imx6q_set_wakeup, .init = usbmisc_imx6q_init, }; static const struct usbmisc_ops vf610_usbmisc_ops = { .init = usbmisc_vf610_init, }; static const struct usbmisc_ops imx6sx_usbmisc_ops = { .set_wakeup = usbmisc_imx6q_set_wakeup, .init = usbmisc_imx6sx_init, }; static const struct usbmisc_ops imx7d_usbmisc_ops = { .init = usbmisc_imx7d_init, .set_wakeup = usbmisc_imx7d_set_wakeup, };
int imx_usbmisc_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc; if (!data) return 0; usbmisc = dev_get_drvdata(data->dev); if (!usbmisc->ops->init) return 0; return usbmisc->ops->init(data); }

Contributors

PersonTokensPropCommitsCommitProp
sascha hauersascha hauer3361.11%133.33%
peter chenpeter chen1222.22%133.33%
stefan agnerstefan agner916.67%133.33%
Total54100.00%3100.00%

EXPORT_SYMBOL_GPL(imx_usbmisc_init);
int imx_usbmisc_init_post(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc; if (!data) return 0; usbmisc = dev_get_drvdata(data->dev); if (!usbmisc->ops->post) return 0; return usbmisc->ops->post(data); }

Contributors

PersonTokensPropCommitsCommitProp
sascha hauersascha hauer3361.11%133.33%
peter chenpeter chen1222.22%133.33%
stefan agnerstefan agner916.67%133.33%
Total54100.00%3100.00%

EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled) { struct imx_usbmisc *usbmisc; if (!data) return 0; usbmisc = dev_get_drvdata(data->dev); if (!usbmisc->ops->set_wakeup) return 0; return usbmisc->ops->set_wakeup(data, enabled); }

Contributors

PersonTokensPropCommitsCommitProp
peter chenpeter chen59100.00%1100.00%
Total59100.00%1100.00%

EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup); static const struct of_device_id usbmisc_imx_dt_ids[] = { { .compatible = "fsl,imx25-usbmisc", .data = &imx25_usbmisc_ops, }, { .compatible = "fsl,imx35-usbmisc", .data = &imx25_usbmisc_ops, }, { .compatible = "fsl,imx27-usbmisc", .data = &imx27_usbmisc_ops, }, { .compatible = "fsl,imx51-usbmisc", .data = &imx53_usbmisc_ops, }, { .compatible = "fsl,imx53-usbmisc", .data = &imx53_usbmisc_ops, }, { .compatible = "fsl,imx6q-usbmisc", .data = &imx6q_usbmisc_ops, }, { .compatible = "fsl,vf610-usbmisc", .data = &vf610_usbmisc_ops, }, { .compatible = "fsl,imx6sx-usbmisc", .data = &imx6sx_usbmisc_ops, }, { .compatible = "fsl,imx6ul-usbmisc", .data = &imx6sx_usbmisc_ops, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
static int usbmisc_imx_probe(struct platform_device *pdev) { struct resource *res; struct imx_usbmisc *data; const struct of_device_id *of_id; of_id = of_match_device(usbmisc_imx_dt_ids, &pdev->dev); if (!of_id) return -ENODEV; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; spin_lock_init(&data->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); data->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(data->base)) return PTR_ERR(data->base); data->ops = (const struct usbmisc_ops *)of_id->data; platform_set_drvdata(pdev, data); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
richard zhaorichard zhao8959.73%114.29%
labbe corentinlabbe corentin2416.11%114.29%
marc kleine-buddemarc kleine-budde1912.75%228.57%
thierry redingthierry reding106.71%114.29%
stefan agnerstefan agner53.36%114.29%
michael grzeschikmichael grzeschik21.34%114.29%
Total149100.00%7100.00%


static int usbmisc_imx_remove(struct platform_device *pdev) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
richard zhaorichard zhao1392.86%150.00%
michael grzeschikmichael grzeschik17.14%150.00%
Total14100.00%2100.00%

static struct platform_driver usbmisc_imx_driver = { .probe = usbmisc_imx_probe, .remove = usbmisc_imx_remove, .driver = { .name = "usbmisc_imx", .of_match_table = usbmisc_imx_dt_ids, }, }; module_platform_driver(usbmisc_imx_driver); MODULE_ALIAS("platform:usbmisc-imx"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("driver for imx usb non-core registers"); MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");

Overall Contributors

PersonTokensPropCommitsCommitProp
peter chenpeter chen86336.44%623.08%
michael grzeschikmichael grzeschik34414.53%311.54%
richard zhaorichard zhao28612.08%13.85%
denis cariklidenis carikli25510.77%13.85%
stefan agnerstefan agner1837.73%13.85%
alexander shiyanalexander shiyan1687.09%27.69%
sascha hauersascha hauer1405.91%13.85%
fabio estevamfabio estevam522.20%311.54%
marc kleine-buddemarc kleine-budde331.39%27.69%
labbe corentinlabbe corentin241.01%13.85%
thierry redingthierry reding100.42%13.85%
arnaud patardarnaud patard70.30%13.85%
fengguang wufengguang wu10.04%13.85%
philipp zabelphilipp zabel10.04%13.85%
alexander shishkinalexander shishkin10.04%13.85%
Total2368100.00%26100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}