Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  1/* linux/drivers/usb/phy/phy-samsung-usb.c
  2 *
  3 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
  4 *              http://www.samsung.com
  5 *
  6 * Author: Praveen Paneri <p.paneri@samsung.com>
  7 *
  8 * Samsung USB-PHY helper driver with common function calls;
  9 * interacts with Samsung USB 2.0 PHY controller driver and later
 10 * with Samsung USB 3.0 PHY driver.
 11 *
 12 * This program is free software; you can redistribute it and/or modify
 13 * it under the terms of the GNU General Public License version 2 as
 14 * published by the Free Software Foundation.
 15 *
 16 * This program is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 19 * GNU General Public License for more details.
 20 */
 21
 22#include <linux/module.h>
 23#include <linux/platform_device.h>
 24#include <linux/clk.h>
 25#include <linux/device.h>
 26#include <linux/err.h>
 27#include <linux/io.h>
 28#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
 34int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy)
 35{
 36	struct device_node *usbphy_sys;
 37
 38	/* Getting node for system controller interface for usb-phy */
 39	usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys");
 40	if (!usbphy_sys) {
 41		dev_err(sphy->dev, "No sys-controller interface for usb-phy\n");
 42		return -ENODEV;
 43	}
 44
 45	sphy->pmuregs = of_iomap(usbphy_sys, 0);
 46
 47	if (sphy->pmuregs == NULL) {
 48		dev_err(sphy->dev, "Can't get usb-phy pmu control register\n");
 49		goto err0;
 50	}
 51
 52	sphy->sysreg = of_iomap(usbphy_sys, 1);
 53
 54	/*
 55	 * Not returning error code here, since this situation is not fatal.
 56	 * Few SoCs may not have this switch available
 57	 */
 58	if (sphy->sysreg == NULL)
 59		dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n");
 60
 61	of_node_put(usbphy_sys);
 62
 63	return 0;
 64
 65err0:
 66	of_node_put(usbphy_sys);
 67	return -ENXIO;
 68}
 69EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt);
 70
 71/*
 72 * Set isolation here for phy.
 73 * Here 'on = true' would mean USB PHY block is isolated, hence
 74 * de-activated and vice-versa.
 75 */
 76void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy, bool on)
 77{
 78	void __iomem *reg = NULL;
 79	u32 reg_val;
 80	u32 en_mask = 0;
 81
 82	if (!sphy->pmuregs) {
 83		dev_warn(sphy->dev, "Can't set pmu isolation\n");
 84		return;
 85	}
 86
 87	if (sphy->phy_type == USB_PHY_TYPE_DEVICE) {
 88		reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset;
 89		en_mask = sphy->drv_data->devphy_en_mask;
 90	} else if (sphy->phy_type == USB_PHY_TYPE_HOST) {
 91		reg = sphy->pmuregs + sphy->drv_data->hostphy_reg_offset;
 92		en_mask = sphy->drv_data->hostphy_en_mask;
 93	}
 94
 95	reg_val = readl(reg);
 96
 97	if (on)
 98		reg_val &= ~en_mask;
 99	else
100		reg_val |= en_mask;
101
102	writel(reg_val, reg);
103
104	if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12) {
105		writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL0);
106		writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL1);
107	}
108}
109EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation_4210);
110
111/*
112 * Configure the mode of working of usb-phy here: HOST/DEVICE.
113 */
114void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy)
115{
116	u32 reg;
117
118	if (!sphy->sysreg) {
119		dev_warn(sphy->dev, "Can't configure specified phy mode\n");
120		return;
121	}
122
123	reg = readl(sphy->sysreg);
124
125	if (sphy->phy_type == USB_PHY_TYPE_DEVICE)
126		reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK;
127	else if (sphy->phy_type == USB_PHY_TYPE_HOST)
128		reg |= EXYNOS_USB20PHY_CFG_HOST_LINK;
129
130	writel(reg, sphy->sysreg);
131}
132EXPORT_SYMBOL_GPL(samsung_usbphy_cfg_sel);
133
134/*
135 * PHYs are different for USB Device and USB Host.
136 * This make sure that correct PHY type is selected before
137 * any operation on PHY.
138 */
139int samsung_usbphy_set_type(struct usb_phy *phy,
140				enum samsung_usb_phy_type phy_type)
141{
142	struct samsung_usbphy *sphy = phy_to_sphy(phy);
143
144	sphy->phy_type = phy_type;
145
146	return 0;
147}
148EXPORT_SYMBOL_GPL(samsung_usbphy_set_type);
149
150int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy,
151							unsigned long rate)
152{
153	unsigned int clksel;
154
155	switch (rate) {
156	case 12 * MHZ:
157		clksel = PHYCLK_CLKSEL_12M;
158		break;
159	case 24 * MHZ:
160		clksel = PHYCLK_CLKSEL_24M;
161		break;
162	case 48 * MHZ:
163		clksel = PHYCLK_CLKSEL_48M;
164		break;
165	default:
166		dev_err(sphy->dev,
167			"Invalid reference clock frequency: %lu\n", rate);
168		return -EINVAL;
169	}
170
171	return clksel;
172}
173EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_64xx);
174
175int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy,
176							unsigned long rate)
177{
178	unsigned int clksel;
179
180	switch (rate) {
181	case 9600 * KHZ:
182		clksel = FSEL_CLKSEL_9600K;
183		break;
184	case 10 * MHZ:
185		clksel = FSEL_CLKSEL_10M;
186		break;
187	case 12 * MHZ:
188		clksel = FSEL_CLKSEL_12M;
189		break;
190	case 19200 * KHZ:
191		clksel = FSEL_CLKSEL_19200K;
192		break;
193	case 20 * MHZ:
194		clksel = FSEL_CLKSEL_20M;
195		break;
196	case 24 * MHZ:
197		clksel = FSEL_CLKSEL_24M;
198		break;
199	case 50 * MHZ:
200		clksel = FSEL_CLKSEL_50M;
201		break;
202	default:
203		dev_err(sphy->dev,
204			"Invalid reference clock frequency: %lu\n", rate);
205		return -EINVAL;
206	}
207
208	return clksel;
209}
210EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_4x12);
211
212/*
213 * Returns reference clock frequency selection value
214 */
215int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy)
216{
217	struct clk *ref_clk;
218	unsigned long rate;
219	int refclk_freq;
220
221	/*
222	 * In exynos5250 USB host and device PHY use
223	 * external crystal clock XXTI
224	 */
225	if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250)
226		ref_clk = clk_get(sphy->dev, "ext_xtal");
227	else
228		ref_clk = clk_get(sphy->dev, "xusbxti");
229	if (IS_ERR(ref_clk)) {
230		dev_err(sphy->dev, "Failed to get reference clock\n");
231		return PTR_ERR(ref_clk);
232	}
233
234	rate = clk_get_rate(ref_clk);
235	refclk_freq = sphy->drv_data->rate_to_clksel(sphy, rate);
236
237	clk_put(ref_clk);
238
239	return refclk_freq;
240}
241EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq);