phy-samsung-usb.c 5.6 KB
Newer Older
1
/* linux/drivers/usb/phy/phy-samsung-usb.c
2 3 4 5 6 7
 *
 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
 *              http://www.samsung.com
 *
 * Author: Praveen Paneri <p.paneri@samsung.com>
 *
8 9 10
 * Samsung USB-PHY helper driver with common function calls;
 * interacts with Samsung USB 2.0 PHY controller driver and later
 * with Samsung USB 3.0 PHY driver.
11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
25
#include <linux/device.h>
26 27 28
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
29
#include <linux/of_address.h>
30
#include <linux/usb/samsung_usb_phy.h>
31

32
#include "phy-samsung-usb.h"
33

34
int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy)
35 36 37 38 39 40 41 42 43 44 45 46 47 48
{
	struct device_node *usbphy_sys;

	/* Getting node for system controller interface for usb-phy */
	usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys");
	if (!usbphy_sys) {
		dev_err(sphy->dev, "No sys-controller interface for usb-phy\n");
		return -ENODEV;
	}

	sphy->pmuregs = of_iomap(usbphy_sys, 0);

	if (sphy->pmuregs == NULL) {
		dev_err(sphy->dev, "Can't get usb-phy pmu control register\n");
49
		goto err0;
50 51
	}

52 53 54 55 56 57 58 59 60 61 62
	sphy->sysreg = of_iomap(usbphy_sys, 1);

	/*
	 * Not returning error code here, since this situation is not fatal.
	 * Few SoCs may not have this switch available
	 */
	if (sphy->sysreg == NULL)
		dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n");

	of_node_put(usbphy_sys);

63
	return 0;
64 65 66 67

err0:
	of_node_put(usbphy_sys);
	return -ENXIO;
68
}
69
EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt);
70 71 72 73 74 75

/*
 * Set isolation here for phy.
 * Here 'on = true' would mean USB PHY block is isolated, hence
 * de-activated and vice-versa.
 */
76
void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on)
77
{
78
	void __iomem *reg = NULL;
79
	u32 reg_val;
80
	u32 en_mask = 0;
81 82 83 84 85 86

	if (!sphy->pmuregs) {
		dev_warn(sphy->dev, "Can't set pmu isolation\n");
		return;
	}

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
	switch (sphy->drv_data->cpu_type) {
	case TYPE_S3C64XX:
		/*
		 * Do nothing: We will add here once S3C64xx goes for DT support
		 */
		break;
	case TYPE_EXYNOS4210:
		/*
		 * Fall through since exynos4210 and exynos5250 have similar
		 * register architecture: two separate registers for host and
		 * device phy control with enable bit at position 0.
		 */
	case TYPE_EXYNOS5250:
		if (sphy->phy_type == USB_PHY_TYPE_DEVICE) {
			reg = sphy->pmuregs +
				sphy->drv_data->devphy_reg_offset;
			en_mask = sphy->drv_data->devphy_en_mask;
		} else if (sphy->phy_type == USB_PHY_TYPE_HOST) {
			reg = sphy->pmuregs +
				sphy->drv_data->hostphy_reg_offset;
			en_mask = sphy->drv_data->hostphy_en_mask;
		}
		break;
	default:
		dev_err(sphy->dev, "Invalid SoC type\n");
		return;
	}
114 115 116 117 118 119 120 121 122 123

	reg_val = readl(reg);

	if (on)
		reg_val &= ~en_mask;
	else
		reg_val |= en_mask;

	writel(reg_val, reg);
}
124
EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation);
125

126 127 128
/*
 * Configure the mode of working of usb-phy here: HOST/DEVICE.
 */
129
void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy)
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
{
	u32 reg;

	if (!sphy->sysreg) {
		dev_warn(sphy->dev, "Can't configure specified phy mode\n");
		return;
	}

	reg = readl(sphy->sysreg);

	if (sphy->phy_type == USB_PHY_TYPE_DEVICE)
		reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK;
	else if (sphy->phy_type == USB_PHY_TYPE_HOST)
		reg |= EXYNOS_USB20PHY_CFG_HOST_LINK;

	writel(reg, sphy->sysreg);
}
147
EXPORT_SYMBOL_GPL(samsung_usbphy_cfg_sel);
148 149 150 151 152 153

/*
 * PHYs are different for USB Device and USB Host.
 * This make sure that correct PHY type is selected before
 * any operation on PHY.
 */
154
int samsung_usbphy_set_type(struct usb_phy *phy,
155 156 157 158 159 160 161 162
				enum samsung_usb_phy_type phy_type)
{
	struct samsung_usbphy *sphy = phy_to_sphy(phy);

	sphy->phy_type = phy_type;

	return 0;
}
163
EXPORT_SYMBOL_GPL(samsung_usbphy_set_type);
164

165 166 167
/*
 * Returns reference clock frequency selection value
 */
168
int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy)
169 170 171 172
{
	struct clk *ref_clk;
	int refclk_freq = 0;

173 174 175 176 177
	/*
	 * In exynos5250 USB host and device PHY use
	 * external crystal clock XXTI
	 */
	if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250)
178
		ref_clk = devm_clk_get(sphy->dev, "ext_xtal");
179
	else
180
		ref_clk = devm_clk_get(sphy->dev, "xusbxti");
181 182 183 184 185
	if (IS_ERR(ref_clk)) {
		dev_err(sphy->dev, "Failed to get reference clock\n");
		return PTR_ERR(ref_clk);
	}

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
	if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) {
		/* set clock frequency for PLL */
		switch (clk_get_rate(ref_clk)) {
		case 9600 * KHZ:
			refclk_freq = FSEL_CLKSEL_9600K;
			break;
		case 10 * MHZ:
			refclk_freq = FSEL_CLKSEL_10M;
			break;
		case 12 * MHZ:
			refclk_freq = FSEL_CLKSEL_12M;
			break;
		case 19200 * KHZ:
			refclk_freq = FSEL_CLKSEL_19200K;
			break;
		case 20 * MHZ:
			refclk_freq = FSEL_CLKSEL_20M;
			break;
		case 50 * MHZ:
			refclk_freq = FSEL_CLKSEL_50M;
			break;
		case 24 * MHZ:
		default:
			/* default reference clock */
			refclk_freq = FSEL_CLKSEL_24M;
			break;
		}
	} else {
		switch (clk_get_rate(ref_clk)) {
		case 12 * MHZ:
			refclk_freq = PHYCLK_CLKSEL_12M;
			break;
		case 24 * MHZ:
219
			refclk_freq = PHYCLK_CLKSEL_24M;
220 221 222 223 224 225 226 227 228 229 230
			break;
		case 48 * MHZ:
			refclk_freq = PHYCLK_CLKSEL_48M;
			break;
		default:
			if (sphy->drv_data->cpu_type == TYPE_S3C64XX)
				refclk_freq = PHYCLK_CLKSEL_48M;
			else
				refclk_freq = PHYCLK_CLKSEL_24M;
			break;
		}
231 232 233 234 235
	}
	clk_put(ref_clk);

	return refclk_freq;
}
236
EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq);