1 /* 2 * Copyright (c) 2017 3 * Patrice Chotard <patrice.chotard@st.com> 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 #include <common.h> 9 #include <asm/io.h> 10 #include <bitfield.h> 11 #include <dm.h> 12 #include <errno.h> 13 #include <fdtdec.h> 14 #include <generic-phy.h> 15 #include <libfdt.h> 16 #include <regmap.h> 17 #include <reset-uclass.h> 18 #include <syscon.h> 19 #include <wait_bit.h> 20 21 #include <linux/bitops.h> 22 #include <linux/compat.h> 23 24 DECLARE_GLOBAL_DATA_PTR; 25 26 /* Default PHY_SEL and REFCLKSEL configuration */ 27 #define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 28 29 /* ports parameters overriding */ 30 #define STIH407_USB_PICOPHY_PARAM_DEF 0x39a4dc 31 32 #define PHYPARAM_REG 1 33 #define PHYCTRL_REG 2 34 #define PHYPARAM_NB 3 35 36 struct sti_usb_phy { 37 struct regmap *regmap; 38 struct reset_ctl global_ctl; 39 struct reset_ctl port_ctl; 40 int param; 41 int ctrl; 42 }; 43 44 static int sti_usb_phy_deassert(struct sti_usb_phy *phy) 45 { 46 int ret; 47 48 ret = reset_deassert(&phy->global_ctl); 49 if (ret < 0) { 50 error("PHY global deassert failed: %d", ret); 51 return ret; 52 } 53 54 ret = reset_deassert(&phy->port_ctl); 55 if (ret < 0) 56 error("PHY port deassert failed: %d", ret); 57 58 return ret; 59 } 60 61 static int sti_usb_phy_init(struct phy *usb_phy) 62 { 63 struct udevice *dev = usb_phy->dev; 64 struct sti_usb_phy *phy = dev_get_priv(dev); 65 void __iomem *reg; 66 67 /* set ctrl picophy value */ 68 reg = (void __iomem *)phy->regmap->base + phy->ctrl; 69 /* CTRL_PORT mask is 0x1f */ 70 clrsetbits_le32(reg, 0x1f, STIH407_USB_PICOPHY_CTRL_PORT_CONF); 71 72 /* set ports parameters overriding */ 73 reg = (void __iomem *)phy->regmap->base + phy->param; 74 /* PARAM_DEF mask is 0xffffffff */ 75 clrsetbits_le32(reg, 0xffffffff, STIH407_USB_PICOPHY_PARAM_DEF); 76 77 return sti_usb_phy_deassert(phy); 78 } 79 80 static int sti_usb_phy_exit(struct phy *usb_phy) 81 { 82 struct udevice *dev = usb_phy->dev; 83 struct sti_usb_phy *phy = dev_get_priv(dev); 84 int ret; 85 86 ret = reset_assert(&phy->port_ctl); 87 if (ret < 0) { 88 error("PHY port assert failed: %d", ret); 89 return ret; 90 } 91 92 ret = reset_assert(&phy->global_ctl); 93 if (ret < 0) 94 error("PHY global assert failed: %d", ret); 95 96 return ret; 97 } 98 99 struct phy_ops sti_usb_phy_ops = { 100 .init = sti_usb_phy_init, 101 .exit = sti_usb_phy_exit, 102 }; 103 104 int sti_usb_phy_probe(struct udevice *dev) 105 { 106 struct sti_usb_phy *priv = dev_get_priv(dev); 107 struct udevice *syscon; 108 struct ofnode_phandle_args syscfg_phandle; 109 u32 cells[PHYPARAM_NB]; 110 int ret, count; 111 112 /* get corresponding syscon phandle */ 113 ret = dev_read_phandle_with_args(dev, "st,syscfg", NULL, 0, 0, 114 &syscfg_phandle); 115 116 if (ret < 0) { 117 error("Can't get syscfg phandle: %d\n", ret); 118 return ret; 119 } 120 121 ret = uclass_get_device_by_ofnode(UCLASS_SYSCON, syscfg_phandle.node, 122 &syscon); 123 if (ret) { 124 error("unable to find syscon device (%d)\n", ret); 125 return ret; 126 } 127 128 priv->regmap = syscon_get_regmap(syscon); 129 if (!priv->regmap) { 130 error("unable to find regmap\n"); 131 return -ENODEV; 132 } 133 134 /* get phy param offset */ 135 count = fdtdec_get_int_array_count(gd->fdt_blob, dev_of_offset(dev), 136 "st,syscfg", cells, 137 ARRAY_SIZE(cells)); 138 139 if (count < 0) { 140 error("Bad PHY st,syscfg property %d\n", count); 141 return -EINVAL; 142 } 143 144 if (count > PHYPARAM_NB) { 145 error("Unsupported PHY param count %d\n", count); 146 return -EINVAL; 147 } 148 149 priv->param = cells[PHYPARAM_REG]; 150 priv->ctrl = cells[PHYCTRL_REG]; 151 152 /* get global reset control */ 153 ret = reset_get_by_name(dev, "global", &priv->global_ctl); 154 if (ret) { 155 error("can't get global reset for %s (%d)", dev->name, ret); 156 return ret; 157 } 158 159 /* get port reset control */ 160 ret = reset_get_by_name(dev, "port", &priv->port_ctl); 161 if (ret) { 162 error("can't get port reset for %s (%d)", dev->name, ret); 163 return ret; 164 } 165 166 return 0; 167 } 168 169 static const struct udevice_id sti_usb_phy_ids[] = { 170 { .compatible = "st,stih407-usb2-phy" }, 171 { } 172 }; 173 174 U_BOOT_DRIVER(sti_usb_phy) = { 175 .name = "sti_usb_phy", 176 .id = UCLASS_PHY, 177 .of_match = sti_usb_phy_ids, 178 .probe = sti_usb_phy_probe, 179 .ops = &sti_usb_phy_ops, 180 .priv_auto_alloc_size = sizeof(struct sti_usb_phy), 181 }; 182