|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* | 
|  | * Freescale 83xx USB SOC setup code | 
|  | * | 
|  | * Copyright (C) 2007 Freescale Semiconductor, Inc. | 
|  | * Author: Li Yang | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include <linux/stddef.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/errno.h> | 
|  | #include <linux/of.h> | 
|  |  | 
|  | #include <asm/io.h> | 
|  | #include <asm/prom.h> | 
|  | #include <sysdev/fsl_soc.h> | 
|  |  | 
|  | #include "mpc83xx.h" | 
|  |  | 
|  |  | 
|  | #ifdef CONFIG_PPC_MPC834x | 
|  | int mpc834x_usb_cfg(void) | 
|  | { | 
|  | unsigned long sccr, sicrl, sicrh; | 
|  | void __iomem *immap; | 
|  | struct device_node *np = NULL; | 
|  | int port0_is_dr = 0, port1_is_dr = 0; | 
|  | const void *prop, *dr_mode; | 
|  |  | 
|  | immap = ioremap(get_immrbase(), 0x1000); | 
|  | if (!immap) | 
|  | return -ENOMEM; | 
|  |  | 
|  | /* Read registers */ | 
|  | /* Note: DR and MPH must use the same clock setting in SCCR */ | 
|  | sccr = in_be32(immap + MPC83XX_SCCR_OFFS) & ~MPC83XX_SCCR_USB_MASK; | 
|  | sicrl = in_be32(immap + MPC83XX_SICRL_OFFS) & ~MPC834X_SICRL_USB_MASK; | 
|  | sicrh = in_be32(immap + MPC83XX_SICRH_OFFS) & ~MPC834X_SICRH_USB_UTMI; | 
|  |  | 
|  | np = of_find_compatible_node(NULL, NULL, "fsl-usb2-dr"); | 
|  | if (np) { | 
|  | sccr |= MPC83XX_SCCR_USB_DRCM_11;  /* 1:3 */ | 
|  |  | 
|  | prop = of_get_property(np, "phy_type", NULL); | 
|  | port1_is_dr = 1; | 
|  | if (prop && (!strcmp(prop, "utmi") || | 
|  | !strcmp(prop, "utmi_wide"))) { | 
|  | sicrl |= MPC834X_SICRL_USB0 | MPC834X_SICRL_USB1; | 
|  | sicrh |= MPC834X_SICRH_USB_UTMI; | 
|  | port0_is_dr = 1; | 
|  | } else if (prop && !strcmp(prop, "serial")) { | 
|  | dr_mode = of_get_property(np, "dr_mode", NULL); | 
|  | if (dr_mode && !strcmp(dr_mode, "otg")) { | 
|  | sicrl |= MPC834X_SICRL_USB0 | MPC834X_SICRL_USB1; | 
|  | port0_is_dr = 1; | 
|  | } else { | 
|  | sicrl |= MPC834X_SICRL_USB1; | 
|  | } | 
|  | } else if (prop && !strcmp(prop, "ulpi")) { | 
|  | sicrl |= MPC834X_SICRL_USB1; | 
|  | } else { | 
|  | printk(KERN_WARNING "834x USB PHY type not supported\n"); | 
|  | } | 
|  | of_node_put(np); | 
|  | } | 
|  | np = of_find_compatible_node(NULL, NULL, "fsl-usb2-mph"); | 
|  | if (np) { | 
|  | sccr |= MPC83XX_SCCR_USB_MPHCM_11; /* 1:3 */ | 
|  |  | 
|  | prop = of_get_property(np, "port0", NULL); | 
|  | if (prop) { | 
|  | if (port0_is_dr) | 
|  | printk(KERN_WARNING | 
|  | "834x USB port0 can't be used by both DR and MPH!\n"); | 
|  | sicrl &= ~MPC834X_SICRL_USB0; | 
|  | } | 
|  | prop = of_get_property(np, "port1", NULL); | 
|  | if (prop) { | 
|  | if (port1_is_dr) | 
|  | printk(KERN_WARNING | 
|  | "834x USB port1 can't be used by both DR and MPH!\n"); | 
|  | sicrl &= ~MPC834X_SICRL_USB1; | 
|  | } | 
|  | of_node_put(np); | 
|  | } | 
|  |  | 
|  | /* Write back */ | 
|  | out_be32(immap + MPC83XX_SCCR_OFFS, sccr); | 
|  | out_be32(immap + MPC83XX_SICRL_OFFS, sicrl); | 
|  | out_be32(immap + MPC83XX_SICRH_OFFS, sicrh); | 
|  |  | 
|  | iounmap(immap); | 
|  | return 0; | 
|  | } | 
|  | #endif /* CONFIG_PPC_MPC834x */ | 
|  |  | 
|  | #ifdef CONFIG_PPC_MPC831x | 
|  | int mpc831x_usb_cfg(void) | 
|  | { | 
|  | u32 temp; | 
|  | void __iomem *immap, *usb_regs; | 
|  | struct device_node *np = NULL; | 
|  | struct device_node *immr_node = NULL; | 
|  | const void *prop; | 
|  | struct resource res; | 
|  | int ret = 0; | 
|  | #ifdef CONFIG_USB_OTG | 
|  | const void *dr_mode; | 
|  | #endif | 
|  |  | 
|  | np = of_find_compatible_node(NULL, NULL, "fsl-usb2-dr"); | 
|  | if (!np) | 
|  | return -ENODEV; | 
|  | prop = of_get_property(np, "phy_type", NULL); | 
|  |  | 
|  | /* Map IMMR space for pin and clock settings */ | 
|  | immap = ioremap(get_immrbase(), 0x1000); | 
|  | if (!immap) { | 
|  | of_node_put(np); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | /* Configure clock */ | 
|  | immr_node = of_get_parent(np); | 
|  | if (immr_node && (of_device_is_compatible(immr_node, "fsl,mpc8315-immr") || | 
|  | of_device_is_compatible(immr_node, "fsl,mpc8308-immr"))) | 
|  | clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, | 
|  | MPC8315_SCCR_USB_MASK, | 
|  | MPC8315_SCCR_USB_DRCM_01); | 
|  | else | 
|  | clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, | 
|  | MPC83XX_SCCR_USB_MASK, | 
|  | MPC83XX_SCCR_USB_DRCM_11); | 
|  |  | 
|  | /* Configure pin mux for ULPI.  There is no pin mux for UTMI */ | 
|  | if (prop && !strcmp(prop, "ulpi")) { | 
|  | if (of_device_is_compatible(immr_node, "fsl,mpc8308-immr")) { | 
|  | clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, | 
|  | MPC8308_SICRH_USB_MASK, | 
|  | MPC8308_SICRH_USB_ULPI); | 
|  | } else if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) { | 
|  | clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, | 
|  | MPC8315_SICRL_USB_MASK, | 
|  | MPC8315_SICRL_USB_ULPI); | 
|  | clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, | 
|  | MPC8315_SICRH_USB_MASK, | 
|  | MPC8315_SICRH_USB_ULPI); | 
|  | } else { | 
|  | clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, | 
|  | MPC831X_SICRL_USB_MASK, | 
|  | MPC831X_SICRL_USB_ULPI); | 
|  | clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, | 
|  | MPC831X_SICRH_USB_MASK, | 
|  | MPC831X_SICRH_USB_ULPI); | 
|  | } | 
|  | } | 
|  |  | 
|  | iounmap(immap); | 
|  |  | 
|  | of_node_put(immr_node); | 
|  |  | 
|  | /* Map USB SOC space */ | 
|  | ret = of_address_to_resource(np, 0, &res); | 
|  | if (ret) { | 
|  | of_node_put(np); | 
|  | return ret; | 
|  | } | 
|  | usb_regs = ioremap(res.start, resource_size(&res)); | 
|  |  | 
|  | /* Using on-chip PHY */ | 
|  | if (prop && (!strcmp(prop, "utmi_wide") || | 
|  | !strcmp(prop, "utmi"))) { | 
|  | u32 refsel; | 
|  |  | 
|  | if (of_device_is_compatible(immr_node, "fsl,mpc8308-immr")) | 
|  | goto out; | 
|  |  | 
|  | if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) | 
|  | refsel = CONTROL_REFSEL_24MHZ; | 
|  | else | 
|  | refsel = CONTROL_REFSEL_48MHZ; | 
|  | /* Set UTMI_PHY_EN and REFSEL */ | 
|  | out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, | 
|  | CONTROL_UTMI_PHY_EN | refsel); | 
|  | /* Using external UPLI PHY */ | 
|  | } else if (prop && !strcmp(prop, "ulpi")) { | 
|  | /* Set PHY_CLK_SEL to ULPI */ | 
|  | temp = CONTROL_PHY_CLK_SEL_ULPI; | 
|  | #ifdef CONFIG_USB_OTG | 
|  | /* Set OTG_PORT */ | 
|  | if (!of_device_is_compatible(immr_node, "fsl,mpc8308-immr")) { | 
|  | dr_mode = of_get_property(np, "dr_mode", NULL); | 
|  | if (dr_mode && !strcmp(dr_mode, "otg")) | 
|  | temp |= CONTROL_OTG_PORT; | 
|  | } | 
|  | #endif /* CONFIG_USB_OTG */ | 
|  | out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, temp); | 
|  | } else { | 
|  | printk(KERN_WARNING "831x USB PHY type not supported\n"); | 
|  | ret = -EINVAL; | 
|  | } | 
|  |  | 
|  | out: | 
|  | iounmap(usb_regs); | 
|  | of_node_put(np); | 
|  | return ret; | 
|  | } | 
|  | #endif /* CONFIG_PPC_MPC831x */ | 
|  |  | 
|  | #ifdef CONFIG_PPC_MPC837x | 
|  | int mpc837x_usb_cfg(void) | 
|  | { | 
|  | void __iomem *immap; | 
|  | struct device_node *np = NULL; | 
|  | const void *prop; | 
|  | int ret = 0; | 
|  |  | 
|  | np = of_find_compatible_node(NULL, NULL, "fsl-usb2-dr"); | 
|  | if (!np || !of_device_is_available(np)) { | 
|  | of_node_put(np); | 
|  | return -ENODEV; | 
|  | } | 
|  | prop = of_get_property(np, "phy_type", NULL); | 
|  |  | 
|  | if (!prop || (strcmp(prop, "ulpi") && strcmp(prop, "serial"))) { | 
|  | printk(KERN_WARNING "837x USB PHY type not supported\n"); | 
|  | of_node_put(np); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* Map IMMR space for pin and clock settings */ | 
|  | immap = ioremap(get_immrbase(), 0x1000); | 
|  | if (!immap) { | 
|  | of_node_put(np); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | /* Configure clock */ | 
|  | clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, MPC837X_SCCR_USB_DRCM_11, | 
|  | MPC837X_SCCR_USB_DRCM_11); | 
|  |  | 
|  | /* Configure pin mux for ULPI/serial */ | 
|  | clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, MPC837X_SICRL_USB_MASK, | 
|  | MPC837X_SICRL_USB_ULPI); | 
|  |  | 
|  | iounmap(immap); | 
|  | of_node_put(np); | 
|  | return ret; | 
|  | } | 
|  | #endif /* CONFIG_PPC_MPC837x */ |