brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · 6bcfe83 Raw
339 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2016-2018 Broadcom4 */5 6#include <linux/delay.h>7#include <linux/io.h>8#include <linux/iopoll.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/phy/phy.h>12#include <linux/platform_device.h>13 14enum bcm_usb_phy_version {15	BCM_SR_USB_COMBO_PHY,16	BCM_SR_USB_HS_PHY,17};18 19enum bcm_usb_phy_reg {20	PLL_CTRL,21	PHY_CTRL,22	PHY_PLL_CTRL,23};24 25/* USB PHY registers */26 27static const u8 bcm_usb_combo_phy_ss[] = {28	[PLL_CTRL]		= 0x18,29	[PHY_CTRL]		= 0x14,30};31 32static const u8 bcm_usb_combo_phy_hs[] = {33	[PLL_CTRL]	= 0x0c,34	[PHY_CTRL]	= 0x10,35};36 37static const u8 bcm_usb_hs_phy[] = {38	[PLL_CTRL]	= 0x8,39	[PHY_CTRL]	= 0xc,40};41 42enum pll_ctrl_bits {43	PLL_RESETB,44	SSPLL_SUSPEND_EN,45	PLL_SEQ_START,46	PLL_LOCK,47};48 49static const u8 u3pll_ctrl[] = {50	[PLL_RESETB]		= 0,51	[SSPLL_SUSPEND_EN]	= 1,52	[PLL_SEQ_START]		= 2,53	[PLL_LOCK]		= 3,54};55 56#define HSPLL_PDIV_MASK		0xF57#define HSPLL_PDIV_VAL		0x158 59static const u8 u2pll_ctrl[] = {60	[PLL_RESETB]	= 5,61	[PLL_LOCK]	= 6,62};63 64enum bcm_usb_phy_ctrl_bits {65	CORERDY,66	PHY_RESETB,67	PHY_PCTL,68};69 70#define PHY_PCTL_MASK	0xffff71#define SSPHY_PCTL_VAL	0x000672 73static const u8 u3phy_ctrl[] = {74	[PHY_RESETB]	= 1,75	[PHY_PCTL]	= 2,76};77 78static const u8 u2phy_ctrl[] = {79	[CORERDY]		= 0,80	[PHY_RESETB]		= 5,81	[PHY_PCTL]		= 6,82};83 84struct bcm_usb_phy_cfg {85	uint32_t type;86	uint32_t version;87	void __iomem *regs;88	struct phy *phy;89	const u8 *offset;90};91 92#define PLL_LOCK_RETRY_COUNT	100093 94enum bcm_usb_phy_type {95	USB_HS_PHY,96	USB_SS_PHY,97};98 99#define NUM_BCM_SR_USB_COMBO_PHYS	2100 101static inline void bcm_usb_reg32_clrbits(void __iomem *addr, uint32_t clear)102{103	writel(readl(addr) & ~clear, addr);104}105 106static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set)107{108	writel(readl(addr) | set, addr);109}110 111static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit)112{113	u32 data;114	int ret;115 116	ret = readl_poll_timeout_atomic(addr, data, (data & bit), 1,117					PLL_LOCK_RETRY_COUNT);118	if (ret)119		pr_err("%s: FAIL\n", __func__);120 121	return ret;122}123 124static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg)125{126	int ret = 0;127	void __iomem *regs = phy_cfg->regs;128	const u8 *offset;129	u32 rd_data;130 131	offset = phy_cfg->offset;132 133	/* Set pctl with mode and soft reset */134	rd_data = readl(regs + offset[PHY_CTRL]);135	rd_data &= ~(PHY_PCTL_MASK << u3phy_ctrl[PHY_PCTL]);136	rd_data |= (SSPHY_PCTL_VAL << u3phy_ctrl[PHY_PCTL]);137	writel(rd_data, regs + offset[PHY_CTRL]);138 139	bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],140			      BIT(u3pll_ctrl[SSPLL_SUSPEND_EN]));141	bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],142			      BIT(u3pll_ctrl[PLL_SEQ_START]));143	bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],144			      BIT(u3pll_ctrl[PLL_RESETB]));145 146	/* Maximum timeout for PLL reset done */147	msleep(30);148 149	ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],150				     BIT(u3pll_ctrl[PLL_LOCK]));151 152	return ret;153}154 155static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg)156{157	int ret = 0;158	void __iomem *regs = phy_cfg->regs;159	const u8 *offset;160 161	offset = phy_cfg->offset;162 163	bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],164			      BIT(u2pll_ctrl[PLL_RESETB]));165	bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],166			      BIT(u2pll_ctrl[PLL_RESETB]));167 168	ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],169				     BIT(u2pll_ctrl[PLL_LOCK]));170 171	return ret;172}173 174static int bcm_usb_phy_reset(struct phy *phy)175{176	struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy);177	void __iomem *regs = phy_cfg->regs;178	const u8 *offset;179 180	offset = phy_cfg->offset;181 182	if (phy_cfg->type == USB_HS_PHY) {183		bcm_usb_reg32_clrbits(regs + offset[PHY_CTRL],184				      BIT(u2phy_ctrl[CORERDY]));185		bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],186				      BIT(u2phy_ctrl[CORERDY]));187	}188 189	return 0;190}191 192static int bcm_usb_phy_init(struct phy *phy)193{194	struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy);195	int ret = -EINVAL;196 197	if (phy_cfg->type == USB_SS_PHY)198		ret = bcm_usb_ss_phy_init(phy_cfg);199	else if (phy_cfg->type == USB_HS_PHY)200		ret = bcm_usb_hs_phy_init(phy_cfg);201 202	return ret;203}204 205static const struct phy_ops sr_phy_ops = {206	.init		= bcm_usb_phy_init,207	.reset		= bcm_usb_phy_reset,208	.owner		= THIS_MODULE,209};210 211static struct phy *bcm_usb_phy_xlate(struct device *dev,212				     const struct of_phandle_args *args)213{214	struct bcm_usb_phy_cfg *phy_cfg;215	int phy_idx;216 217	phy_cfg = dev_get_drvdata(dev);218	if (!phy_cfg)219		return ERR_PTR(-EINVAL);220 221	if (phy_cfg->version == BCM_SR_USB_COMBO_PHY) {222		phy_idx = args->args[0];223 224		if (WARN_ON(phy_idx > 1))225			return ERR_PTR(-ENODEV);226 227		return phy_cfg[phy_idx].phy;228	} else229		return phy_cfg->phy;230}231 232static int bcm_usb_phy_create(struct device *dev, struct device_node *node,233			      void __iomem *regs, uint32_t version)234{235	struct bcm_usb_phy_cfg *phy_cfg;236	int idx;237 238	if (version == BCM_SR_USB_COMBO_PHY) {239		phy_cfg = devm_kzalloc(dev, NUM_BCM_SR_USB_COMBO_PHYS *240				       sizeof(struct bcm_usb_phy_cfg),241				       GFP_KERNEL);242		if (!phy_cfg)243			return -ENOMEM;244 245		for (idx = 0; idx < NUM_BCM_SR_USB_COMBO_PHYS; idx++) {246			phy_cfg[idx].regs = regs;247			phy_cfg[idx].version = version;248			if (idx == 0) {249				phy_cfg[idx].offset = bcm_usb_combo_phy_hs;250				phy_cfg[idx].type = USB_HS_PHY;251			} else {252				phy_cfg[idx].offset = bcm_usb_combo_phy_ss;253				phy_cfg[idx].type = USB_SS_PHY;254			}255			phy_cfg[idx].phy = devm_phy_create(dev, node,256							   &sr_phy_ops);257			if (IS_ERR(phy_cfg[idx].phy))258				return PTR_ERR(phy_cfg[idx].phy);259 260			phy_set_drvdata(phy_cfg[idx].phy, &phy_cfg[idx]);261		}262	} else if (version == BCM_SR_USB_HS_PHY) {263		phy_cfg = devm_kzalloc(dev, sizeof(struct bcm_usb_phy_cfg),264				       GFP_KERNEL);265		if (!phy_cfg)266			return -ENOMEM;267 268		phy_cfg->regs = regs;269		phy_cfg->version = version;270		phy_cfg->offset = bcm_usb_hs_phy;271		phy_cfg->type = USB_HS_PHY;272		phy_cfg->phy = devm_phy_create(dev, node, &sr_phy_ops);273		if (IS_ERR(phy_cfg->phy))274			return PTR_ERR(phy_cfg->phy);275 276		phy_set_drvdata(phy_cfg->phy, phy_cfg);277	} else278		return -ENODEV;279 280	dev_set_drvdata(dev, phy_cfg);281 282	return 0;283}284 285static const struct of_device_id bcm_usb_phy_of_match[] = {286	{287		.compatible = "brcm,sr-usb-combo-phy",288		.data = (void *)BCM_SR_USB_COMBO_PHY,289	},290	{291		.compatible = "brcm,sr-usb-hs-phy",292		.data = (void *)BCM_SR_USB_HS_PHY,293	},294	{ /* sentinel */ },295};296MODULE_DEVICE_TABLE(of, bcm_usb_phy_of_match);297 298static int bcm_usb_phy_probe(struct platform_device *pdev)299{300	struct device *dev = &pdev->dev;301	struct device_node *dn = dev->of_node;302	const struct of_device_id *of_id;303	void __iomem *regs;304	int ret;305	enum bcm_usb_phy_version version;306	struct phy_provider *phy_provider;307 308	regs = devm_platform_ioremap_resource(pdev, 0);309	if (IS_ERR(regs))310		return PTR_ERR(regs);311 312	of_id = of_match_node(bcm_usb_phy_of_match, dn);313	if (of_id)314		version = (uintptr_t)of_id->data;315	else316		return -ENODEV;317 318	ret = bcm_usb_phy_create(dev, dn, regs, version);319	if (ret)320		return ret;321 322	phy_provider = devm_of_phy_provider_register(dev, bcm_usb_phy_xlate);323 324	return PTR_ERR_OR_ZERO(phy_provider);325}326 327static struct platform_driver bcm_usb_phy_driver = {328	.driver = {329		.name = "phy-bcm-sr-usb",330		.of_match_table = bcm_usb_phy_of_match,331	},332	.probe = bcm_usb_phy_probe,333};334module_platform_driver(bcm_usb_phy_driver);335 336MODULE_AUTHOR("Broadcom");337MODULE_DESCRIPTION("Broadcom stingray USB Phy driver");338MODULE_LICENSE("GPL v2");339