Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
  4 *
  5 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
  6 * Author: Kamil Debski <k.debski@samsung.com>
  7 */
  8
  9#include <linux/delay.h>
 10#include <linux/io.h>
 11#include <linux/phy/phy.h>
 12#include <linux/regmap.h>
 13#include "phy-samsung-usb2.h"
 14
 15/* Exynos USB PHY registers */
 16
 17/* PHY power control */
 18#define EXYNOS_4210_UPHYPWR			0x0
 19
 20#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	BIT(0)
 21#define EXYNOS_4210_UPHYPWR_PHY0_PWR		BIT(3)
 22#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	BIT(4)
 23#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		BIT(5)
 24#define EXYNOS_4210_UPHYPWR_PHY0	( \
 25	EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
 26	EXYNOS_4210_UPHYPWR_PHY0_PWR | \
 27	EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
 28	EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
 29
 30#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND	BIT(6)
 31#define EXYNOS_4210_UPHYPWR_PHY1_PWR		BIT(7)
 32#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		BIT(8)
 33#define EXYNOS_4210_UPHYPWR_PHY1 ( \
 34	EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
 35	EXYNOS_4210_UPHYPWR_PHY1_PWR | \
 36	EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
 37
 38#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND	BIT(9)
 39#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP		BIT(10)
 40#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
 41	EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
 42	EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
 43
 44#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND	BIT(11)
 45#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP		BIT(12)
 46#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
 47	EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
 48	EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
 49
 50/* PHY clock control */
 51#define EXYNOS_4210_UPHYCLK			0x4
 52
 53#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK	(0x3 << 0)
 54#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET	0
 55#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ	(0x0 << 0)
 56#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ	(0x3 << 0)
 57#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
 58
 59#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP	BIT(2)
 60#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	BIT(4)
 61#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	BIT(7)
 62
 63/* PHY reset control */
 64#define EXYNOS_4210_UPHYRST			0x8
 65
 66#define EXYNOS_4210_URSTCON_PHY0		BIT(0)
 67#define EXYNOS_4210_URSTCON_OTG_HLINK		BIT(1)
 68#define EXYNOS_4210_URSTCON_OTG_PHYLINK		BIT(2)
 69#define EXYNOS_4210_URSTCON_PHY1_ALL		BIT(3)
 70#define EXYNOS_4210_URSTCON_PHY1_P0		BIT(4)
 71#define EXYNOS_4210_URSTCON_PHY1_P1P2		BIT(5)
 72#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	BIT(6)
 73#define EXYNOS_4210_URSTCON_HOST_LINK_P0	BIT(7)
 74#define EXYNOS_4210_URSTCON_HOST_LINK_P1	BIT(8)
 75#define EXYNOS_4210_URSTCON_HOST_LINK_P2	BIT(9)
 76
 77/* Isolation, configured in the power management unit */
 78#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x704
 79#define EXYNOS_4210_USB_ISOL_DEVICE		BIT(0)
 80#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x708
 81#define EXYNOS_4210_USB_ISOL_HOST		BIT(0)
 82
 83/* USBYPHY1 Floating prevention */
 84#define EXYNOS_4210_UPHY1CON			0x34
 85#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
 86
 87/* Mode switching SUB Device <-> Host */
 88#define EXYNOS_4210_MODE_SWITCH_OFFSET		0x21c
 89#define EXYNOS_4210_MODE_SWITCH_MASK		1
 90#define EXYNOS_4210_MODE_SWITCH_DEVICE		0
 91#define EXYNOS_4210_MODE_SWITCH_HOST		1
 92
 93enum exynos4210_phy_id {
 94	EXYNOS4210_DEVICE,
 95	EXYNOS4210_HOST,
 96	EXYNOS4210_HSIC0,
 97	EXYNOS4210_HSIC1,
 98	EXYNOS4210_NUM_PHYS,
 99};
100
101/*
102 * exynos4210_rate_to_clk() converts the supplied clock rate to the value that
103 * can be written to the phy register.
104 */
105static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
106{
107	switch (rate) {
108	case 12 * MHZ:
109		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
110		break;
111	case 24 * MHZ:
112		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
113		break;
114	case 48 * MHZ:
115		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
116		break;
117	default:
118		return -EINVAL;
119	}
120
121	return 0;
122}
123
124static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
125{
126	struct samsung_usb2_phy_driver *drv = inst->drv;
127	u32 offset;
128	u32 mask;
129
130	switch (inst->cfg->id) {
131	case EXYNOS4210_DEVICE:
132		offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
133		mask = EXYNOS_4210_USB_ISOL_DEVICE;
134		break;
135	case EXYNOS4210_HOST:
136		offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
137		mask = EXYNOS_4210_USB_ISOL_HOST;
138		break;
139	default:
140		return;
141	}
142
143	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
144}
145
146static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
147{
148	struct samsung_usb2_phy_driver *drv = inst->drv;
149	u32 rstbits = 0;
150	u32 phypwr = 0;
151	u32 rst;
152	u32 pwr;
153	u32 clk;
154
155	switch (inst->cfg->id) {
156	case EXYNOS4210_DEVICE:
157		phypwr =	EXYNOS_4210_UPHYPWR_PHY0;
158		rstbits =	EXYNOS_4210_URSTCON_PHY0;
159		break;
160	case EXYNOS4210_HOST:
161		phypwr =	EXYNOS_4210_UPHYPWR_PHY1;
162		rstbits =	EXYNOS_4210_URSTCON_PHY1_ALL |
163				EXYNOS_4210_URSTCON_PHY1_P0 |
164				EXYNOS_4210_URSTCON_PHY1_P1P2 |
165				EXYNOS_4210_URSTCON_HOST_LINK_ALL |
166				EXYNOS_4210_URSTCON_HOST_LINK_P0;
167		writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
168		break;
169	case EXYNOS4210_HSIC0:
170		phypwr =	EXYNOS_4210_UPHYPWR_HSIC0;
171		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
172				EXYNOS_4210_URSTCON_HOST_LINK_P1;
173		break;
174	case EXYNOS4210_HSIC1:
175		phypwr =	EXYNOS_4210_UPHYPWR_HSIC1;
176		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
177				EXYNOS_4210_URSTCON_HOST_LINK_P2;
178		break;
179	}
180
181	if (on) {
182		clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK);
183		clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK;
184		clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET;
185		writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK);
186
187		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
188		pwr &= ~phypwr;
189		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
190
191		rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
192		rst |= rstbits;
193		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
194		udelay(10);
195		rst &= ~rstbits;
196		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
197		/* The following delay is necessary for the reset sequence to be
198		 * completed */
199		udelay(80);
200	} else {
201		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
202		pwr |= phypwr;
203		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
204	}
205}
206
207static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
208{
209	/* Order of initialisation is important - first power then isolation */
210	exynos4210_phy_pwr(inst, 1);
211	exynos4210_isol(inst, 0);
212
213	return 0;
214}
215
216static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
217{
218	exynos4210_isol(inst, 1);
219	exynos4210_phy_pwr(inst, 0);
220
221	return 0;
222}
223
224
225static const struct samsung_usb2_common_phy exynos4210_phys[] = {
226	{
227		.label		= "device",
228		.id		= EXYNOS4210_DEVICE,
229		.power_on	= exynos4210_power_on,
230		.power_off	= exynos4210_power_off,
231	},
232	{
233		.label		= "host",
234		.id		= EXYNOS4210_HOST,
235		.power_on	= exynos4210_power_on,
236		.power_off	= exynos4210_power_off,
237	},
238	{
239		.label		= "hsic0",
240		.id		= EXYNOS4210_HSIC0,
241		.power_on	= exynos4210_power_on,
242		.power_off	= exynos4210_power_off,
243	},
244	{
245		.label		= "hsic1",
246		.id		= EXYNOS4210_HSIC1,
247		.power_on	= exynos4210_power_on,
248		.power_off	= exynos4210_power_off,
249	},
250};
251
252const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
253	.has_mode_switch	= 0,
254	.num_phys		= EXYNOS4210_NUM_PHYS,
255	.phys			= exynos4210_phys,
256	.rate_to_clk		= exynos4210_rate_to_clk,
257};