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 - S5PV210 support
  4 *
  5 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
  6 * Authors: 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 "phy-samsung-usb2.h"
 13
 14/* Exynos USB PHY registers */
 15
 16/* PHY power control */
 17#define S5PV210_UPHYPWR			0x0
 18
 19#define S5PV210_UPHYPWR_PHY0_SUSPEND	BIT(0)
 20#define S5PV210_UPHYPWR_PHY0_PWR	BIT(3)
 21#define S5PV210_UPHYPWR_PHY0_OTG_PWR	BIT(4)
 22#define S5PV210_UPHYPWR_PHY0	( \
 23	S5PV210_UPHYPWR_PHY0_SUSPEND | \
 24	S5PV210_UPHYPWR_PHY0_PWR | \
 25	S5PV210_UPHYPWR_PHY0_OTG_PWR)
 26
 27#define S5PV210_UPHYPWR_PHY1_SUSPEND	BIT(6)
 28#define S5PV210_UPHYPWR_PHY1_PWR	BIT(7)
 29#define S5PV210_UPHYPWR_PHY1 ( \
 30	S5PV210_UPHYPWR_PHY1_SUSPEND | \
 31	S5PV210_UPHYPWR_PHY1_PWR)
 32
 33/* PHY clock control */
 34#define S5PV210_UPHYCLK			0x4
 35
 36#define S5PV210_UPHYCLK_PHYFSEL_MASK	(0x3 << 0)
 37#define S5PV210_UPHYCLK_PHYFSEL_48MHZ	(0x0 << 0)
 38#define S5PV210_UPHYCLK_PHYFSEL_24MHZ	(0x3 << 0)
 39#define S5PV210_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
 40
 41#define S5PV210_UPHYCLK_PHY0_ID_PULLUP	BIT(2)
 42#define S5PV210_UPHYCLK_PHY0_COMMON_ON	BIT(4)
 43#define S5PV210_UPHYCLK_PHY1_COMMON_ON	BIT(7)
 44
 45/* PHY reset control */
 46#define S5PV210_UPHYRST			0x8
 47
 48#define S5PV210_URSTCON_PHY0		BIT(0)
 49#define S5PV210_URSTCON_OTG_HLINK	BIT(1)
 50#define S5PV210_URSTCON_OTG_PHYLINK	BIT(2)
 51#define S5PV210_URSTCON_PHY1_ALL	BIT(3)
 52#define S5PV210_URSTCON_HOST_LINK_ALL	BIT(4)
 53
 54/* Isolation, configured in the power management unit */
 55#define S5PV210_USB_ISOL_OFFSET		0x680c
 56#define S5PV210_USB_ISOL_DEVICE		BIT(0)
 57#define S5PV210_USB_ISOL_HOST		BIT(1)
 58
 59
 60enum s5pv210_phy_id {
 61	S5PV210_DEVICE,
 62	S5PV210_HOST,
 63	S5PV210_NUM_PHYS,
 64};
 65
 66/*
 67 * s5pv210_rate_to_clk() converts the supplied clock rate to the value that
 68 * can be written to the phy register.
 69 */
 70static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg)
 71{
 72	switch (rate) {
 73	case 12 * MHZ:
 74		*reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
 75		break;
 76	case 24 * MHZ:
 77		*reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
 78		break;
 79	case 48 * MHZ:
 80		*reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
 81		break;
 82	default:
 83		return -EINVAL;
 84	}
 85
 86	return 0;
 87}
 88
 89static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on)
 90{
 91	struct samsung_usb2_phy_driver *drv = inst->drv;
 92	u32 mask;
 93
 94	switch (inst->cfg->id) {
 95	case S5PV210_DEVICE:
 96		mask = S5PV210_USB_ISOL_DEVICE;
 97		break;
 98	case S5PV210_HOST:
 99		mask = S5PV210_USB_ISOL_HOST;
100		break;
101	default:
102		return;
103	}
104
105	regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET,
106							mask, on ? 0 : mask);
107}
108
109static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
110{
111	struct samsung_usb2_phy_driver *drv = inst->drv;
112	u32 rstbits = 0;
113	u32 phypwr = 0;
114	u32 rst;
115	u32 pwr;
116
117	switch (inst->cfg->id) {
118	case S5PV210_DEVICE:
119		phypwr =	S5PV210_UPHYPWR_PHY0;
120		rstbits =	S5PV210_URSTCON_PHY0;
121		break;
122	case S5PV210_HOST:
123		phypwr =	S5PV210_UPHYPWR_PHY1;
124		rstbits =	S5PV210_URSTCON_PHY1_ALL |
125				S5PV210_URSTCON_HOST_LINK_ALL;
126		break;
127	}
128
129	if (on) {
130		writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK);
131
132		pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
133		pwr &= ~phypwr;
134		writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
135
136		rst = readl(drv->reg_phy + S5PV210_UPHYRST);
137		rst |= rstbits;
138		writel(rst, drv->reg_phy + S5PV210_UPHYRST);
139		udelay(10);
140		rst &= ~rstbits;
141		writel(rst, drv->reg_phy + S5PV210_UPHYRST);
142		/* The following delay is necessary for the reset sequence to be
143		 * completed
144		 */
145		udelay(80);
146	} else {
147		pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
148		pwr |= phypwr;
149		writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
150	}
151}
152
153static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
154{
155	s5pv210_isol(inst, 0);
156	s5pv210_phy_pwr(inst, 1);
157
158	return 0;
159}
160
161static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
162{
163	s5pv210_phy_pwr(inst, 0);
164	s5pv210_isol(inst, 1);
165
166	return 0;
167}
168
169static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = {
170	[S5PV210_DEVICE] = {
171		.label		= "device",
172		.id		= S5PV210_DEVICE,
173		.power_on	= s5pv210_power_on,
174		.power_off	= s5pv210_power_off,
175	},
176	[S5PV210_HOST] = {
177		.label		= "host",
178		.id		= S5PV210_HOST,
179		.power_on	= s5pv210_power_on,
180		.power_off	= s5pv210_power_off,
181	},
182};
183
184const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
185	.num_phys	= ARRAY_SIZE(s5pv210_phys),
186	.phys		= s5pv210_phys,
187	.rate_to_clk	= s5pv210_rate_to_clk,
188};