brintos

brintos / linux-shallow public Read only

0
0
Text · 12.2 KiB · 20023f6 Raw
427 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Rockchip emmc PHY driver4 *5 * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>6 * Copyright (C) 2016 ROCKCHIP, Inc.7 */8 9#include <linux/clk.h>10#include <linux/delay.h>11#include <linux/mfd/syscon.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/of_address.h>15#include <linux/phy/phy.h>16#include <linux/platform_device.h>17#include <linux/regmap.h>18 19/*20 * The higher 16-bit of this register is used for write protection21 * only if BIT(x + 16) set to 1 the BIT(x) can be written.22 */23#define HIWORD_UPDATE(val, mask, shift) \24		((val) << (shift) | (mask) << ((shift) + 16))25 26/* Register definition */27#define GRF_EMMCPHY_CON0		0x028#define GRF_EMMCPHY_CON1		0x429#define GRF_EMMCPHY_CON2		0x830#define GRF_EMMCPHY_CON3		0xc31#define GRF_EMMCPHY_CON4		0x1032#define GRF_EMMCPHY_CON5		0x1433#define GRF_EMMCPHY_CON6		0x1834#define GRF_EMMCPHY_STATUS		0x2035 36#define PHYCTRL_PDB_MASK		0x137#define PHYCTRL_PDB_SHIFT		0x038#define PHYCTRL_PDB_PWR_ON		0x139#define PHYCTRL_PDB_PWR_OFF		0x040#define PHYCTRL_ENDLL_MASK		0x141#define PHYCTRL_ENDLL_SHIFT		0x142#define PHYCTRL_ENDLL_ENABLE		0x143#define PHYCTRL_ENDLL_DISABLE		0x044#define PHYCTRL_CALDONE_MASK		0x145#define PHYCTRL_CALDONE_SHIFT		0x646#define PHYCTRL_CALDONE_DONE		0x147#define PHYCTRL_CALDONE_GOING		0x048#define PHYCTRL_DLLRDY_MASK		0x149#define PHYCTRL_DLLRDY_SHIFT		0x550#define PHYCTRL_DLLRDY_DONE		0x151#define PHYCTRL_DLLRDY_GOING		0x052#define PHYCTRL_FREQSEL_200M		0x053#define PHYCTRL_FREQSEL_50M		0x154#define PHYCTRL_FREQSEL_100M		0x255#define PHYCTRL_FREQSEL_150M		0x356#define PHYCTRL_FREQSEL_MASK		0x357#define PHYCTRL_FREQSEL_SHIFT		0xc58#define PHYCTRL_DR_MASK			0x759#define PHYCTRL_DR_SHIFT		0x460#define PHYCTRL_DR_50OHM		0x061#define PHYCTRL_DR_33OHM		0x162#define PHYCTRL_DR_66OHM		0x263#define PHYCTRL_DR_100OHM		0x364#define PHYCTRL_DR_40OHM		0x465#define PHYCTRL_OTAPDLYENA		0x166#define PHYCTRL_OTAPDLYENA_MASK		0x167#define PHYCTRL_OTAPDLYENA_SHIFT	0xb68#define PHYCTRL_OTAPDLYSEL_DEFAULT	0x469#define PHYCTRL_OTAPDLYSEL_MAXVALUE	0xf70#define PHYCTRL_OTAPDLYSEL_MASK		0xf71#define PHYCTRL_OTAPDLYSEL_SHIFT	0x772#define PHYCTRL_REN_STRB_DISABLE	0x073#define PHYCTRL_REN_STRB_ENABLE		0x174#define PHYCTRL_REN_STRB_MASK		0x175#define PHYCTRL_REN_STRB_SHIFT		0x976 77#define PHYCTRL_IS_CALDONE(x) \78	((((x) >> PHYCTRL_CALDONE_SHIFT) & \79	  PHYCTRL_CALDONE_MASK) == PHYCTRL_CALDONE_DONE)80#define PHYCTRL_IS_DLLRDY(x) \81	((((x) >> PHYCTRL_DLLRDY_SHIFT) & \82	  PHYCTRL_DLLRDY_MASK) == PHYCTRL_DLLRDY_DONE)83 84struct rockchip_emmc_phy {85	unsigned int	reg_offset;86	struct regmap	*reg_base;87	struct clk	*emmcclk;88	unsigned int drive_impedance;89	unsigned int enable_strobe_pulldown;90	unsigned int output_tapdelay_select;91};92 93static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)94{95	struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);96	unsigned int caldone;97	unsigned int dllrdy;98	unsigned int freqsel = PHYCTRL_FREQSEL_200M;99	unsigned long rate;100	int ret;101 102	/*103	 * Keep phyctrl_pdb and phyctrl_endll low to allow104	 * initialization of CALIO state M/C DFFs105	 */106	regmap_write(rk_phy->reg_base,107		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,108		     HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,109				   PHYCTRL_PDB_MASK,110				   PHYCTRL_PDB_SHIFT));111	regmap_write(rk_phy->reg_base,112		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,113		     HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,114				   PHYCTRL_ENDLL_MASK,115				   PHYCTRL_ENDLL_SHIFT));116 117	/* Already finish power_off above */118	if (on_off == PHYCTRL_PDB_PWR_OFF)119		return 0;120 121	rate = clk_get_rate(rk_phy->emmcclk);122 123	if (rate != 0) {124		unsigned long ideal_rate;125		unsigned long diff;126 127		switch (rate) {128		case 1 ... 74999999:129			ideal_rate = 50000000;130			freqsel = PHYCTRL_FREQSEL_50M;131			break;132		case 75000000 ... 124999999:133			ideal_rate = 100000000;134			freqsel = PHYCTRL_FREQSEL_100M;135			break;136		case 125000000 ... 174999999:137			ideal_rate = 150000000;138			freqsel = PHYCTRL_FREQSEL_150M;139			break;140		default:141			ideal_rate = 200000000;142			break;143		}144 145		diff = (rate > ideal_rate) ?146			rate - ideal_rate : ideal_rate - rate;147 148		/*149		 * In order for tuning delays to be accurate we need to be150		 * pretty spot on for the DLL range, so warn if we're too151		 * far off.  Also warn if we're above the 200 MHz max.  Don't152		 * warn for really slow rates since we won't be tuning then.153		 */154		if ((rate > 50000000 && diff > 15000000) || (rate > 200000000))155			dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate);156	}157 158	/*159	 * According to the user manual, calpad calibration160	 * cycle takes more than 2us without the minimal recommended161	 * value, so we may need a little margin here162	 */163	udelay(3);164	regmap_write(rk_phy->reg_base,165		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,166		     HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON,167				   PHYCTRL_PDB_MASK,168				   PHYCTRL_PDB_SHIFT));169 170	/*171	 * According to the user manual, it asks driver to wait 5us for172	 * calpad busy trimming. However it is documented that this value is173	 * PVT(A.K.A process,voltage and temperature) relevant, so some174	 * failure cases are found which indicates we should be more tolerant175	 * to calpad busy trimming.176	 */177	ret = regmap_read_poll_timeout(rk_phy->reg_base,178				       rk_phy->reg_offset + GRF_EMMCPHY_STATUS,179				       caldone, PHYCTRL_IS_CALDONE(caldone),180				       0, 50);181	if (ret) {182		pr_err("%s: caldone failed, ret=%d\n", __func__, ret);183		return ret;184	}185 186	/* Set the frequency of the DLL operation */187	regmap_write(rk_phy->reg_base,188		     rk_phy->reg_offset + GRF_EMMCPHY_CON0,189		     HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK,190				   PHYCTRL_FREQSEL_SHIFT));191 192	/* Turn on the DLL */193	regmap_write(rk_phy->reg_base,194		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,195		     HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,196				   PHYCTRL_ENDLL_MASK,197				   PHYCTRL_ENDLL_SHIFT));198 199	/*200	 * We turned on the DLL even though the rate was 0 because we the201	 * clock might be turned on later.  ...but we can't wait for the DLL202	 * to lock when the rate is 0 because it will never lock with no203	 * input clock.204	 *205	 * Technically we should be checking the lock later when the clock206	 * is turned on, but for now we won't.207	 */208	if (rate == 0)209		return 0;210 211	/*212	 * After enabling analog DLL circuits docs say that we need 10.2 us if213	 * our source clock is at 50 MHz and that lock time scales linearly214	 * with clock speed.  If we are powering on the PHY and the card clock215	 * is super slow (like 100 kHZ) this could take as long as 5.1 ms as216	 * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms217	 * Hopefully we won't be running at 100 kHz, but we should still make218	 * sure we wait long enough.219	 *220	 * NOTE: There appear to be corner cases where the DLL seems to take221	 * extra long to lock for reasons that aren't understood.  In some222	 * extreme cases we've seen it take up to over 10ms (!).  We'll be223	 * generous and give it 50ms.224	 */225	ret = regmap_read_poll_timeout(rk_phy->reg_base,226				       rk_phy->reg_offset + GRF_EMMCPHY_STATUS,227				       dllrdy, PHYCTRL_IS_DLLRDY(dllrdy),228				       0, 50 * USEC_PER_MSEC);229	if (ret) {230		pr_err("%s: dllrdy failed. ret=%d\n", __func__, ret);231		return ret;232	}233 234	return 0;235}236 237static int rockchip_emmc_phy_init(struct phy *phy)238{239	struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);240	int ret = 0;241 242	/*243	 * We purposely get the clock here and not in probe to avoid the244	 * circular dependency problem.  We expect:245	 * - PHY driver to probe246	 * - SDHCI driver to start probe247	 * - SDHCI driver to register it's clock248	 * - SDHCI driver to get the PHY249	 * - SDHCI driver to init the PHY250	 *251	 * The clock is optional, using clk_get_optional() to get the clock252	 * and do error processing if the return value != NULL253	 *254	 * NOTE: we don't do anything special for EPROBE_DEFER here.  Given the255	 * above expected use case, EPROBE_DEFER isn't sensible to expect, so256	 * it's just like any other error.257	 */258	rk_phy->emmcclk = clk_get_optional(&phy->dev, "emmcclk");259	if (IS_ERR(rk_phy->emmcclk)) {260		ret = PTR_ERR(rk_phy->emmcclk);261		dev_err(&phy->dev, "Error getting emmcclk: %d\n", ret);262		rk_phy->emmcclk = NULL;263	}264 265	return ret;266}267 268static int rockchip_emmc_phy_exit(struct phy *phy)269{270	struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);271 272	clk_put(rk_phy->emmcclk);273 274	return 0;275}276 277static int rockchip_emmc_phy_power_off(struct phy *phy)278{279	/* Power down emmc phy analog blocks */280	return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF);281}282 283static int rockchip_emmc_phy_power_on(struct phy *phy)284{285	struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);286 287	/* Drive impedance: from DTS */288	regmap_write(rk_phy->reg_base,289		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,290		     HIWORD_UPDATE(rk_phy->drive_impedance,291				   PHYCTRL_DR_MASK,292				   PHYCTRL_DR_SHIFT));293 294	/* Output tap delay: enable */295	regmap_write(rk_phy->reg_base,296		     rk_phy->reg_offset + GRF_EMMCPHY_CON0,297		     HIWORD_UPDATE(PHYCTRL_OTAPDLYENA,298				   PHYCTRL_OTAPDLYENA_MASK,299				   PHYCTRL_OTAPDLYENA_SHIFT));300 301	/* Output tap delay */302	regmap_write(rk_phy->reg_base,303		     rk_phy->reg_offset + GRF_EMMCPHY_CON0,304		     HIWORD_UPDATE(rk_phy->output_tapdelay_select,305				   PHYCTRL_OTAPDLYSEL_MASK,306				   PHYCTRL_OTAPDLYSEL_SHIFT));307 308	/* Internal pull-down for strobe line */309	regmap_write(rk_phy->reg_base,310		     rk_phy->reg_offset + GRF_EMMCPHY_CON2,311		     HIWORD_UPDATE(rk_phy->enable_strobe_pulldown,312				   PHYCTRL_REN_STRB_MASK,313				   PHYCTRL_REN_STRB_SHIFT));314 315	/* Power up emmc phy analog blocks */316	return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON);317}318 319static const struct phy_ops ops = {320	.init		= rockchip_emmc_phy_init,321	.exit		= rockchip_emmc_phy_exit,322	.power_on	= rockchip_emmc_phy_power_on,323	.power_off	= rockchip_emmc_phy_power_off,324	.owner		= THIS_MODULE,325};326 327static u32 convert_drive_impedance_ohm(struct platform_device *pdev, u32 dr_ohm)328{329	switch (dr_ohm) {330	case 100:331		return PHYCTRL_DR_100OHM;332	case 66:333		return PHYCTRL_DR_66OHM;334	case 50:335		return PHYCTRL_DR_50OHM;336	case 40:337		return PHYCTRL_DR_40OHM;338	case 33:339		return PHYCTRL_DR_33OHM;340	}341 342	dev_warn(&pdev->dev, "Invalid value %u for drive-impedance-ohm.\n",343		 dr_ohm);344	return PHYCTRL_DR_50OHM;345}346 347static int rockchip_emmc_phy_probe(struct platform_device *pdev)348{349	struct device *dev = &pdev->dev;350	struct rockchip_emmc_phy *rk_phy;351	struct phy *generic_phy;352	struct phy_provider *phy_provider;353	struct regmap *grf;354	unsigned int reg_offset;355	u32 val;356 357	if (!dev->parent || !dev->parent->of_node)358		return -ENODEV;359 360	grf = syscon_node_to_regmap(dev->parent->of_node);361	if (IS_ERR(grf)) {362		dev_err(dev, "Missing rockchip,grf property\n");363		return PTR_ERR(grf);364	}365 366	rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);367	if (!rk_phy)368		return -ENOMEM;369 370	if (of_property_read_u32(dev->of_node, "reg", &reg_offset)) {371		dev_err(dev, "missing reg property in node %pOFn\n",372			dev->of_node);373		return -EINVAL;374	}375 376	rk_phy->reg_offset = reg_offset;377	rk_phy->reg_base = grf;378	rk_phy->drive_impedance = PHYCTRL_DR_50OHM;379	rk_phy->enable_strobe_pulldown = PHYCTRL_REN_STRB_DISABLE;380	rk_phy->output_tapdelay_select = PHYCTRL_OTAPDLYSEL_DEFAULT;381 382	if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val))383		rk_phy->drive_impedance = convert_drive_impedance_ohm(pdev, val);384 385	if (of_property_read_bool(dev->of_node, "rockchip,enable-strobe-pulldown"))386		rk_phy->enable_strobe_pulldown = PHYCTRL_REN_STRB_ENABLE;387 388	if (!of_property_read_u32(dev->of_node, "rockchip,output-tapdelay-select", &val)) {389		if (val <= PHYCTRL_OTAPDLYSEL_MAXVALUE)390			rk_phy->output_tapdelay_select = val;391		else392			dev_err(dev, "output-tapdelay-select exceeds limit, apply default\n");393	}394 395	generic_phy = devm_phy_create(dev, dev->of_node, &ops);396	if (IS_ERR(generic_phy)) {397		dev_err(dev, "failed to create PHY\n");398		return PTR_ERR(generic_phy);399	}400 401	phy_set_drvdata(generic_phy, rk_phy);402	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);403 404	return PTR_ERR_OR_ZERO(phy_provider);405}406 407static const struct of_device_id rockchip_emmc_phy_dt_ids[] = {408	{ .compatible = "rockchip,rk3399-emmc-phy" },409	{}410};411 412MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids);413 414static struct platform_driver rockchip_emmc_driver = {415	.probe		= rockchip_emmc_phy_probe,416	.driver		= {417		.name	= "rockchip-emmc-phy",418		.of_match_table = rockchip_emmc_phy_dt_ids,419	},420};421 422module_platform_driver(rockchip_emmc_driver);423 424MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");425MODULE_DESCRIPTION("Rockchip EMMC PHY driver");426MODULE_LICENSE("GPL v2");427