| 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | /* |
| 3 | * Freescale 83xx USB SOC setup code |
| 4 | * |
| 5 | * Copyright (C) 2007 Freescale Semiconductor, Inc. |
| 6 | * Author: Li Yang |
| 7 | */ |
| 8 | |
| 9 | #include <linux/stddef.h> |
| 10 | #include <linux/kernel.h> |
| 11 | #include <linux/errno.h> |
| 12 | #include <linux/of.h> |
| 13 | #include <linux/of_address.h> |
| 14 | #include <linux/io.h> |
| 15 | |
| 16 | #include <sysdev/fsl_soc.h> |
| 17 | |
| 18 | #include "mpc83xx.h" |
| 19 | |
| 20 | int __init mpc831x_usb_cfg(void) |
| 21 | { |
| 22 | u32 temp; |
| 23 | void __iomem *immap, *usb_regs; |
| 24 | struct device_node *np = NULL; |
| 25 | struct device_node *immr_node = NULL; |
| 26 | const void *prop; |
| 27 | struct resource res; |
| 28 | int ret = 0; |
| 29 | #ifdef CONFIG_USB_OTG |
| 30 | const void *dr_mode; |
| 31 | #endif |
| 32 | |
| 33 | np = of_find_compatible_node(NULL, NULL, compat: "fsl-usb2-dr" ); |
| 34 | if (!np) |
| 35 | return -ENODEV; |
| 36 | prop = of_get_property(node: np, name: "phy_type" , NULL); |
| 37 | |
| 38 | /* Map IMMR space for pin and clock settings */ |
| 39 | immap = ioremap(offset: get_immrbase(), size: 0x1000); |
| 40 | if (!immap) { |
| 41 | of_node_put(node: np); |
| 42 | return -ENOMEM; |
| 43 | } |
| 44 | |
| 45 | /* Configure clock */ |
| 46 | immr_node = of_get_parent(node: np); |
| 47 | if (immr_node && (of_device_is_compatible(device: immr_node, "fsl,mpc8315-immr" ) || |
| 48 | of_device_is_compatible(device: immr_node, "fsl,mpc8308-immr" ))) |
| 49 | clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, |
| 50 | MPC8315_SCCR_USB_MASK, |
| 51 | MPC8315_SCCR_USB_DRCM_01); |
| 52 | else |
| 53 | clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, |
| 54 | MPC83XX_SCCR_USB_MASK, |
| 55 | MPC83XX_SCCR_USB_DRCM_11); |
| 56 | |
| 57 | /* Configure pin mux for ULPI. There is no pin mux for UTMI */ |
| 58 | if (prop && !strcmp(prop, "ulpi" )) { |
| 59 | if (of_device_is_compatible(device: immr_node, "fsl,mpc8308-immr" )) { |
| 60 | clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, |
| 61 | MPC8308_SICRH_USB_MASK, |
| 62 | MPC8308_SICRH_USB_ULPI); |
| 63 | } else if (of_device_is_compatible(device: immr_node, "fsl,mpc8315-immr" )) { |
| 64 | clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, |
| 65 | MPC8315_SICRL_USB_MASK, |
| 66 | MPC8315_SICRL_USB_ULPI); |
| 67 | clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, |
| 68 | MPC8315_SICRH_USB_MASK, |
| 69 | MPC8315_SICRH_USB_ULPI); |
| 70 | } else { |
| 71 | clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, |
| 72 | MPC831X_SICRL_USB_MASK, |
| 73 | MPC831X_SICRL_USB_ULPI); |
| 74 | clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, |
| 75 | MPC831X_SICRH_USB_MASK, |
| 76 | MPC831X_SICRH_USB_ULPI); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | iounmap(addr: immap); |
| 81 | |
| 82 | of_node_put(node: immr_node); |
| 83 | |
| 84 | /* Map USB SOC space */ |
| 85 | ret = of_address_to_resource(dev: np, index: 0, r: &res); |
| 86 | if (ret) { |
| 87 | of_node_put(node: np); |
| 88 | return ret; |
| 89 | } |
| 90 | usb_regs = ioremap(offset: res.start, size: resource_size(res: &res)); |
| 91 | |
| 92 | /* Using on-chip PHY */ |
| 93 | if (prop && (!strcmp(prop, "utmi_wide" ) || !strcmp(prop, "utmi" ))) { |
| 94 | u32 refsel; |
| 95 | |
| 96 | if (of_device_is_compatible(device: immr_node, "fsl,mpc8308-immr" )) |
| 97 | goto out; |
| 98 | |
| 99 | if (of_device_is_compatible(device: immr_node, "fsl,mpc8315-immr" )) |
| 100 | refsel = CONTROL_REFSEL_24MHZ; |
| 101 | else |
| 102 | refsel = CONTROL_REFSEL_48MHZ; |
| 103 | /* Set UTMI_PHY_EN and REFSEL */ |
| 104 | out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, |
| 105 | CONTROL_UTMI_PHY_EN | refsel); |
| 106 | /* Using external UPLI PHY */ |
| 107 | } else if (prop && !strcmp(prop, "ulpi" )) { |
| 108 | /* Set PHY_CLK_SEL to ULPI */ |
| 109 | temp = CONTROL_PHY_CLK_SEL_ULPI; |
| 110 | #ifdef CONFIG_USB_OTG |
| 111 | /* Set OTG_PORT */ |
| 112 | if (!of_device_is_compatible(device: immr_node, "fsl,mpc8308-immr" )) { |
| 113 | dr_mode = of_get_property(node: np, name: "dr_mode" , NULL); |
| 114 | if (dr_mode && !strcmp(dr_mode, "otg" )) |
| 115 | temp |= CONTROL_OTG_PORT; |
| 116 | } |
| 117 | #endif /* CONFIG_USB_OTG */ |
| 118 | out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, temp); |
| 119 | } else { |
| 120 | pr_warn("831x USB PHY type not supported\n" ); |
| 121 | ret = -EINVAL; |
| 122 | } |
| 123 | |
| 124 | out: |
| 125 | iounmap(addr: usb_regs); |
| 126 | of_node_put(node: np); |
| 127 | return ret; |
| 128 | } |
| 129 | |