17597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter/*
27597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter * phy-bcm-kona-usb2.c - Broadcom Kona USB2 Phy Driver
37597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter *
47597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter * Copyright (C) 2013 Linaro Limited
57597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter * Matt Porter <mporter@linaro.org>
67597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter *
77597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter * This software is licensed under the terms of the GNU General Public
87597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter * License version 2, as published by the Free Software Foundation, and
97597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter * may be copied, distributed, and modified under those terms.
107597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter *
117597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter * This program is distributed in the hope that it will be useful,
127597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter * but WITHOUT ANY WARRANTY; without even the implied warranty of
137597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
147597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter * GNU General Public License for more details.
157597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter */
167597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
177597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#include <linux/clk.h>
187597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#include <linux/delay.h>
197597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#include <linux/err.h>
207597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#include <linux/io.h>
217597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#include <linux/module.h>
227597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#include <linux/of.h>
237597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#include <linux/phy/phy.h>
247597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#include <linux/platform_device.h>
257597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
267597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#define OTGCTL			(0)
277597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#define OTGCTL_OTGSTAT2		BIT(31)
287597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#define OTGCTL_OTGSTAT1		BIT(30)
297597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#define OTGCTL_PRST_N_SW	BIT(11)
307597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#define OTGCTL_HRESET_N		BIT(10)
317597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#define OTGCTL_UTMI_LINE_STATE1	BIT(9)
327597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#define OTGCTL_UTMI_LINE_STATE0	BIT(8)
337597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
347597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#define P1CTL			(8)
357597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#define P1CTL_SOFT_RESET	BIT(1)
367597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter#define P1CTL_NON_DRIVING	BIT(0)
377597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
387597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porterstruct bcm_kona_usb {
397597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	void __iomem *regs;
407597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter};
417597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
427597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porterstatic void bcm_kona_usb_phy_power(struct bcm_kona_usb *phy, int on)
437597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter{
447597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	u32 val;
457597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
467597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	val = readl(phy->regs + OTGCTL);
477597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	if (on) {
487597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter		/* Configure and power PHY */
497597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter		val &= ~(OTGCTL_OTGSTAT2 | OTGCTL_OTGSTAT1 |
507597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter			 OTGCTL_UTMI_LINE_STATE1 | OTGCTL_UTMI_LINE_STATE0);
517597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter		val |= OTGCTL_PRST_N_SW | OTGCTL_HRESET_N;
527597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	} else {
537597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter		val &= ~(OTGCTL_PRST_N_SW | OTGCTL_HRESET_N);
547597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	}
557597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	writel(val, phy->regs + OTGCTL);
567597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter}
577597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
587597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porterstatic int bcm_kona_usb_phy_init(struct phy *gphy)
597597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter{
607597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	struct bcm_kona_usb *phy = phy_get_drvdata(gphy);
617597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	u32 val;
627597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
637597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	/* Soft reset PHY */
647597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	val = readl(phy->regs + P1CTL);
657597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	val &= ~P1CTL_NON_DRIVING;
667597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	val |= P1CTL_SOFT_RESET;
677597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	writel(val, phy->regs + P1CTL);
687597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	writel(val & ~P1CTL_SOFT_RESET, phy->regs + P1CTL);
697597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	/* Reset needs to be asserted for 2ms */
707597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	mdelay(2);
717597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	writel(val | P1CTL_SOFT_RESET, phy->regs + P1CTL);
727597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
737597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	return 0;
747597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter}
757597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
767597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porterstatic int bcm_kona_usb_phy_power_on(struct phy *gphy)
777597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter{
787597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	struct bcm_kona_usb *phy = phy_get_drvdata(gphy);
797597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
807597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	bcm_kona_usb_phy_power(phy, 1);
817597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
827597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	return 0;
837597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter}
847597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
857597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porterstatic int bcm_kona_usb_phy_power_off(struct phy *gphy)
867597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter{
877597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	struct bcm_kona_usb *phy = phy_get_drvdata(gphy);
887597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
897597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	bcm_kona_usb_phy_power(phy, 0);
907597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
917597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	return 0;
927597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter}
937597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
947597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porterstatic struct phy_ops ops = {
957597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	.init		= bcm_kona_usb_phy_init,
967597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	.power_on	= bcm_kona_usb_phy_power_on,
977597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	.power_off	= bcm_kona_usb_phy_power_off,
987597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	.owner		= THIS_MODULE,
997597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter};
1007597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1017597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porterstatic int bcm_kona_usb2_probe(struct platform_device *pdev)
1027597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter{
1037597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	struct device *dev = &pdev->dev;
1047597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	struct bcm_kona_usb *phy;
1057597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	struct resource *res;
1067597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	struct phy *gphy;
1077597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	struct phy_provider *phy_provider;
1087597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1097597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
1107597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	if (!phy)
1117597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter		return -ENOMEM;
1127597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1137597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1147597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	phy->regs = devm_ioremap_resource(&pdev->dev, res);
1157597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	if (IS_ERR(phy->regs))
1167597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter		return PTR_ERR(phy->regs);
1177597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1187597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	platform_set_drvdata(pdev, phy);
1197597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
120f0ed817638b59aa927f1f7e9564dd8796b18dc4fKishon Vijay Abraham I	gphy = devm_phy_create(dev, NULL, &ops, NULL);
1217597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	if (IS_ERR(gphy))
1227597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter		return PTR_ERR(gphy);
1237597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1247597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	/* The Kona PHY supports an 8-bit wide UTMI interface */
1257597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	phy_set_bus_width(gphy, 8);
1267597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1277597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	phy_set_drvdata(gphy, phy);
1287597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1297597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	phy_provider = devm_of_phy_provider_register(dev,
1307597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter			of_phy_simple_xlate);
1317597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
132e4b9f782fe923e75cf47d369b770c49c33dfb39dSachin Kamat	return PTR_ERR_OR_ZERO(phy_provider);
1337597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter}
1347597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1357597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porterstatic const struct of_device_id bcm_kona_usb2_dt_ids[] = {
1367597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	{ .compatible = "brcm,kona-usb2-phy" },
1377597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	{ /* sentinel */ }
1387597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter};
1397597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1407597fdfca983025a3de91d4b3cf7b21ba452003cMatt PorterMODULE_DEVICE_TABLE(of, bcm_kona_usb2_dt_ids);
1417597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1427597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porterstatic struct platform_driver bcm_kona_usb2_driver = {
1437597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	.probe		= bcm_kona_usb2_probe,
1447597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	.driver		= {
1457597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter		.name	= "bcm-kona-usb2",
1467597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter		.of_match_table = bcm_kona_usb2_dt_ids,
1477597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter	},
1487597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter};
1497597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1507597fdfca983025a3de91d4b3cf7b21ba452003cMatt Portermodule_platform_driver(bcm_kona_usb2_driver);
1517597fdfca983025a3de91d4b3cf7b21ba452003cMatt Porter
1527597fdfca983025a3de91d4b3cf7b21ba452003cMatt PorterMODULE_ALIAS("platform:bcm-kona-usb2");
1537597fdfca983025a3de91d4b3cf7b21ba452003cMatt PorterMODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
1547597fdfca983025a3de91d4b3cf7b21ba452003cMatt PorterMODULE_DESCRIPTION("BCM Kona USB 2.0 PHY driver");
1557597fdfca983025a3de91d4b3cf7b21ba452003cMatt PorterMODULE_LICENSE("GPL v2");
156