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