--- zzzz-none-000/linux-3.10.107/drivers/usb/chipidea/usbmisc_imx.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/usb/chipidea/usbmisc_imx.c 2021-02-04 17:41:59.000000000 +0000 @@ -11,73 +11,137 @@ #include #include -#include #include #include #include -#include "ci13xxx_imx.h" - -#define USB_DEV_MAX 4 +#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; - struct clk *clk; - struct usbmisc_usb_device usbdev[USB_DEV_MAX]; const struct usbmisc_ops *ops; }; -static struct imx_usbmisc *usbmisc; - -static struct usbmisc_usb_device *get_usbdev(struct device *dev) +static int usbmisc_imx25_init(struct imx_usbmisc_data *data) { - int i, ret; + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val = 0; - for (i = 0; i < USB_DEV_MAX; i++) { - if (usbmisc->usbdev[i].dev == dev) - return &usbmisc->usbdev[i]; - else if (!usbmisc->usbdev[i].dev) - break; - } + if (data->index > 1) + return -EINVAL; - if (i >= USB_DEV_MAX) - return ERR_PTR(-EBUSY); + 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); - ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]); - if (ret) - return ERR_PTR(ret); + writel(val, usbmisc->base); - return &usbmisc->usbdev[i]; + break; + } + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; } -static int usbmisc_imx25_post(struct device *dev) +static int usbmisc_imx25_post(struct imx_usbmisc_data *data) { - struct usbmisc_usb_device *usbdev; + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); void __iomem *reg; unsigned long flags; u32 val; - usbdev = get_usbdev(dev); - if (IS_ERR(usbdev)) - return PTR_ERR(usbdev); - - reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET; + if (data->index > 2) + return -EINVAL; - if (usbdev->evdo) { + 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); @@ -87,20 +151,56 @@ return 0; } -static int usbmisc_imx53_init(struct device *dev) +static int usbmisc_imx27_init(struct imx_usbmisc_data *data) { - struct usbmisc_usb_device *usbdev; + 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; +} + +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; - usbdev = get_usbdev(dev); - if (IS_ERR(usbdev)) - return PTR_ERR(usbdev); + if (data->index > 3) + return -EINVAL; - if (usbdev->disable_oc) { + /* 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 (usbdev->index) { + switch (data->index) { case 0: reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET; val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG; @@ -126,46 +226,253 @@ return 0; } -static int usbmisc_imx6q_init(struct device *dev) +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; +} - struct usbmisc_usb_device *usbdev; +static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); unsigned long flags; u32 reg; - usbdev = get_usbdev(dev); - if (IS_ERR(usbdev)) - return PTR_ERR(usbdev); + if (data->index > 3) + return -EINVAL; - if (usbdev->disable_oc) { - spin_lock_irqsave(&usbmisc->lock, flags); - reg = readl(usbmisc->base + usbdev->index * 4); + 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 + usbdev->index * 4); + 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; +} + +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; } +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; +} + +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; +} + +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; +} + 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); +} +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); +} +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); +} +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, }, @@ -173,18 +480,31 @@ .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; - int ret; - struct of_device_id *tmp_dev; + const struct of_device_id *of_id; - if (usbmisc) - return -EBUSY; + 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) @@ -197,39 +517,14 @@ if (IS_ERR(data->base)) return PTR_ERR(data->base); - data->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(data->clk)) { - dev_err(&pdev->dev, - "failed to get clock, err=%ld\n", PTR_ERR(data->clk)); - return PTR_ERR(data->clk); - } - - ret = clk_prepare_enable(data->clk); - if (ret) { - dev_err(&pdev->dev, - "clk_prepare_enable failed, err=%d\n", ret); - return ret; - } - - tmp_dev = (struct of_device_id *) - of_match_device(usbmisc_imx_dt_ids, &pdev->dev); - data->ops = (const struct usbmisc_ops *)tmp_dev->data; - usbmisc = data; - ret = usbmisc_set_ops(data->ops); - if (ret) { - usbmisc = NULL; - clk_disable_unprepare(data->clk); - return ret; - } + data->ops = (const struct usbmisc_ops *)of_id->data; + platform_set_drvdata(pdev, data); return 0; } static int usbmisc_imx_remove(struct platform_device *pdev) { - usbmisc_unset_ops(usbmisc->ops); - clk_disable_unprepare(usbmisc->clk); - usbmisc = NULL; return 0; } @@ -238,22 +533,11 @@ .remove = usbmisc_imx_remove, .driver = { .name = "usbmisc_imx", - .owner = THIS_MODULE, .of_match_table = usbmisc_imx_dt_ids, }, }; -int usbmisc_imx_drv_init(void) -{ - return platform_driver_register(&usbmisc_imx_driver); -} -subsys_initcall(usbmisc_imx_drv_init); - -void usbmisc_imx_drv_exit(void) -{ - platform_driver_unregister(&usbmisc_imx_driver); -} -module_exit(usbmisc_imx_drv_exit); +module_platform_driver(usbmisc_imx_driver); MODULE_ALIAS("platform:usbmisc-imx"); MODULE_LICENSE("GPL v2");