brintos

brintos / linux-shallow public Read only

0
0
Text · 11.4 KiB · 8c8673d Raw
471 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * phy-uniphier-usb3hs.c - HS-PHY driver for Socionext UniPhier USB3 controller4 * Copyright 2015-2018 Socionext Inc.5 * Author:6 *      Kunihiko Hayashi <hayashi.kunihiko@socionext.com>7 * Contributors:8 *      Motoya Tanigawa <tanigawa.motoya@socionext.com>9 *      Masami Hiramatsu <masami.hiramatsu@linaro.org>10 */11 12#include <linux/bitfield.h>13#include <linux/bitops.h>14#include <linux/clk.h>15#include <linux/io.h>16#include <linux/module.h>17#include <linux/nvmem-consumer.h>18#include <linux/of.h>19#include <linux/of_platform.h>20#include <linux/phy/phy.h>21#include <linux/platform_device.h>22#include <linux/regulator/consumer.h>23#include <linux/reset.h>24#include <linux/slab.h>25 26#define HSPHY_CFG0		0x027#define HSPHY_CFG0_HS_I_MASK	GENMASK(31, 28)28#define HSPHY_CFG0_HSDISC_MASK	GENMASK(27, 26)29#define HSPHY_CFG0_SWING_MASK	GENMASK(17, 16)30#define HSPHY_CFG0_SEL_T_MASK	GENMASK(15, 12)31#define HSPHY_CFG0_RTERM_MASK	GENMASK(7, 6)32#define HSPHY_CFG0_TRIMMASK	(HSPHY_CFG0_HS_I_MASK \33				 | HSPHY_CFG0_SEL_T_MASK \34				 | HSPHY_CFG0_RTERM_MASK)35 36#define HSPHY_CFG1		0x437#define HSPHY_CFG1_DAT_EN	BIT(29)38#define HSPHY_CFG1_ADR_EN	BIT(28)39#define HSPHY_CFG1_ADR_MASK	GENMASK(27, 16)40#define HSPHY_CFG1_DAT_MASK	GENMASK(23, 16)41 42#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) }43 44#define RX_CHK_SYNC	PHY_F(0, 5, 5)	/* RX sync mode */45#define RX_SYNC_SEL	PHY_F(1, 1, 0)	/* RX sync length */46#define LS_SLEW		PHY_F(10, 6, 6)	/* LS mode slew rate */47#define FS_LS_DRV	PHY_F(10, 5, 5)	/* FS/LS slew rate */48 49#define MAX_PHY_PARAMS	450 51struct uniphier_u3hsphy_param {52	struct {53		int reg_no;54		int msb;55		int lsb;56	} field;57	u8 value;58};59 60struct uniphier_u3hsphy_trim_param {61	unsigned int rterm;62	unsigned int sel_t;63	unsigned int hs_i;64};65 66#define trim_param_is_valid(p)	((p)->rterm || (p)->sel_t || (p)->hs_i)67 68struct uniphier_u3hsphy_priv {69	struct device *dev;70	void __iomem *base;71	struct clk *clk, *clk_parent, *clk_ext, *clk_parent_gio;72	struct reset_control *rst, *rst_parent, *rst_parent_gio;73	struct regulator *vbus;74	const struct uniphier_u3hsphy_soc_data *data;75};76 77struct uniphier_u3hsphy_soc_data {78	bool is_legacy;79	int nparams;80	const struct uniphier_u3hsphy_param param[MAX_PHY_PARAMS];81	u32 config0;82	u32 config1;83	void (*trim_func)(struct uniphier_u3hsphy_priv *priv, u32 *pconfig,84			  struct uniphier_u3hsphy_trim_param *pt);85};86 87static void uniphier_u3hsphy_trim_ld20(struct uniphier_u3hsphy_priv *priv,88				       u32 *pconfig,89				       struct uniphier_u3hsphy_trim_param *pt)90{91	*pconfig &= ~HSPHY_CFG0_RTERM_MASK;92	*pconfig |= FIELD_PREP(HSPHY_CFG0_RTERM_MASK, pt->rterm);93 94	*pconfig &= ~HSPHY_CFG0_SEL_T_MASK;95	*pconfig |= FIELD_PREP(HSPHY_CFG0_SEL_T_MASK, pt->sel_t);96 97	*pconfig &= ~HSPHY_CFG0_HS_I_MASK;98	*pconfig |= FIELD_PREP(HSPHY_CFG0_HS_I_MASK,  pt->hs_i);99}100 101static int uniphier_u3hsphy_get_nvparam(struct uniphier_u3hsphy_priv *priv,102					const char *name, unsigned int *val)103{104	struct nvmem_cell *cell;105	u8 *buf;106 107	cell = devm_nvmem_cell_get(priv->dev, name);108	if (IS_ERR(cell))109		return PTR_ERR(cell);110 111	buf = nvmem_cell_read(cell, NULL);112	if (IS_ERR(buf))113		return PTR_ERR(buf);114 115	*val = *buf;116 117	kfree(buf);118 119	return 0;120}121 122static int uniphier_u3hsphy_get_nvparams(struct uniphier_u3hsphy_priv *priv,123					 struct uniphier_u3hsphy_trim_param *pt)124{125	int ret;126 127	ret = uniphier_u3hsphy_get_nvparam(priv, "rterm", &pt->rterm);128	if (ret)129		return ret;130 131	ret = uniphier_u3hsphy_get_nvparam(priv, "sel_t", &pt->sel_t);132	if (ret)133		return ret;134 135	ret = uniphier_u3hsphy_get_nvparam(priv, "hs_i", &pt->hs_i);136	if (ret)137		return ret;138 139	return 0;140}141 142static int uniphier_u3hsphy_update_config(struct uniphier_u3hsphy_priv *priv,143					  u32 *pconfig)144{145	struct uniphier_u3hsphy_trim_param trim;146	int ret, trimmed = 0;147 148	if (priv->data->trim_func) {149		ret = uniphier_u3hsphy_get_nvparams(priv, &trim);150		if (ret == -EPROBE_DEFER)151			return ret;152 153		/*154		 * call trim_func only when trimming parameters that aren't155		 * all-zero can be acquired. All-zero parameters mean nothing156		 * has been written to nvmem.157		 */158		if (!ret && trim_param_is_valid(&trim)) {159			priv->data->trim_func(priv, pconfig, &trim);160			trimmed = 1;161		} else {162			dev_dbg(priv->dev, "can't get parameter from nvmem\n");163		}164	}165 166	/* use default parameters without trimming values */167	if (!trimmed) {168		*pconfig &= ~HSPHY_CFG0_HSDISC_MASK;169		*pconfig |= FIELD_PREP(HSPHY_CFG0_HSDISC_MASK, 3);170	}171 172	return 0;173}174 175static void uniphier_u3hsphy_set_param(struct uniphier_u3hsphy_priv *priv,176				       const struct uniphier_u3hsphy_param *p)177{178	u32 val;179	u32 field_mask = GENMASK(p->field.msb, p->field.lsb);180	u8 data;181 182	val = readl(priv->base + HSPHY_CFG1);183	val &= ~HSPHY_CFG1_ADR_MASK;184	val |= FIELD_PREP(HSPHY_CFG1_ADR_MASK, p->field.reg_no)185		| HSPHY_CFG1_ADR_EN;186	writel(val, priv->base + HSPHY_CFG1);187 188	val = readl(priv->base + HSPHY_CFG1);189	val &= ~HSPHY_CFG1_ADR_EN;190	writel(val, priv->base + HSPHY_CFG1);191 192	val = readl(priv->base + HSPHY_CFG1);193	val &= ~FIELD_PREP(HSPHY_CFG1_DAT_MASK, field_mask);194	data = field_mask & (p->value << p->field.lsb);195	val |=  FIELD_PREP(HSPHY_CFG1_DAT_MASK, data) | HSPHY_CFG1_DAT_EN;196	writel(val, priv->base + HSPHY_CFG1);197 198	val = readl(priv->base + HSPHY_CFG1);199	val &= ~HSPHY_CFG1_DAT_EN;200	writel(val, priv->base + HSPHY_CFG1);201}202 203static int uniphier_u3hsphy_power_on(struct phy *phy)204{205	struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);206	int ret;207 208	ret = clk_prepare_enable(priv->clk_ext);209	if (ret)210		return ret;211 212	ret = clk_prepare_enable(priv->clk);213	if (ret)214		goto out_clk_ext_disable;215 216	ret = reset_control_deassert(priv->rst);217	if (ret)218		goto out_clk_disable;219 220	if (priv->vbus) {221		ret = regulator_enable(priv->vbus);222		if (ret)223			goto out_rst_assert;224	}225 226	return 0;227 228out_rst_assert:229	reset_control_assert(priv->rst);230out_clk_disable:231	clk_disable_unprepare(priv->clk);232out_clk_ext_disable:233	clk_disable_unprepare(priv->clk_ext);234 235	return ret;236}237 238static int uniphier_u3hsphy_power_off(struct phy *phy)239{240	struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);241 242	if (priv->vbus)243		regulator_disable(priv->vbus);244 245	reset_control_assert(priv->rst);246	clk_disable_unprepare(priv->clk);247	clk_disable_unprepare(priv->clk_ext);248 249	return 0;250}251 252static int uniphier_u3hsphy_init(struct phy *phy)253{254	struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);255	u32 config0, config1;256	int i, ret;257 258	ret = clk_prepare_enable(priv->clk_parent);259	if (ret)260		return ret;261 262	ret = clk_prepare_enable(priv->clk_parent_gio);263	if (ret)264		goto out_clk_disable;265 266	ret = reset_control_deassert(priv->rst_parent);267	if (ret)268		goto out_clk_gio_disable;269 270	ret = reset_control_deassert(priv->rst_parent_gio);271	if (ret)272		goto out_rst_assert;273 274	if ((priv->data->is_legacy)275	    || (!priv->data->config0 && !priv->data->config1))276		return 0;277 278	config0 = priv->data->config0;279	config1 = priv->data->config1;280 281	ret = uniphier_u3hsphy_update_config(priv, &config0);282	if (ret)283		goto out_rst_assert;284 285	writel(config0, priv->base + HSPHY_CFG0);286	writel(config1, priv->base + HSPHY_CFG1);287 288	for (i = 0; i < priv->data->nparams; i++)289		uniphier_u3hsphy_set_param(priv, &priv->data->param[i]);290 291	return 0;292 293out_rst_assert:294	reset_control_assert(priv->rst_parent);295out_clk_gio_disable:296	clk_disable_unprepare(priv->clk_parent_gio);297out_clk_disable:298	clk_disable_unprepare(priv->clk_parent);299 300	return ret;301}302 303static int uniphier_u3hsphy_exit(struct phy *phy)304{305	struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);306 307	reset_control_assert(priv->rst_parent_gio);308	reset_control_assert(priv->rst_parent);309	clk_disable_unprepare(priv->clk_parent_gio);310	clk_disable_unprepare(priv->clk_parent);311 312	return 0;313}314 315static const struct phy_ops uniphier_u3hsphy_ops = {316	.init           = uniphier_u3hsphy_init,317	.exit           = uniphier_u3hsphy_exit,318	.power_on       = uniphier_u3hsphy_power_on,319	.power_off      = uniphier_u3hsphy_power_off,320	.owner          = THIS_MODULE,321};322 323static int uniphier_u3hsphy_probe(struct platform_device *pdev)324{325	struct device *dev = &pdev->dev;326	struct uniphier_u3hsphy_priv *priv;327	struct phy_provider *phy_provider;328	struct phy *phy;329 330	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);331	if (!priv)332		return -ENOMEM;333 334	priv->dev = dev;335	priv->data = of_device_get_match_data(dev);336	if (WARN_ON(!priv->data ||337		    priv->data->nparams > MAX_PHY_PARAMS))338		return -EINVAL;339 340	priv->base = devm_platform_ioremap_resource(pdev, 0);341	if (IS_ERR(priv->base))342		return PTR_ERR(priv->base);343 344	if (!priv->data->is_legacy) {345		priv->clk = devm_clk_get(dev, "phy");346		if (IS_ERR(priv->clk))347			return PTR_ERR(priv->clk);348 349		priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");350		if (IS_ERR(priv->clk_ext))351			return PTR_ERR(priv->clk_ext);352 353		priv->rst = devm_reset_control_get_shared(dev, "phy");354		if (IS_ERR(priv->rst))355			return PTR_ERR(priv->rst);356 357	} else {358		priv->clk_parent_gio = devm_clk_get(dev, "gio");359		if (IS_ERR(priv->clk_parent_gio))360			return PTR_ERR(priv->clk_parent_gio);361 362		priv->rst_parent_gio =363			devm_reset_control_get_shared(dev, "gio");364		if (IS_ERR(priv->rst_parent_gio))365			return PTR_ERR(priv->rst_parent_gio);366	}367 368	priv->clk_parent = devm_clk_get(dev, "link");369	if (IS_ERR(priv->clk_parent))370		return PTR_ERR(priv->clk_parent);371 372	priv->rst_parent = devm_reset_control_get_shared(dev, "link");373	if (IS_ERR(priv->rst_parent))374		return PTR_ERR(priv->rst_parent);375 376	priv->vbus = devm_regulator_get_optional(dev, "vbus");377	if (IS_ERR(priv->vbus)) {378		if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)379			return PTR_ERR(priv->vbus);380		priv->vbus = NULL;381	}382 383	phy = devm_phy_create(dev, dev->of_node, &uniphier_u3hsphy_ops);384	if (IS_ERR(phy))385		return PTR_ERR(phy);386 387	phy_set_drvdata(phy, priv);388	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);389 390	return PTR_ERR_OR_ZERO(phy_provider);391}392 393static const struct uniphier_u3hsphy_soc_data uniphier_pro5_data = {394	.is_legacy = true,395	.nparams = 0,396};397 398static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = {399	.is_legacy = false,400	.nparams = 2,401	.param = {402		{ RX_CHK_SYNC, 1 },403		{ RX_SYNC_SEL, 1 },404	},405};406 407static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = {408	.is_legacy = false,409	.nparams = 4,410	.param = {411		{ RX_CHK_SYNC, 1 },412		{ RX_SYNC_SEL, 1 },413		{ LS_SLEW, 1 },414		{ FS_LS_DRV, 1 },415	},416	.trim_func = uniphier_u3hsphy_trim_ld20,417	.config0 = 0x92316680,418	.config1 = 0x00000106,419};420 421static const struct uniphier_u3hsphy_soc_data uniphier_pxs3_data = {422	.is_legacy = false,423	.nparams = 2,424	.param = {425		{ RX_CHK_SYNC, 1 },426		{ RX_SYNC_SEL, 1 },427	},428	.trim_func = uniphier_u3hsphy_trim_ld20,429	.config0 = 0x92316680,430	.config1 = 0x00000106,431};432 433static const struct of_device_id uniphier_u3hsphy_match[] = {434	{435		.compatible = "socionext,uniphier-pro5-usb3-hsphy",436		.data = &uniphier_pro5_data,437	},438	{439		.compatible = "socionext,uniphier-pxs2-usb3-hsphy",440		.data = &uniphier_pxs2_data,441	},442	{443		.compatible = "socionext,uniphier-ld20-usb3-hsphy",444		.data = &uniphier_ld20_data,445	},446	{447		.compatible = "socionext,uniphier-pxs3-usb3-hsphy",448		.data = &uniphier_pxs3_data,449	},450	{451		.compatible = "socionext,uniphier-nx1-usb3-hsphy",452		.data = &uniphier_pxs3_data,453	},454	{ /* sentinel */ }455};456MODULE_DEVICE_TABLE(of, uniphier_u3hsphy_match);457 458static struct platform_driver uniphier_u3hsphy_driver = {459	.probe = uniphier_u3hsphy_probe,460	.driver	= {461		.name = "uniphier-usb3-hsphy",462		.of_match_table	= uniphier_u3hsphy_match,463	},464};465 466module_platform_driver(uniphier_u3hsphy_driver);467 468MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");469MODULE_DESCRIPTION("UniPhier HS-PHY driver for USB3 controller");470MODULE_LICENSE("GPL v2");471