Add new PHY drivers for SATA and USB in exynos, for USB in sunxi,

and a multi-purpose PHY in APM, all adapted to generic PHY framework.
 Adapted USB3 PHY driver in OMAP to generic PHY driver and also used
 the same driver for SATA in OMAP. It also includes miscellaneous cleanups
 and fixes.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJTHBXfAAoJEA5ceFyATYLZiq8P/jp8hqVxRXaJy6UUniggN2vX
 JSdVgj6NPQijp9HAi5llb032coU4WYt7UFAAzOnIkzlQf+oIqu7e75DjGKCnV8sN
 DlxsBTxptD949AGM3UVa1PnTHsJoIxq7HSV7rvpu2V2oH9vO4a2m7dGSHaGUpNei
 ZH73oZtYrD+40eqv3TlUxw7aZpqTIvFvKD2HTJQab0jO06PT/mi7KC0TYXMnT+iK
 GaZTiLkpocQGElz9CI5YzizjZY3zjK7yo9XOdiABx+NjNZ1sjLjc3eLRcqnL3NA0
 0+oK/Gb/ZUuWrPDbUyB71VCi3OseOmNL3oAFIm+i1eV+8m56EKudyTn3jKyIpewG
 Cv1si5t6dkyZELCUp2zi0tJqSiP+cTieO3UELXe2Hs6x0+ilHF6ElHEvgV9JcQHT
 4ETtaN7ppkq35/D5SPxyW6Muqh+Pfxn4Duylp2HQXuqFFblFqIgSUtp1ZmSZIDzI
 qYlVUgRVVZV7Qh+cgSR6YcU1j44os2RK+phlpTTInhun3UJDw/UeNluZhqPX3RRm
 /luBWDuYRdn0h/4IPCS97ImeGBRkYJq5qMzm/JsYvBZBqq4deSOF9rgtwsHx4C5w
 HFBqex3utumv/zNJtuNZ+dQce8olqrUkbPbp9eL8oNJbo5r7PQ1IhCWHTTQiuuiN
 786aeMFxfMsfXVKztT4M
 =mpZ3
 -----END PGP SIGNATURE-----

Merge tag 'for_3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next

Kishon writes:

Add new PHY drivers for SATA and USB in exynos, for USB in sunxi,
and a multi-purpose PHY in APM, all adapted to generic PHY framework.
Adapted USB3 PHY driver in OMAP to generic PHY driver and also used
the same driver for SATA in OMAP. It also includes miscellaneous cleanups
and fixes.
This commit is contained in:
Greg Kroah-Hartman 2014-03-09 11:16:38 -07:00
commit ea1990c379
28 changed files with 4795 additions and 550 deletions

View File

@ -0,0 +1,79 @@
* APM X-Gene 15Gbps Multi-purpose PHY nodes
PHY nodes are defined to describe on-chip 15Gbps Multi-purpose PHY. Each
PHY (pair of lanes) has its own node.
Required properties:
- compatible : Shall be "apm,xgene-phy".
- reg : PHY memory resource is the SDS PHY access resource.
- #phy-cells : Shall be 1 as it expects one argument for setting
the mode of the PHY. Possible values are 0 (SATA),
1 (SGMII), 2 (PCIe), 3 (USB), and 4 (XFI).
Optional properties:
- status : Shall be "ok" if enabled or "disabled" if disabled.
Default is "ok".
- clocks : Reference to the clock entry.
- apm,tx-eye-tuning : Manual control to fine tune the capture of the serial
bit lines from the automatic calibrated position.
Two set of 3-tuple setting for each (up to 3)
supported link speed on the host. Range from 0 to
127 in unit of one bit period. Default is 10.
- apm,tx-eye-direction : Eye tuning manual control direction. 0 means sample
data earlier than the nominal sampling point. 1 means
sample data later than the nominal sampling point.
Two set of 3-tuple setting for each (up to 3)
supported link speed on the host. Default is 0.
- apm,tx-boost-gain : Frequency boost AC (LSB 3-bit) and DC (2-bit)
gain control. Two set of 3-tuple setting for each
(up to 3) supported link speed on the host. Range is
between 0 to 31 in unit of dB. Default is 3.
- apm,tx-amplitude : Amplitude control. Two set of 3-tuple setting for
each (up to 3) supported link speed on the host.
Range is between 0 to 199500 in unit of uV.
Default is 199500 uV.
- apm,tx-pre-cursor1 : 1st pre-cursor emphasis taps control. Two set of
3-tuple setting for each (up to 3) supported link
speed on the host. Range is 0 to 273000 in unit of
uV. Default is 0.
- apm,tx-pre-cursor2 : 2st pre-cursor emphasis taps control. Two set of
3-tuple setting for each (up to 3) supported link
speed on the host. Range is 0 to 127400 in unit uV.
Default is 0x0.
- apm,tx-post-cursor : Post-cursor emphasis taps control. Two set of
3-tuple setting for Gen1, Gen2, and Gen3. Range is
between 0 to 0x1f in unit of 18.2mV. Default is 0xf.
- apm,tx-speed : Tx operating speed. One set of 3-tuple for each
supported link speed on the host.
0 = 1-2Gbps
1 = 2-4Gbps (1st tuple default)
2 = 4-8Gbps
3 = 8-15Gbps (2nd tuple default)
4 = 2.5-4Gbps
5 = 4-5Gbps
6 = 5-6Gbps
7 = 6-16Gbps (3rd tuple default)
NOTE: PHY override parameters are board specific setting.
Example:
phy1: phy@1f21a000 {
compatible = "apm,xgene-phy";
reg = <0x0 0x1f21a000 0x0 0x100>;
#phy-cells = <1>;
status = "disabled";
};
phy2: phy@1f22a000 {
compatible = "apm,xgene-phy";
reg = <0x0 0x1f22a000 0x0 0x100>;
#phy-cells = <1>;
status = "ok";
};
phy3: phy@1f23a000 {
compatible = "apm,xgene-phy";
reg = <0x0 0x1f23a000 0x0 0x100>;
#phy-cells = <1>;
status = "ok";
};

View File

@ -20,3 +20,57 @@ Required properties:
- compatible : should be "samsung,exynos5250-dp-video-phy";
- reg : offset and length of the Display Port PHY register set;
- #phy-cells : from the generic PHY bindings, must be 0;
Samsung S5P/EXYNOS SoC series USB PHY
-------------------------------------------------
Required properties:
- compatible : should be one of the listed compatibles:
- "samsung,exynos4210-usb2-phy"
- "samsung,exynos4x12-usb2-phy"
- "samsung,exynos5250-usb2-phy"
- reg : a list of registers used by phy driver
- first and obligatory is the location of phy modules registers
- samsung,sysreg-phandle - handle to syscon used to control the system registers
- samsung,pmureg-phandle - handle to syscon used to control PMU registers
- #phy-cells : from the generic phy bindings, must be 1;
- clocks and clock-names:
- the "phy" clock is required by the phy module, used as a gate
- the "ref" clock is used to get the rate of the clock provided to the
PHY module
The first phandle argument in the PHY specifier identifies the PHY, its
meaning is compatible dependent. For the currently supported SoCs (Exynos 4210
and Exynos 4212) it is as follows:
0 - USB device ("device"),
1 - USB host ("host"),
2 - HSIC0 ("hsic0"),
3 - HSIC1 ("hsic1"),
Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
register is supplied.
Example:
For Exynos 4412 (compatible with Exynos 4212):
usbphy: phy@125b0000 {
compatible = "samsung,exynos4x12-usb2-phy";
reg = <0x125b0000 0x100>;
clocks = <&clock 305>, <&clock 2>;
clock-names = "phy", "ref";
status = "okay";
#phy-cells = <1>;
samsung,sysreg-phandle = <&sys_reg>;
samsung,pmureg-phandle = <&pmu_reg>;
};
Then the PHY can be used in other nodes such as:
phy-consumer@12340000 {
phys = <&usbphy 2>;
phy-names = "phy";
};
Refer to DT bindings documentation of particular PHY consumer devices for more
information about required PHYs and the way of specification.

View File

@ -0,0 +1,26 @@
Allwinner sun4i USB PHY
-----------------------
Required properties:
- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
"allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
- reg : a list of offset + length pairs
- reg-names : "phy_ctrl", "pmu1" and for sun4i or sun7i "pmu2"
- #phy-cells : from the generic phy bindings, must be 1
- clocks : phandle + clock specifier for the phy clock
- clock-names : "usb_phy"
- resets : a list of phandle + reset specifier pairs
- reset-names : "usb0_reset", "usb1_reset" and for sun4i or sun7i "usb2_reset"
Example:
usbphy: phy@0x01c13400 {
#phy-cells = <1>;
compatible = "allwinner,sun4i-a10-usb-phy";
/* phy base regs, phy1 pmu reg, phy2 pmu reg */
reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
reg-names = "phy_ctrl", "pmu1", "pmu2";
clocks = <&usb_clk 8>;
clock-names = "usb_phy";
resets = <&usb_clk 1>, <&usb_clk 2>;
reset-names = "usb1_reset", "usb2_reset";
};

View File

@ -0,0 +1,135 @@
.------------------------------------------------------------------------------+
| Samsung USB 2.0 PHY adaptation layer |
+-----------------------------------------------------------------------------+'
| 1. Description
+----------------
The architecture of the USB 2.0 PHY module in Samsung SoCs is similar
among many SoCs. In spite of the similarities it proved difficult to
create a one driver that would fit all these PHY controllers. Often
the differences were minor and were found in particular bits of the
registers of the PHY. In some rare cases the order of register writes or
the PHY powering up process had to be altered. This adaptation layer is
a compromise between having separate drivers and having a single driver
with added support for many special cases.
| 2. Files description
+----------------------
- phy-samsung-usb2.c
This is the main file of the adaptation layer. This file contains
the probe function and provides two callbacks to the Generic PHY
Framework. This two callbacks are used to power on and power off the
phy. They carry out the common work that has to be done on all version
of the PHY module. Depending on which SoC was chosen they execute SoC
specific callbacks. The specific SoC version is selected by choosing
the appropriate compatible string. In addition, this file contains
struct of_device_id definitions for particular SoCs.
- phy-samsung-usb2.h
This is the include file. It declares the structures used by this
driver. In addition it should contain extern declarations for
structures that describe particular SoCs.
| 3. Supporting SoCs
+--------------------
To support a new SoC a new file should be added to the drivers/phy
directory. Each SoC's configuration is stored in an instance of the
struct samsung_usb2_phy_config.
struct samsung_usb2_phy_config {
const struct samsung_usb2_common_phy *phys;
int (*rate_to_clk)(unsigned long, u32 *);
unsigned int num_phys;
bool has_mode_switch;
};
The num_phys is the number of phys handled by the driver. *phys is an
array that contains the configuration for each phy. The has_mode_switch
property is a boolean flag that determines whether the SoC has USB host
and device on a single pair of pins. If so, a special register has to
be modified to change the internal routing of these pins between a USB
device or host module.
For example the configuration for Exynos 4210 is following:
const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
.has_mode_switch = 0,
.num_phys = EXYNOS4210_NUM_PHYS,
.phys = exynos4210_phys,
.rate_to_clk = exynos4210_rate_to_clk,
}
- int (*rate_to_clk)(unsigned long, u32 *)
The rate_to_clk callback is to convert the rate of the clock
used as the reference clock for the PHY module to the value
that should be written in the hardware register.
The exynos4210_phys configuration array is as follows:
static const struct samsung_usb2_common_phy exynos4210_phys[] = {
{
.label = "device",
.id = EXYNOS4210_DEVICE,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "host",
.id = EXYNOS4210_HOST,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "hsic0",
.id = EXYNOS4210_HSIC0,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "hsic1",
.id = EXYNOS4210_HSIC1,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{},
};
- int (*power_on)(struct samsung_usb2_phy_instance *);
- int (*power_off)(struct samsung_usb2_phy_instance *);
These two callbacks are used to power on and power off the phy
by modifying appropriate registers.
Final change to the driver is adding appropriate compatible value to the
phy-samsung-usb2.c file. In case of Exynos 4210 the following lines were
added to the struct of_device_id samsung_usb2_phy_of_match[] array:
#ifdef CONFIG_PHY_EXYNOS4210_USB2
{
.compatible = "samsung,exynos4210-usb2-phy",
.data = &exynos4210_usb2_phy_config,
},
#endif
To add further flexibility to the driver the Kconfig file enables to
include support for selected SoCs in the compiled driver. The Kconfig
entry for Exynos 4210 is following:
config PHY_EXYNOS4210_USB2
bool "Support for Exynos 4210"
depends on PHY_SAMSUNG_USB2
depends on CPU_EXYNOS4210
help
Enable USB PHY support for Exynos 4210. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 4210 four
phys are available - device, host, HSCI0 and HSCI1.
The newly created file that supports the new SoC has to be also added to the
Makefile. In case of Exynos 4210 the added line is following:
obj-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o
After completing these steps the support for the new SoC should be ready.

View File

@ -16,30 +16,56 @@ config GENERIC_PHY
framework should select this config.
config PHY_EXYNOS_MIPI_VIDEO
depends on HAS_IOMEM
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
depends on HAS_IOMEM
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
select GENERIC_PHY
default y if ARCH_S5PV210 || ARCH_EXYNOS
help
Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
and EXYNOS SoCs.
config PHY_MVEBU_SATA
def_bool y
depends on ARCH_KIRKWOOD || ARCH_DOVE
depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE
depends on OF
select GENERIC_PHY
config OMAP_CONTROL_PHY
tristate "OMAP CONTROL PHY Driver"
help
Enable this to add support for the PHY part present in the control
module. This driver has API to power on the USB2 PHY and to write to
the mailbox. The mailbox is present only in omap4 and the register to
power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
additional register to power on USB3 PHY/SATA PHY/PCIE PHY
(PIPE3 PHY).
config OMAP_USB2
tristate "OMAP USB2 PHY Driver"
depends on ARCH_OMAP2PLUS
depends on USB_PHY
select GENERIC_PHY
select OMAP_CONTROL_USB
select OMAP_CONTROL_PHY
depends on OMAP_OCP2SCP
help
Enable this to support the transceiver that is part of SOC. This
driver takes care of all the PHY functionality apart from comparator.
The USB OTG controller communicates with the comparator using this
driver.
config TI_PIPE3
tristate "TI PIPE3 PHY Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
select GENERIC_PHY
select OMAP_CONTROL_PHY
depends on OMAP_OCP2SCP
help
Enable this to support the PIPE3 PHY that is part of TI SOCs. This
driver takes care of all the PHY functionality apart from comparator.
This driver interacts with the "OMAP Control PHY Driver" to power
on/off the PHY.
config TWL4030_USB
tristate "TWL4030 USB Transceiver Driver"
depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
@ -54,6 +80,8 @@ config TWL4030_USB
config PHY_EXYNOS_DP_VIDEO
tristate "EXYNOS SoC series Display Port PHY driver"
depends on OF
depends on ARCH_EXYNOS || COMPILE_TEST
default ARCH_EXYNOS
select GENERIC_PHY
help
Support for Display Port PHY found on Samsung EXYNOS SoCs.
@ -65,4 +93,77 @@ config BCM_KONA_USB2_PHY
help
Enable this to support the Broadcom Kona USB 2.0 PHY.
config PHY_EXYNOS5250_SATA
tristate "Exynos5250 Sata SerDes/PHY driver"
depends on SOC_EXYNOS5250
depends on HAS_IOMEM
depends on OF
select GENERIC_PHY
select I2C
select I2C_S3C2410
select MFD_SYSCON
help
Enable this to support SATA SerDes/Phy found on Samsung's
Exynos5250 based SoCs.This SerDes/Phy supports SATA 1.5 Gb/s,
SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host
port to accept one SATA device.
config PHY_SUN4I_USB
tristate "Allwinner sunxi SoC USB PHY driver"
depends on ARCH_SUNXI && HAS_IOMEM && OF
select GENERIC_PHY
help
Enable this to support the transceiver that is part of Allwinner
sunxi SoCs.
This driver controls the entire USB PHY block, both the USB OTG
parts, as well as the 2 regular USB 2 host PHYs.
config PHY_SAMSUNG_USB2
tristate "Samsung USB 2.0 PHY driver"
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support the Samsung USB 2.0 PHY driver for Samsung
SoCs. This driver provides the interface for USB 2.0 PHY. Support for
particular SoCs has to be enabled in addition to this driver. Number
and type of supported phys depends on the SoC.
config PHY_EXYNOS4210_USB2
bool "Support for Exynos 4210"
depends on PHY_SAMSUNG_USB2
depends on CPU_EXYNOS4210
help
Enable USB PHY support for Exynos 4210. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 4210 four
phys are available - device, host, HSIC0 and HSIC1.
config PHY_EXYNOS4X12_USB2
bool "Support for Exynos 4x12"
depends on PHY_SAMSUNG_USB2
depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
help
Enable USB PHY support for Exynos 4x12. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 4x12 four
phys are available - device, host, HSIC0 and HSIC1.
config PHY_EXYNOS5250_USB2
bool "Support for Exynos 5250"
depends on PHY_SAMSUNG_USB2
depends on SOC_EXYNOS5250
help
Enable USB PHY support for Exynos 5250. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 5250 four
phys are available - device, host, HSIC0 and HSIC.
config PHY_XGENE
tristate "APM X-Gene 15Gbps PHY support"
depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST)
select GENERIC_PHY
help
This option enables support for APM X-Gene SoC multi-purpose PHY.
endmenu

View File

@ -7,5 +7,14 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-samsung-usb2.o
obj-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o
obj-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o
obj-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o

View File

@ -128,10 +128,8 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
phy_provider = devm_of_phy_provider_register(dev,
of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id bcm_kona_usb2_dt_ids[] = {

View File

@ -274,8 +274,8 @@ int phy_power_off(struct phy *phy)
EXPORT_SYMBOL_GPL(phy_power_off);
/**
* of_phy_get() - lookup and obtain a reference to a phy by phandle
* @dev: device that requests this phy
* _of_phy_get() - lookup and obtain a reference to a phy by phandle
* @np: device_node for which to get the phy
* @index: the index of the phy
*
* Returns the phy associated with the given phandle value,
@ -284,20 +284,17 @@ EXPORT_SYMBOL_GPL(phy_power_off);
* not yet loaded. This function uses of_xlate call back function provided
* while registering the phy_provider to find the phy instance.
*/
static struct phy *of_phy_get(struct device *dev, int index)
static struct phy *_of_phy_get(struct device_node *np, int index)
{
int ret;
struct phy_provider *phy_provider;
struct phy *phy = NULL;
struct of_phandle_args args;
ret = of_parse_phandle_with_args(dev->of_node, "phys", "#phy-cells",
ret = of_parse_phandle_with_args(np, "phys", "#phy-cells",
index, &args);
if (ret) {
dev_dbg(dev, "failed to get phy in %s node\n",
dev->of_node->full_name);
if (ret)
return ERR_PTR(-ENODEV);
}
mutex_lock(&phy_provider_mutex);
phy_provider = of_phy_provider_lookup(args.np);
@ -316,6 +313,36 @@ static struct phy *of_phy_get(struct device *dev, int index)
return phy;
}
/**
* of_phy_get() - lookup and obtain a reference to a phy using a device_node.
* @np: device_node for which to get the phy
* @con_id: name of the phy from device's point of view
*
* Returns the phy driver, after getting a refcount to it; or
* -ENODEV if there is no such phy. The caller is responsible for
* calling phy_put() to release that count.
*/
struct phy *of_phy_get(struct device_node *np, const char *con_id)
{
struct phy *phy = NULL;
int index = 0;
if (con_id)
index = of_property_match_string(np, "phy-names", con_id);
phy = _of_phy_get(np, index);
if (IS_ERR(phy))
return phy;
if (!try_module_get(phy->ops->owner))
return ERR_PTR(-EPROBE_DEFER);
get_device(&phy->dev);
return phy;
}
EXPORT_SYMBOL_GPL(of_phy_get);
/**
* phy_put() - release the PHY
* @phy: the phy returned by phy_get()
@ -407,7 +434,7 @@ struct phy *phy_get(struct device *dev, const char *string)
if (dev->of_node) {
index = of_property_match_string(dev->of_node, "phy-names",
string);
phy = of_phy_get(dev, index);
phy = _of_phy_get(dev->of_node, index);
} else {
phy = phy_lookup(dev, string);
}
@ -498,6 +525,37 @@ struct phy *devm_phy_optional_get(struct device *dev, const char *string)
}
EXPORT_SYMBOL_GPL(devm_phy_optional_get);
/**
* devm_of_phy_get() - lookup and obtain a reference to a phy.
* @dev: device that requests this phy
* @np: node containing the phy
* @con_id: name of the phy from device's point of view
*
* Gets the phy using of_phy_get(), and associates a device with it using
* devres. On driver detach, release function is invoked on the devres data,
* then, devres data is freed.
*/
struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
const char *con_id)
{
struct phy **ptr, *phy;
ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
phy = of_phy_get(np, con_id);
if (!IS_ERR(phy)) {
*ptr = phy;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return phy;
}
EXPORT_SYMBOL_GPL(devm_of_phy_get);
/**
* phy_create() - create a new phy
* @dev: device that is creating the new phy

View File

@ -0,0 +1,261 @@
/*
* Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Kamil Debski <k.debski@samsung.com>
*
* 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.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include "phy-samsung-usb2.h"
/* Exynos USB PHY registers */
/* PHY power control */
#define EXYNOS_4210_UPHYPWR 0x0
#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0)
#define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3)
#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4)
#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5)
#define EXYNOS_4210_UPHYPWR_PHY0 ( \
EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
EXYNOS_4210_UPHYPWR_PHY0_PWR | \
EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6)
#define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7)
#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8)
#define EXYNOS_4210_UPHYPWR_PHY1 ( \
EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
EXYNOS_4210_UPHYPWR_PHY1_PWR | \
EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9)
#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10)
#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11)
#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12)
#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
/* PHY clock control */
#define EXYNOS_4210_UPHYCLK 0x4
#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0)
#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0
#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0)
#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0)
#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2)
#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4)
#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7)
/* PHY reset control */
#define EXYNOS_4210_UPHYRST 0x8
#define EXYNOS_4210_URSTCON_PHY0 BIT(0)
#define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1)
#define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2)
#define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3)
#define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4)
#define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5)
#define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6)
#define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7)
#define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8)
#define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9)
/* Isolation, configured in the power management unit */
#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704
#define EXYNOS_4210_USB_ISOL_DEVICE BIT(0)
#define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708
#define EXYNOS_4210_USB_ISOL_HOST BIT(0)
/* USBYPHY1 Floating prevention */
#define EXYNOS_4210_UPHY1CON 0x34
#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1
/* Mode switching SUB Device <-> Host */
#define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c
#define EXYNOS_4210_MODE_SWITCH_MASK 1
#define EXYNOS_4210_MODE_SWITCH_DEVICE 0
#define EXYNOS_4210_MODE_SWITCH_HOST 1
enum exynos4210_phy_id {
EXYNOS4210_DEVICE,
EXYNOS4210_HOST,
EXYNOS4210_HSIC0,
EXYNOS4210_HSIC1,
EXYNOS4210_NUM_PHYS,
};
/*
* exynos4210_rate_to_clk() converts the supplied clock rate to the value that
* can be written to the phy register.
*/
static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
{
switch (rate) {
case 12 * MHZ:
*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
break;
case 24 * MHZ:
*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
break;
case 48 * MHZ:
*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
break;
default:
return -EINVAL;
}
return 0;
}
static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 offset;
u32 mask;
switch (inst->cfg->id) {
case EXYNOS4210_DEVICE:
offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
mask = EXYNOS_4210_USB_ISOL_DEVICE;
break;
case EXYNOS4210_HOST:
offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
mask = EXYNOS_4210_USB_ISOL_HOST;
break;
default:
return;
};
regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
}
static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 rstbits = 0;
u32 phypwr = 0;
u32 rst;
u32 pwr;
u32 clk;
switch (inst->cfg->id) {
case EXYNOS4210_DEVICE:
phypwr = EXYNOS_4210_UPHYPWR_PHY0;
rstbits = EXYNOS_4210_URSTCON_PHY0;
break;
case EXYNOS4210_HOST:
phypwr = EXYNOS_4210_UPHYPWR_PHY1;
rstbits = EXYNOS_4210_URSTCON_PHY1_ALL |
EXYNOS_4210_URSTCON_PHY1_P0 |
EXYNOS_4210_URSTCON_PHY1_P1P2 |
EXYNOS_4210_URSTCON_HOST_LINK_ALL |
EXYNOS_4210_URSTCON_HOST_LINK_P0;
writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
break;
case EXYNOS4210_HSIC0:
phypwr = EXYNOS_4210_UPHYPWR_HSIC0;
rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 |
EXYNOS_4210_URSTCON_HOST_LINK_P1;
break;
case EXYNOS4210_HSIC1:
phypwr = EXYNOS_4210_UPHYPWR_HSIC1;
rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 |
EXYNOS_4210_URSTCON_HOST_LINK_P2;
break;
};
if (on) {
clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK);
clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK;
clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET;
writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK);
pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
pwr &= ~phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
rst |= rstbits;
writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
udelay(10);
rst &= ~rstbits;
writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
/* The following delay is necessary for the reset sequence to be
* completed */
udelay(80);
} else {
pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
pwr |= phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
}
}
static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
{
/* Order of initialisation is important - first power then isolation */
exynos4210_phy_pwr(inst, 1);
exynos4210_isol(inst, 0);
return 0;
}
static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
{
exynos4210_isol(inst, 1);
exynos4210_phy_pwr(inst, 0);
return 0;
}
static const struct samsung_usb2_common_phy exynos4210_phys[] = {
{
.label = "device",
.id = EXYNOS4210_DEVICE,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "host",
.id = EXYNOS4210_HOST,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "hsic0",
.id = EXYNOS4210_HSIC0,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "hsic1",
.id = EXYNOS4210_HSIC1,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{},
};
const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
.has_mode_switch = 0,
.num_phys = EXYNOS4210_NUM_PHYS,
.phys = exynos4210_phys,
.rate_to_clk = exynos4210_rate_to_clk,
};

View File

@ -0,0 +1,328 @@
/*
* Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Kamil Debski <k.debski@samsung.com>
*
* 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.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include "phy-samsung-usb2.h"
/* Exynos USB PHY registers */
/* PHY power control */
#define EXYNOS_4x12_UPHYPWR 0x0
#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND BIT(0)
#define EXYNOS_4x12_UPHYPWR_PHY0_PWR BIT(3)
#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR BIT(4)
#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP BIT(5)
#define EXYNOS_4x12_UPHYPWR_PHY0 ( \
EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \
EXYNOS_4x12_UPHYPWR_PHY0_PWR | \
EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \
EXYNOS_4x12_UPHYPWR_PHY0_SLEEP)
#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND BIT(6)
#define EXYNOS_4x12_UPHYPWR_PHY1_PWR BIT(7)
#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP BIT(8)
#define EXYNOS_4x12_UPHYPWR_PHY1 ( \
EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \
EXYNOS_4x12_UPHYPWR_PHY1_PWR | \
EXYNOS_4x12_UPHYPWR_PHY1_SLEEP)
#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND BIT(9)
#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR BIT(10)
#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP BIT(11)
#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \
EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \
EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \
EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP)
#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND BIT(12)
#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR BIT(13)
#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP BIT(14)
#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \
EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \
EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \
EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP)
/* PHY clock control */
#define EXYNOS_4x12_UPHYCLK 0x4
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK (0x7 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET 0
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6 (0x0 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ (0x1 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2 (0x3 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ (0x4 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0)
#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3)
#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4)
#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK (0x7f << 10)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET 10
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ (0x24 << 10)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ (0x1c << 10)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ (0x1a << 10)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ (0x14 << 10)
/* PHY reset control */
#define EXYNOS_4x12_UPHYRST 0x8
#define EXYNOS_4x12_URSTCON_PHY0 BIT(0)
#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1)
#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2)
#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3)
#define EXYNOS_4x12_URSTCON_PHY1 BIT(4)
#define EXYNOS_4x12_URSTCON_HSIC0 BIT(5)
#define EXYNOS_4x12_URSTCON_HSIC1 BIT(6)
#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7)
#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8)
#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9)
#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10)
/* Isolation, configured in the power management unit */
#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704
#define EXYNOS_4x12_USB_ISOL_OTG BIT(0)
#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET 0x708
#define EXYNOS_4x12_USB_ISOL_HSIC0 BIT(0)
#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET 0x70c
#define EXYNOS_4x12_USB_ISOL_HSIC1 BIT(0)
/* Mode switching SUB Device <-> Host */
#define EXYNOS_4x12_MODE_SWITCH_OFFSET 0x21c
#define EXYNOS_4x12_MODE_SWITCH_MASK 1
#define EXYNOS_4x12_MODE_SWITCH_DEVICE 0
#define EXYNOS_4x12_MODE_SWITCH_HOST 1
enum exynos4x12_phy_id {
EXYNOS4x12_DEVICE,
EXYNOS4x12_HOST,
EXYNOS4x12_HSIC0,
EXYNOS4x12_HSIC1,
EXYNOS4x12_NUM_PHYS,
};
/*
* exynos4x12_rate_to_clk() converts the supplied clock rate to the value that
* can be written to the phy register.
*/
static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg)
{
/* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */
switch (rate) {
case 9600 * KHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6;
break;
case 10 * MHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ;
break;
case 12 * MHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ;
break;
case 19200 * KHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2;
break;
case 20 * MHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ;
break;
case 24 * MHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ;
break;
case 50 * MHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ;
break;
default:
return -EINVAL;
}
return 0;
}
static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 offset;
u32 mask;
switch (inst->cfg->id) {
case EXYNOS4x12_DEVICE:
case EXYNOS4x12_HOST:
offset = EXYNOS_4x12_USB_ISOL_OFFSET;
mask = EXYNOS_4x12_USB_ISOL_OTG;
break;
case EXYNOS4x12_HSIC0:
offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET;
mask = EXYNOS_4x12_USB_ISOL_HSIC0;
break;
case EXYNOS4x12_HSIC1:
offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET;
mask = EXYNOS_4x12_USB_ISOL_HSIC1;
break;
default:
return;
};
regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
}
static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 clk;
clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
}
static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 rstbits = 0;
u32 phypwr = 0;
u32 rst;
u32 pwr;
u32 mode = 0;
u32 switch_mode = 0;
switch (inst->cfg->id) {
case EXYNOS4x12_DEVICE:
phypwr = EXYNOS_4x12_UPHYPWR_PHY0;
rstbits = EXYNOS_4x12_URSTCON_PHY0;
mode = EXYNOS_4x12_MODE_SWITCH_DEVICE;
switch_mode = 1;
break;
case EXYNOS4x12_HOST:
phypwr = EXYNOS_4x12_UPHYPWR_PHY1;
rstbits = EXYNOS_4x12_URSTCON_HOST_PHY;
mode = EXYNOS_4x12_MODE_SWITCH_HOST;
switch_mode = 1;
break;
case EXYNOS4x12_HSIC0:
phypwr = EXYNOS_4x12_UPHYPWR_HSIC0;
rstbits = EXYNOS_4x12_URSTCON_HSIC1 |
EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
EXYNOS_4x12_URSTCON_HOST_PHY;
break;
case EXYNOS4x12_HSIC1:
phypwr = EXYNOS_4x12_UPHYPWR_HSIC1;
rstbits = EXYNOS_4x12_URSTCON_HSIC1 |
EXYNOS_4x12_URSTCON_HOST_LINK_P1;
break;
};
if (on) {
if (switch_mode)
regmap_update_bits(drv->reg_sys,
EXYNOS_4x12_MODE_SWITCH_OFFSET,
EXYNOS_4x12_MODE_SWITCH_MASK, mode);
pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
pwr &= ~phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST);
rst |= rstbits;
writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
udelay(10);
rst &= ~rstbits;
writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
/* The following delay is necessary for the reset sequence to be
* completed */
udelay(80);
} else {
pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
pwr |= phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
}
}
static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
inst->enabled = 1;
exynos4x12_setup_clk(inst);
exynos4x12_phy_pwr(inst, 1);
exynos4x12_isol(inst, 0);
/* Power on the device, as it is necessary for HSIC to work */
if (inst->cfg->id == EXYNOS4x12_HSIC0) {
struct samsung_usb2_phy_instance *device =
&drv->instances[EXYNOS4x12_DEVICE];
exynos4x12_phy_pwr(device, 1);
exynos4x12_isol(device, 0);
}
return 0;
}
static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
struct samsung_usb2_phy_instance *device =
&drv->instances[EXYNOS4x12_DEVICE];
inst->enabled = 0;
exynos4x12_isol(inst, 1);
exynos4x12_phy_pwr(inst, 0);
if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
exynos4x12_isol(device, 1);
exynos4x12_phy_pwr(device, 0);
}
return 0;
}
static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
{
.label = "device",
.id = EXYNOS4x12_DEVICE,
.power_on = exynos4x12_power_on,
.power_off = exynos4x12_power_off,
},
{
.label = "host",
.id = EXYNOS4x12_HOST,
.power_on = exynos4x12_power_on,
.power_off = exynos4x12_power_off,
},
{
.label = "hsic0",
.id = EXYNOS4x12_HSIC0,
.power_on = exynos4x12_power_on,
.power_off = exynos4x12_power_off,
},
{
.label = "hsic1",
.id = EXYNOS4x12_HSIC1,
.power_on = exynos4x12_power_on,
.power_off = exynos4x12_power_off,
},
{},
};
const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
.has_mode_switch = 1,
.num_phys = EXYNOS4x12_NUM_PHYS,
.phys = exynos4x12_phys,
.rate_to_clk = exynos4x12_rate_to_clk,
};

View File

@ -0,0 +1,251 @@
/*
* Samsung SATA SerDes(PHY) driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Authors: Girish K S <ks.giri@samsung.com>
* Yuvaraj Kumar C D <yuvaraj.cd@samsung.com>
*
* 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.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/mfd/syscon.h>
#define SATAPHY_CONTROL_OFFSET 0x0724
#define EXYNOS5_SATAPHY_PMU_ENABLE BIT(0)
#define EXYNOS5_SATA_RESET 0x4
#define RESET_GLOBAL_RST_N BIT(0)
#define RESET_CMN_RST_N BIT(1)
#define RESET_CMN_BLOCK_RST_N BIT(2)
#define RESET_CMN_I2C_RST_N BIT(3)
#define RESET_TX_RX_PIPE_RST_N BIT(4)
#define RESET_TX_RX_BLOCK_RST_N BIT(5)
#define RESET_TX_RX_I2C_RST_N (BIT(6) | BIT(7))
#define LINK_RESET 0xf0000
#define EXYNOS5_SATA_MODE0 0x10
#define SATA_SPD_GEN3 BIT(1)
#define EXYNOS5_SATA_CTRL0 0x14
#define CTRL0_P0_PHY_CALIBRATED_SEL BIT(9)
#define CTRL0_P0_PHY_CALIBRATED BIT(8)
#define EXYNOS5_SATA_PHSATA_CTRLM 0xe0
#define PHCTRLM_REF_RATE BIT(1)
#define PHCTRLM_HIGH_SPEED BIT(0)
#define EXYNOS5_SATA_PHSATA_STATM 0xf0
#define PHSTATM_PLL_LOCKED BIT(0)
#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000))
struct exynos_sata_phy {
struct phy *phy;
struct clk *phyclk;
void __iomem *regs;
struct regmap *pmureg;
struct i2c_client *client;
};
static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
u32 status)
{
unsigned long timeout = jiffies + PHY_PLL_TIMEOUT;
while (time_before(jiffies, timeout)) {
if ((readl(base + reg) & checkbit) == status)
return 0;
}
return -EFAULT;
}
static int exynos_sata_phy_power_on(struct phy *phy)
{
struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
EXYNOS5_SATAPHY_PMU_ENABLE, true);
}
static int exynos_sata_phy_power_off(struct phy *phy)
{
struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
EXYNOS5_SATAPHY_PMU_ENABLE, false);
}
static int exynos_sata_phy_init(struct phy *phy)
{
u32 val = 0;
int ret = 0;
u8 buf[] = { 0x3a, 0x0b };
struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
EXYNOS5_SATAPHY_PMU_ENABLE, true);
if (ret != 0)
dev_err(&sata_phy->phy->dev, "phy init failed\n");
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N
| RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N
| RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N;
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
val |= LINK_RESET;
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
val |= RESET_CMN_RST_N;
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
val &= ~PHCTRLM_REF_RATE;
writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
/* High speed enable for Gen3 */
val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
val |= PHCTRLM_HIGH_SPEED;
writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0);
val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED;
writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0);
val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0);
val |= SATA_SPD_GEN3;
writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0);
ret = i2c_master_send(sata_phy->client, buf, sizeof(buf));
if (ret < 0)
return ret;
/* release cmu reset */
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
val &= ~RESET_CMN_RST_N;
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
val |= RESET_CMN_RST_N;
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
ret = wait_for_reg_status(sata_phy->regs,
EXYNOS5_SATA_PHSATA_STATM,
PHSTATM_PLL_LOCKED, 1);
if (ret < 0)
dev_err(&sata_phy->phy->dev,
"PHY PLL locking failed\n");
return ret;
}
static struct phy_ops exynos_sata_phy_ops = {
.init = exynos_sata_phy_init,
.power_on = exynos_sata_phy_power_on,
.power_off = exynos_sata_phy_power_off,
.owner = THIS_MODULE,
};
static int exynos_sata_phy_probe(struct platform_device *pdev)
{
struct exynos_sata_phy *sata_phy;
struct device *dev = &pdev->dev;
struct resource *res;
struct phy_provider *phy_provider;
struct device_node *node;
int ret = 0;
sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL);
if (!sata_phy)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sata_phy->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(sata_phy->regs))
return PTR_ERR(sata_phy->regs);
sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,syscon-phandle");
if (IS_ERR(sata_phy->pmureg)) {
dev_err(dev, "syscon regmap lookup failed.\n");
return PTR_ERR(sata_phy->pmureg);
}
node = of_parse_phandle(dev->of_node,
"samsung,exynos-sataphy-i2c-phandle", 0);
if (!node)
return -EINVAL;
sata_phy->client = of_find_i2c_device_by_node(node);
if (!sata_phy->client)
return -EPROBE_DEFER;
dev_set_drvdata(dev, sata_phy);
sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
if (IS_ERR(sata_phy->phyclk)) {
dev_err(dev, "failed to get clk for PHY\n");
return PTR_ERR(sata_phy->phyclk);
}
ret = clk_prepare_enable(sata_phy->phyclk);
if (ret < 0) {
dev_err(dev, "failed to enable source clk\n");
return ret;
}
sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL);
if (IS_ERR(sata_phy->phy)) {
clk_disable_unprepare(sata_phy->phyclk);
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(sata_phy->phy);
}
phy_set_drvdata(sata_phy->phy, sata_phy);
phy_provider = devm_of_phy_provider_register(dev,
of_phy_simple_xlate);
if (IS_ERR(phy_provider)) {
clk_disable_unprepare(sata_phy->phyclk);
return PTR_ERR(phy_provider);
}
return 0;
}
static const struct of_device_id exynos_sata_phy_of_match[] = {
{ .compatible = "samsung,exynos5250-sata-phy" },
{ },
};
MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match);
static struct platform_driver exynos_sata_phy_driver = {
.probe = exynos_sata_phy_probe,
.driver = {
.of_match_table = exynos_sata_phy_of_match,
.name = "samsung,sata-phy",
.owner = THIS_MODULE,
}
};
module_platform_driver(exynos_sata_phy_driver);
MODULE_DESCRIPTION("Samsung SerDes PHY driver");
MODULE_LICENSE("GPL V2");
MODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
MODULE_AUTHOR("Yuvaraj C D <yuvaraj.cd@samsung.com>");

View File

@ -0,0 +1,404 @@
/*
* Samsung SoC USB 1.1/2.0 PHY driver - Exynos 5250 support
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Kamil Debski <k.debski@samsung.com>
*
* 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.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include "phy-samsung-usb2.h"
/* Exynos USB PHY registers */
#define EXYNOS_5250_REFCLKSEL_CRYSTAL 0x0
#define EXYNOS_5250_REFCLKSEL_XO 0x1
#define EXYNOS_5250_REFCLKSEL_CLKCORE 0x2
#define EXYNOS_5250_FSEL_9MHZ6 0x0
#define EXYNOS_5250_FSEL_10MHZ 0x1
#define EXYNOS_5250_FSEL_12MHZ 0x2
#define EXYNOS_5250_FSEL_19MHZ2 0x3
#define EXYNOS_5250_FSEL_20MHZ 0x4
#define EXYNOS_5250_FSEL_24MHZ 0x5
#define EXYNOS_5250_FSEL_50MHZ 0x7
/* Normal host */
#define EXYNOS_5250_HOSTPHYCTRL0 0x0
#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL BIT(31)
#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT 19
#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_MASK \
(0x3 << EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT)
#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT 16
#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK \
(0x7 << EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT)
#define EXYNOS_5250_HOSTPHYCTRL0_TESTBURNIN BIT(11)
#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE BIT(10)
#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N BIT(9)
#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_MASK (0x3 << 7)
#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_DUAL (0x0 << 7)
#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ID0 (0x1 << 7)
#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ANALOGTEST (0x2 << 7)
#define EXYNOS_5250_HOSTPHYCTRL0_SIDDQ BIT(6)
#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP BIT(5)
#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND BIT(4)
#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE BIT(3)
#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST BIT(2)
#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST BIT(1)
#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST BIT(0)
/* HSIC0 & HSIC1 */
#define EXYNOS_5250_HSICPHYCTRL1 0x10
#define EXYNOS_5250_HSICPHYCTRL2 0x20
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_MASK (0x3 << 23)
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT (0x2 << 23)
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_MASK (0x7f << 16)
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 (0x24 << 16)
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_15 (0x1c << 16)
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_16 (0x1a << 16)
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_19_2 (0x15 << 16)
#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_20 (0x14 << 16)
#define EXYNOS_5250_HSICPHYCTRLX_SIDDQ BIT(6)
#define EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP BIT(5)
#define EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND BIT(4)
#define EXYNOS_5250_HSICPHYCTRLX_WORDINTERFACE BIT(3)
#define EXYNOS_5250_HSICPHYCTRLX_UTMISWRST BIT(2)
#define EXYNOS_5250_HSICPHYCTRLX_PHYSWRST BIT(0)
/* EHCI control */
#define EXYNOS_5250_HOSTEHCICTRL 0x30
#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN BIT(29)
#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 BIT(28)
#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 BIT(27)
#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16 BIT(26)
#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN BIT(25)
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT 19
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \
(0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT)
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT 13
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_MASK \
(0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT)
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL2_SHIFT 7
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \
(0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT)
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT 1
#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_MASK \
(0x1 << EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT)
#define EXYNOS_5250_HOSTEHCICTRL_SIMULATIONMODE BIT(0)
/* OHCI control */
#define EXYNOS_5250_HOSTOHCICTRL 0x34
#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT 1
#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_MASK \
(0x3ff << EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT)
#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVALEN BIT(0)
/* USBOTG */
#define EXYNOS_5250_USBOTGSYS 0x38
#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET BIT(14)
#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG BIT(13)
#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST BIT(12)
#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT 9
#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK \
(0x3 << EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT)
#define EXYNOS_5250_USBOTGSYS_ID_PULLUP BIT(8)
#define EXYNOS_5250_USBOTGSYS_COMMON_ON BIT(7)
#define EXYNOS_5250_USBOTGSYS_FSEL_SHIFT 4
#define EXYNOS_5250_USBOTGSYS_FSEL_MASK \
(0x3 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT)
#define EXYNOS_5250_USBOTGSYS_FORCE_SLEEP BIT(3)
#define EXYNOS_5250_USBOTGSYS_OTGDISABLE BIT(2)
#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG BIT(1)
#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND BIT(0)
/* Isolation, configured in the power management unit */
#define EXYNOS_5250_USB_ISOL_OTG_OFFSET 0x704
#define EXYNOS_5250_USB_ISOL_OTG BIT(0)
#define EXYNOS_5250_USB_ISOL_HOST_OFFSET 0x708
#define EXYNOS_5250_USB_ISOL_HOST BIT(0)
/* Mode swtich register */
#define EXYNOS_5250_MODE_SWITCH_OFFSET 0x230
#define EXYNOS_5250_MODE_SWITCH_MASK 1
#define EXYNOS_5250_MODE_SWITCH_DEVICE 0
#define EXYNOS_5250_MODE_SWITCH_HOST 1
enum exynos4x12_phy_id {
EXYNOS5250_DEVICE,
EXYNOS5250_HOST,
EXYNOS5250_HSIC0,
EXYNOS5250_HSIC1,
EXYNOS5250_NUM_PHYS,
};
/*
* exynos5250_rate_to_clk() converts the supplied clock rate to the value that
* can be written to the phy register.
*/
static int exynos5250_rate_to_clk(unsigned long rate, u32 *reg)
{
/* EXYNOS_5250_FSEL_MASK */
switch (rate) {
case 9600 * KHZ:
*reg = EXYNOS_5250_FSEL_9MHZ6;
break;
case 10 * MHZ:
*reg = EXYNOS_5250_FSEL_10MHZ;
break;
case 12 * MHZ:
*reg = EXYNOS_5250_FSEL_12MHZ;
break;
case 19200 * KHZ:
*reg = EXYNOS_5250_FSEL_19MHZ2;
break;
case 20 * MHZ:
*reg = EXYNOS_5250_FSEL_20MHZ;
break;
case 24 * MHZ:
*reg = EXYNOS_5250_FSEL_24MHZ;
break;
case 50 * MHZ:
*reg = EXYNOS_5250_FSEL_50MHZ;
break;
default:
return -EINVAL;
}
return 0;
}
static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 offset;
u32 mask;
switch (inst->cfg->id) {
case EXYNOS5250_DEVICE:
offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET;
mask = EXYNOS_5250_USB_ISOL_OTG;
break;
case EXYNOS5250_HOST:
offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET;
mask = EXYNOS_5250_USB_ISOL_HOST;
break;
default:
return;
};
regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
}
static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 ctrl0;
u32 otg;
u32 ehci;
u32 ohci;
u32 hsic;
switch (inst->cfg->id) {
case EXYNOS5250_DEVICE:
regmap_update_bits(drv->reg_sys,
EXYNOS_5250_MODE_SWITCH_OFFSET,
EXYNOS_5250_MODE_SWITCH_MASK,
EXYNOS_5250_MODE_SWITCH_DEVICE);
/* OTG configuration */
otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
/* The clock */
otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
/* Reset */
otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
EXYNOS_5250_USBOTGSYS_OTGDISABLE;
/* Ref clock */
otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK;
otg |= EXYNOS_5250_REFCLKSEL_CLKCORE <<
EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT;
writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
udelay(100);
otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
EXYNOS_5250_USBOTGSYS_OTGDISABLE);
writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
break;
case EXYNOS5250_HOST:
case EXYNOS5250_HSIC0:
case EXYNOS5250_HSIC1:
/* Host registers configuration */
ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
/* The clock */
ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
ctrl0 |= drv->ref_reg_val <<
EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT;
/* Reset */
ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST |
EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL |
EXYNOS_5250_HOSTPHYCTRL0_SIDDQ |
EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND |
EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP);
ctrl0 |= EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST |
EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST |
EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N;
writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
udelay(10);
ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST |
EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST);
writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
/* OTG configuration */
otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
/* The clock */
otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
/* Reset */
otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
EXYNOS_5250_USBOTGSYS_OTGDISABLE;
/* Ref clock */
otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK;
otg |= EXYNOS_5250_REFCLKSEL_CLKCORE <<
EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT;
writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
udelay(10);
otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET);
/* HSIC phy configuration */
hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 |
EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT |
EXYNOS_5250_HSICPHYCTRLX_PHYSWRST);
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
udelay(10);
hsic &= ~EXYNOS_5250_HSICPHYCTRLX_PHYSWRST;
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
/* The following delay is necessary for the reset sequence to be
* completed */
udelay(80);
/* Enable EHCI DMA burst */
ehci = readl(drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL);
ehci |= EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN |
EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 |
EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 |
EXYNOS_5250_HOSTEHCICTRL_ENAINCR16;
writel(ehci, drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL);
/* OHCI settings */
ohci = readl(drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
/* Following code is based on the old driver */
ohci |= 0x1 << 3;
writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
break;
}
inst->enabled = 1;
exynos5250_isol(inst, 0);
return 0;
}
static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 ctrl0;
u32 otg;
u32 hsic;
inst->enabled = 0;
exynos5250_isol(inst, 1);
switch (inst->cfg->id) {
case EXYNOS5250_DEVICE:
otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
otg |= (EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG |
EXYNOS_5250_USBOTGSYS_FORCE_SLEEP);
writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
break;
case EXYNOS5250_HOST:
ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
ctrl0 |= (EXYNOS_5250_HOSTPHYCTRL0_SIDDQ |
EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND |
EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP |
EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST |
EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL);
writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
break;
case EXYNOS5250_HSIC0:
case EXYNOS5250_HSIC1:
hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 |
EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT |
EXYNOS_5250_HSICPHYCTRLX_SIDDQ |
EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP |
EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND
);
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
break;
}
return 0;
}
static const struct samsung_usb2_common_phy exynos5250_phys[] = {
{
.label = "device",
.id = EXYNOS5250_DEVICE,
.power_on = exynos5250_power_on,
.power_off = exynos5250_power_off,
},
{
.label = "host",
.id = EXYNOS5250_HOST,
.power_on = exynos5250_power_on,
.power_off = exynos5250_power_off,
},
{
.label = "hsic0",
.id = EXYNOS5250_HSIC0,
.power_on = exynos5250_power_on,
.power_off = exynos5250_power_off,
},
{
.label = "hsic1",
.id = EXYNOS5250_HSIC1,
.power_on = exynos5250_power_on,
.power_off = exynos5250_power_off,
},
{},
};
const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = {
.has_mode_switch = 1,
.num_phys = EXYNOS5250_NUM_PHYS,
.phys = exynos5250_phys,
.rate_to_clk = exynos5250_rate_to_clk,
};

View File

@ -1,5 +1,5 @@
/*
* omap-control-usb.c - The USB part of control module.
* omap-control-phy.c - The PHY part of control module.
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
* This program is free software; you can redistribute it and/or modify
@ -24,36 +24,36 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/usb/omap_control_usb.h>
#include <linux/phy/omap_control_phy.h>
/**
* omap_control_usb_phy_power - power on/off the phy using control module reg
* omap_control_phy_power - power on/off the phy using control module reg
* @dev: the control module device
* @on: 0 or 1, based on powering on or off the PHY
*/
void omap_control_usb_phy_power(struct device *dev, int on)
void omap_control_phy_power(struct device *dev, int on)
{
u32 val;
unsigned long rate;
struct omap_control_usb *control_usb;
struct omap_control_phy *control_phy;
if (IS_ERR(dev) || !dev) {
pr_err("%s: invalid device\n", __func__);
return;
}
control_usb = dev_get_drvdata(dev);
if (!control_usb) {
dev_err(dev, "%s: invalid control usb device\n", __func__);
control_phy = dev_get_drvdata(dev);
if (!control_phy) {
dev_err(dev, "%s: invalid control phy device\n", __func__);
return;
}
if (control_usb->type == OMAP_CTRL_TYPE_OTGHS)
if (control_phy->type == OMAP_CTRL_TYPE_OTGHS)
return;
val = readl(control_usb->power);
val = readl(control_phy->power);
switch (control_usb->type) {
switch (control_phy->type) {
case OMAP_CTRL_TYPE_USB2:
if (on)
val &= ~OMAP_CTRL_DEV_PHY_PD;
@ -62,19 +62,20 @@ void omap_control_usb_phy_power(struct device *dev, int on)
break;
case OMAP_CTRL_TYPE_PIPE3:
rate = clk_get_rate(control_usb->sys_clk);
rate = clk_get_rate(control_phy->sys_clk);
rate = rate/1000000;
if (on) {
val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK |
OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK);
val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON <<
OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT;
val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK);
val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON <<
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
val |= rate <<
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
} else {
val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK;
val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF <<
OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK;
val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF <<
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
}
break;
@ -100,66 +101,66 @@ void omap_control_usb_phy_power(struct device *dev, int on)
break;
default:
dev_err(dev, "%s: type %d not recognized\n",
__func__, control_usb->type);
__func__, control_phy->type);
break;
}
writel(val, control_usb->power);
writel(val, control_phy->power);
}
EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
EXPORT_SYMBOL_GPL(omap_control_phy_power);
/**
* omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
* @ctrl_usb: struct omap_control_usb *
* @ctrl_phy: struct omap_control_phy *
*
* Writes to the mailbox register to notify the usb core that a usb
* device has been connected.
*/
static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb)
static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy)
{
u32 val;
val = readl(ctrl_usb->otghs_control);
val = readl(ctrl_phy->otghs_control);
val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND);
val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
writel(val, ctrl_usb->otghs_control);
writel(val, ctrl_phy->otghs_control);
}
/**
* omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
* impedance
* @ctrl_usb: struct omap_control_usb *
* @ctrl_phy: struct omap_control_phy *
*
* Writes to the mailbox register to notify the usb core that it has been
* connected to a usb host.
*/
static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb)
static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy)
{
u32 val;
val = readl(ctrl_usb->otghs_control);
val = readl(ctrl_phy->otghs_control);
val &= ~OMAP_CTRL_DEV_SESSEND;
val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID |
OMAP_CTRL_DEV_VBUSVALID;
writel(val, ctrl_usb->otghs_control);
writel(val, ctrl_phy->otghs_control);
}
/**
* omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
* impedance
* @ctrl_usb: struct omap_control_usb *
* @ctrl_phy: struct omap_control_phy *
*
* Writes to the mailbox register to notify the usb core it's now in
* disconnected state.
*/
static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb)
static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy)
{
u32 val;
val = readl(ctrl_usb->otghs_control);
val = readl(ctrl_phy->otghs_control);
val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID);
val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND;
writel(val, ctrl_usb->otghs_control);
writel(val, ctrl_phy->otghs_control);
}
/**
@ -174,30 +175,30 @@ static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb)
void omap_control_usb_set_mode(struct device *dev,
enum omap_control_usb_mode mode)
{
struct omap_control_usb *ctrl_usb;
struct omap_control_phy *ctrl_phy;
if (IS_ERR(dev) || !dev)
return;
ctrl_usb = dev_get_drvdata(dev);
ctrl_phy = dev_get_drvdata(dev);
if (!ctrl_usb) {
dev_err(dev, "Invalid control usb device\n");
if (!ctrl_phy) {
dev_err(dev, "Invalid control phy device\n");
return;
}
if (ctrl_usb->type != OMAP_CTRL_TYPE_OTGHS)
if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS)
return;
switch (mode) {
case USB_MODE_HOST:
omap_control_usb_host_mode(ctrl_usb);
omap_control_usb_host_mode(ctrl_phy);
break;
case USB_MODE_DEVICE:
omap_control_usb_device_mode(ctrl_usb);
omap_control_usb_device_mode(ctrl_phy);
break;
case USB_MODE_DISCONNECT:
omap_control_usb_set_sessionend(ctrl_usb);
omap_control_usb_set_sessionend(ctrl_phy);
break;
default:
dev_vdbg(dev, "invalid omap control usb mode\n");
@ -207,13 +208,13 @@ EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
#ifdef CONFIG_OF
static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2;
static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
static const enum omap_control_usb_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2;
static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
static const struct of_device_id omap_control_usb_id_table[] = {
static const struct of_device_id omap_control_phy_id_table[] = {
{
.compatible = "ti,control-phy-otghs",
.data = &otghs_data,
@ -227,93 +228,93 @@ static const struct of_device_id omap_control_usb_id_table[] = {
.data = &pipe3_data,
},
{
.compatible = "ti,control-phy-dra7usb2",
.compatible = "ti,control-phy-usb2-dra7",
.data = &dra7usb2_data,
},
{
.compatible = "ti,control-phy-am437usb2",
.compatible = "ti,control-phy-usb2-am437",
.data = &am437usb2_data,
},
{},
};
MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);
#endif
static int omap_control_usb_probe(struct platform_device *pdev)
static int omap_control_phy_probe(struct platform_device *pdev)
{
struct resource *res;
const struct of_device_id *of_id;
struct omap_control_usb *control_usb;
struct omap_control_phy *control_phy;
of_id = of_match_device(of_match_ptr(omap_control_usb_id_table),
&pdev->dev);
of_id = of_match_device(of_match_ptr(omap_control_phy_id_table),
&pdev->dev);
if (!of_id)
return -EINVAL;
control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy),
GFP_KERNEL);
if (!control_usb) {
dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
if (!control_phy) {
dev_err(&pdev->dev, "unable to alloc memory for control phy\n");
return -ENOMEM;
}
control_usb->dev = &pdev->dev;
control_usb->type = *(enum omap_control_usb_type *)of_id->data;
control_phy->dev = &pdev->dev;
control_phy->type = *(enum omap_control_phy_type *)of_id->data;
if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) {
if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"otghs_control");
control_usb->otghs_control = devm_ioremap_resource(
control_phy->otghs_control = devm_ioremap_resource(
&pdev->dev, res);
if (IS_ERR(control_usb->otghs_control))
return PTR_ERR(control_usb->otghs_control);
if (IS_ERR(control_phy->otghs_control))
return PTR_ERR(control_phy->otghs_control);
} else {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"power");
control_usb->power = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(control_usb->power)) {
control_phy->power = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(control_phy->power)) {
dev_err(&pdev->dev, "Couldn't get power register\n");
return PTR_ERR(control_usb->power);
return PTR_ERR(control_phy->power);
}
}
if (control_usb->type == OMAP_CTRL_TYPE_PIPE3) {
control_usb->sys_clk = devm_clk_get(control_usb->dev,
if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) {
control_phy->sys_clk = devm_clk_get(control_phy->dev,
"sys_clkin");
if (IS_ERR(control_usb->sys_clk)) {
if (IS_ERR(control_phy->sys_clk)) {
pr_err("%s: unable to get sys_clkin\n", __func__);
return -EINVAL;
}
}
dev_set_drvdata(control_usb->dev, control_usb);
dev_set_drvdata(control_phy->dev, control_phy);
return 0;
}
static struct platform_driver omap_control_usb_driver = {
.probe = omap_control_usb_probe,
static struct platform_driver omap_control_phy_driver = {
.probe = omap_control_phy_probe,
.driver = {
.name = "omap-control-usb",
.name = "omap-control-phy",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(omap_control_usb_id_table),
.of_match_table = of_match_ptr(omap_control_phy_id_table),
},
};
static int __init omap_control_usb_init(void)
static int __init omap_control_phy_init(void)
{
return platform_driver_register(&omap_control_usb_driver);
return platform_driver_register(&omap_control_phy_driver);
}
subsys_initcall(omap_control_usb_init);
subsys_initcall(omap_control_phy_init);
static void __exit omap_control_usb_exit(void)
static void __exit omap_control_phy_exit(void)
{
platform_driver_unregister(&omap_control_usb_driver);
platform_driver_unregister(&omap_control_phy_driver);
}
module_exit(omap_control_usb_exit);
module_exit(omap_control_phy_exit);
MODULE_ALIAS("platform: omap_control_usb");
MODULE_ALIAS("platform: omap_control_phy");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("OMAP Control Module USB Driver");
MODULE_DESCRIPTION("OMAP Control Module PHY Driver");
MODULE_LICENSE("GPL v2");

View File

@ -21,16 +21,19 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/usb/omap_usb.h>
#include <linux/phy/omap_usb.h>
#include <linux/usb/phy_companion.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/usb/omap_control_usb.h>
#include <linux/phy/omap_control_phy.h>
#include <linux/phy/phy.h>
#include <linux/of_platform.h>
#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
#define USB2PHY_ANA_CONFIG1 0x4c
/**
* omap_usb2_set_comparator - links the comparator present in the sytem with
* this phy
@ -98,33 +101,11 @@ static int omap_usb_set_peripheral(struct usb_otg *otg,
return 0;
}
static int omap_usb2_suspend(struct usb_phy *x, int suspend)
{
struct omap_usb *phy = phy_to_omapusb(x);
int ret;
if (suspend && !phy->is_suspended) {
omap_control_usb_phy_power(phy->control_dev, 0);
pm_runtime_put_sync(phy->dev);
phy->is_suspended = 1;
} else if (!suspend && phy->is_suspended) {
ret = pm_runtime_get_sync(phy->dev);
if (ret < 0) {
dev_err(phy->dev, "get_sync failed with err %d\n", ret);
return ret;
}
omap_control_usb_phy_power(phy->control_dev, 1);
phy->is_suspended = 0;
}
return 0;
}
static int omap_usb_power_off(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
omap_control_usb_phy_power(phy->control_dev, 0);
omap_control_phy_power(phy->control_dev, 0);
return 0;
}
@ -133,30 +114,103 @@ static int omap_usb_power_on(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
omap_control_usb_phy_power(phy->control_dev, 1);
omap_control_phy_power(phy->control_dev, 1);
return 0;
}
static int omap_usb_init(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
u32 val;
if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
/*
*
* Reduce the sensitivity of internal PHY by enabling the
* DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
* resolves issues with certain devices which can otherwise
* be prone to false disconnects.
*
*/
val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1);
val |= USB2PHY_DISCON_BYP_LATCH;
omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val);
}
return 0;
}
static struct phy_ops ops = {
.init = omap_usb_init,
.power_on = omap_usb_power_on,
.power_off = omap_usb_power_off,
.owner = THIS_MODULE,
};
#ifdef CONFIG_OF
static const struct usb_phy_data omap_usb2_data = {
.label = "omap_usb2",
.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
};
static const struct usb_phy_data omap5_usb2_data = {
.label = "omap5_usb2",
.flags = 0,
};
static const struct usb_phy_data dra7x_usb2_data = {
.label = "dra7x_usb2",
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
};
static const struct usb_phy_data am437x_usb2_data = {
.label = "am437x_usb2",
.flags = 0,
};
static const struct of_device_id omap_usb2_id_table[] = {
{
.compatible = "ti,omap-usb2",
.data = &omap_usb2_data,
},
{
.compatible = "ti,omap5-usb2",
.data = &omap5_usb2_data,
},
{
.compatible = "ti,dra7x-usb2",
.data = &dra7x_usb2_data,
},
{
.compatible = "ti,am437x-usb2",
.data = &am437x_usb2_data,
},
{},
};
MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
#endif
static int omap_usb2_probe(struct platform_device *pdev)
{
struct omap_usb *phy;
struct phy *generic_phy;
struct resource *res;
struct phy_provider *phy_provider;
struct usb_otg *otg;
struct device_node *node = pdev->dev.of_node;
struct device_node *control_node;
struct platform_device *control_pdev;
const struct of_device_id *of_id;
struct usb_phy_data *phy_data;
if (!node)
of_id = of_match_device(of_match_ptr(omap_usb2_id_table), &pdev->dev);
if (!of_id)
return -EINVAL;
phy_data = (struct usb_phy_data *)of_id->data;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy) {
dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
@ -172,11 +226,18 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->dev = &pdev->dev;
phy->phy.dev = phy->dev;
phy->phy.label = "omap-usb2";
phy->phy.set_suspend = omap_usb2_suspend;
phy->phy.label = phy_data->label;
phy->phy.otg = otg;
phy->phy.type = USB_PHY_TYPE_USB2;
if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
phy->phy_base = devm_ioremap_resource(&pdev->dev, res);
if (!phy->phy_base)
return -ENOMEM;
phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
}
control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) {
dev_err(&pdev->dev, "Failed to get control device phandle\n");
@ -190,14 +251,14 @@ static int omap_usb2_probe(struct platform_device *pdev)
}
phy->control_dev = &control_pdev->dev;
phy->is_suspended = 1;
omap_control_usb_phy_power(phy->control_dev, 0);
omap_control_phy_power(phy->control_dev, 0);
otg->set_host = omap_usb_set_host;
otg->set_peripheral = omap_usb_set_peripheral;
otg->set_vbus = omap_usb_set_vbus;
otg->start_srp = omap_usb_start_srp;
if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
otg->set_vbus = omap_usb_set_vbus;
if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
otg->start_srp = omap_usb_start_srp;
otg->phy = &phy->phy;
platform_set_drvdata(pdev, phy);
@ -297,14 +358,6 @@ static const struct dev_pm_ops omap_usb2_pm_ops = {
#define DEV_PM_OPS NULL
#endif
#ifdef CONFIG_OF
static const struct of_device_id omap_usb2_id_table[] = {
{ .compatible = "ti,omap-usb2" },
{}
};
MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
#endif
static struct platform_driver omap_usb2_driver = {
.probe = omap_usb2_probe,
.remove = omap_usb2_remove,

View File

@ -0,0 +1,228 @@
/*
* Samsung SoC USB 1.1/2.0 PHY driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Kamil Debski <k.debski@samsung.com>
*
* 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.
*/
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include "phy-samsung-usb2.h"
static int samsung_usb2_phy_power_on(struct phy *phy)
{
struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
struct samsung_usb2_phy_driver *drv = inst->drv;
int ret;
dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
inst->cfg->label);
ret = clk_prepare_enable(drv->clk);
if (ret)
goto err_main_clk;
ret = clk_prepare_enable(drv->ref_clk);
if (ret)
goto err_instance_clk;
if (inst->cfg->power_on) {
spin_lock(&drv->lock);
ret = inst->cfg->power_on(inst);
spin_unlock(&drv->lock);
}
return 0;
err_instance_clk:
clk_disable_unprepare(drv->clk);
err_main_clk:
return ret;
}
static int samsung_usb2_phy_power_off(struct phy *phy)
{
struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
struct samsung_usb2_phy_driver *drv = inst->drv;
int ret = 0;
dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
inst->cfg->label);
if (inst->cfg->power_off) {
spin_lock(&drv->lock);
ret = inst->cfg->power_off(inst);
spin_unlock(&drv->lock);
}
clk_disable_unprepare(drv->ref_clk);
clk_disable_unprepare(drv->clk);
return ret;
}
static struct phy_ops samsung_usb2_phy_ops = {
.power_on = samsung_usb2_phy_power_on,
.power_off = samsung_usb2_phy_power_off,
.owner = THIS_MODULE,
};
static struct phy *samsung_usb2_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct samsung_usb2_phy_driver *drv;
drv = dev_get_drvdata(dev);
if (!drv)
return ERR_PTR(-EINVAL);
if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
return ERR_PTR(-ENODEV);
return drv->instances[args->args[0]].phy;
}
static const struct of_device_id samsung_usb2_phy_of_match[] = {
#ifdef CONFIG_PHY_EXYNOS4210_USB2
{
.compatible = "samsung,exynos4210-usb2-phy",
.data = &exynos4210_usb2_phy_config,
},
#endif
#ifdef CONFIG_PHY_EXYNOS4X12_USB2
{
.compatible = "samsung,exynos4x12-usb2-phy",
.data = &exynos4x12_usb2_phy_config,
},
#endif
#ifdef CONFIG_PHY_EXYNOS5250_USB2
{
.compatible = "samsung,exynos5250-usb2-phy",
.data = &exynos5250_usb2_phy_config,
},
#endif
{ },
};
static int samsung_usb2_phy_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
const struct samsung_usb2_phy_config *cfg;
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
struct resource *mem;
struct samsung_usb2_phy_driver *drv;
int i, ret;
if (!pdev->dev.of_node) {
dev_err(dev, "This driver is required to be instantiated from device tree\n");
return -EINVAL;
}
match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node);
if (!match) {
dev_err(dev, "of_match_node() failed\n");
return -EINVAL;
}
cfg = match->data;
drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
GFP_KERNEL);
if (!drv)
return -ENOMEM;
dev_set_drvdata(dev, drv);
spin_lock_init(&drv->lock);
drv->cfg = cfg;
drv->dev = dev;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
drv->reg_phy = devm_ioremap_resource(dev, mem);
if (IS_ERR(drv->reg_phy)) {
dev_err(dev, "Failed to map register memory (phy)\n");
return PTR_ERR(drv->reg_phy);
}
drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"samsung,pmureg-phandle");
if (IS_ERR(drv->reg_pmu)) {
dev_err(dev, "Failed to map PMU registers (via syscon)\n");
return PTR_ERR(drv->reg_pmu);
}
if (drv->cfg->has_mode_switch) {
drv->reg_sys = syscon_regmap_lookup_by_phandle(
pdev->dev.of_node, "samsung,sysreg-phandle");
if (IS_ERR(drv->reg_sys)) {
dev_err(dev, "Failed to map system registers (via syscon)\n");
return PTR_ERR(drv->reg_sys);
}
}
drv->clk = devm_clk_get(dev, "phy");
if (IS_ERR(drv->clk)) {
dev_err(dev, "Failed to get clock of phy controller\n");
return PTR_ERR(drv->clk);
}
drv->ref_clk = devm_clk_get(dev, "ref");
if (IS_ERR(drv->ref_clk)) {
dev_err(dev, "Failed to get reference clock for the phy controller\n");
return PTR_ERR(drv->ref_clk);
}
drv->ref_rate = clk_get_rate(drv->ref_clk);
if (drv->cfg->rate_to_clk) {
ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val);
if (ret)
return ret;
}
for (i = 0; i < drv->cfg->num_phys; i++) {
char *label = drv->cfg->phys[i].label;
struct samsung_usb2_phy_instance *p = &drv->instances[i];
dev_dbg(dev, "Creating phy \"%s\"\n", label);
p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
if (IS_ERR(p->phy)) {
dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
label);
return PTR_ERR(p->phy);
}
p->cfg = &drv->cfg->phys[i];
p->drv = drv;
phy_set_bus_width(p->phy, 8);
phy_set_drvdata(p->phy, p);
}
phy_provider = devm_of_phy_provider_register(dev,
samsung_usb2_phy_xlate);
if (IS_ERR(phy_provider)) {
dev_err(drv->dev, "Failed to register phy provider\n");
return PTR_ERR(phy_provider);
}
return 0;
}
static struct platform_driver samsung_usb2_phy_driver = {
.probe = samsung_usb2_phy_probe,
.driver = {
.of_match_table = samsung_usb2_phy_of_match,
.name = "samsung-usb2-phy",
.owner = THIS_MODULE,
}
};
module_platform_driver(samsung_usb2_phy_driver);
MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:samsung-usb2-phy");

View File

@ -0,0 +1,67 @@
/*
* Samsung SoC USB 1.1/2.0 PHY driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Kamil Debski <k.debski@samsung.com>
*
* 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.
*/
#ifndef _PHY_EXYNOS_USB2_H
#define _PHY_EXYNOS_USB2_H
#include <linux/clk.h>
#include <linux/phy/phy.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#define KHZ 1000
#define MHZ (KHZ * KHZ)
struct samsung_usb2_phy_driver;
struct samsung_usb2_phy_instance;
struct samsung_usb2_phy_config;
struct samsung_usb2_phy_instance {
const struct samsung_usb2_common_phy *cfg;
struct phy *phy;
struct samsung_usb2_phy_driver *drv;
bool enabled;
};
struct samsung_usb2_phy_driver {
const struct samsung_usb2_phy_config *cfg;
struct clk *clk;
struct clk *ref_clk;
unsigned long ref_rate;
u32 ref_reg_val;
struct device *dev;
void __iomem *reg_phy;
struct regmap *reg_pmu;
struct regmap *reg_sys;
spinlock_t lock;
struct samsung_usb2_phy_instance instances[0];
};
struct samsung_usb2_common_phy {
int (*power_on)(struct samsung_usb2_phy_instance *);
int (*power_off)(struct samsung_usb2_phy_instance *);
unsigned int id;
char *label;
};
struct samsung_usb2_phy_config {
const struct samsung_usb2_common_phy *phys;
int (*rate_to_clk)(unsigned long, u32 *);
unsigned int num_phys;
bool has_mode_switch;
};
extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config;
#endif

331
drivers/phy/phy-sun4i-usb.c Normal file
View File

@ -0,0 +1,331 @@
/*
* Allwinner sun4i USB phy driver
*
* Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
*
* Based on code from
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
*
* Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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/clk.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#define REG_ISCR 0x00
#define REG_PHYCTL 0x04
#define REG_PHYBIST 0x08
#define REG_PHYTUNE 0x0c
#define PHYCTL_DATA BIT(7)
#define SUNXI_AHB_ICHR8_EN BIT(10)
#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
#define SUNXI_ULPI_BYPASS_EN BIT(0)
/* Common Control Bits for Both PHYs */
#define PHY_PLL_BW 0x03
#define PHY_RES45_CAL_EN 0x0c
/* Private Control Bits for Each PHY */
#define PHY_TX_AMPLITUDE_TUNE 0x20
#define PHY_TX_SLEWRATE_TUNE 0x22
#define PHY_VBUSVALID_TH_SEL 0x25
#define PHY_PULLUP_RES_SEL 0x27
#define PHY_OTG_FUNC_EN 0x28
#define PHY_VBUS_DET_EN 0x29
#define PHY_DISCON_TH_SEL 0x2a
#define MAX_PHYS 3
struct sun4i_usb_phy_data {
struct clk *clk;
void __iomem *base;
struct mutex mutex;
int num_phys;
u32 disc_thresh;
struct sun4i_usb_phy {
struct phy *phy;
void __iomem *pmu;
struct regulator *vbus;
struct reset_control *reset;
int index;
} phys[MAX_PHYS];
};
#define to_sun4i_usb_phy_data(phy) \
container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
int len)
{
struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
u32 temp, usbc_bit = BIT(phy->index * 2);
int i;
mutex_lock(&phy_data->mutex);
for (i = 0; i < len; i++) {
temp = readl(phy_data->base + REG_PHYCTL);
/* clear the address portion */
temp &= ~(0xff << 8);
/* set the address */
temp |= ((addr + i) << 8);
writel(temp, phy_data->base + REG_PHYCTL);
/* set the data bit and clear usbc bit*/
temp = readb(phy_data->base + REG_PHYCTL);
if (data & 0x1)
temp |= PHYCTL_DATA;
else
temp &= ~PHYCTL_DATA;
temp &= ~usbc_bit;
writeb(temp, phy_data->base + REG_PHYCTL);
/* pulse usbc_bit */
temp = readb(phy_data->base + REG_PHYCTL);
temp |= usbc_bit;
writeb(temp, phy_data->base + REG_PHYCTL);
temp = readb(phy_data->base + REG_PHYCTL);
temp &= ~usbc_bit;
writeb(temp, phy_data->base + REG_PHYCTL);
data >>= 1;
}
mutex_unlock(&phy_data->mutex);
}
static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
{
u32 bits, reg_value;
if (!phy->pmu)
return;
bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
reg_value = readl(phy->pmu);
if (enable)
reg_value |= bits;
else
reg_value &= ~bits;
writel(reg_value, phy->pmu);
}
static int sun4i_usb_phy_init(struct phy *_phy)
{
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
int ret;
ret = clk_prepare_enable(data->clk);
if (ret)
return ret;
ret = reset_control_deassert(phy->reset);
if (ret) {
clk_disable_unprepare(data->clk);
return ret;
}
/* Adjust PHY's magnitude and rate */
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
/* Disconnect threshold adjustment */
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
sun4i_usb_phy_passby(phy, 1);
return 0;
}
static int sun4i_usb_phy_exit(struct phy *_phy)
{
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
sun4i_usb_phy_passby(phy, 0);
reset_control_assert(phy->reset);
clk_disable_unprepare(data->clk);
return 0;
}
static int sun4i_usb_phy_power_on(struct phy *_phy)
{
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
int ret = 0;
if (phy->vbus)
ret = regulator_enable(phy->vbus);
return ret;
}
static int sun4i_usb_phy_power_off(struct phy *_phy)
{
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
if (phy->vbus)
regulator_disable(phy->vbus);
return 0;
}
static struct phy_ops sun4i_usb_phy_ops = {
.init = sun4i_usb_phy_init,
.exit = sun4i_usb_phy_exit,
.power_on = sun4i_usb_phy_power_on,
.power_off = sun4i_usb_phy_power_off,
.owner = THIS_MODULE,
};
static struct phy *sun4i_usb_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
return ERR_PTR(-ENODEV);
return data->phys[args->args[0]].phy;
}
static int sun4i_usb_phy_probe(struct platform_device *pdev)
{
struct sun4i_usb_phy_data *data;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
void __iomem *pmu = NULL;
struct phy_provider *phy_provider;
struct reset_control *reset;
struct regulator *vbus;
struct resource *res;
struct phy *phy;
char name[16];
int i;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
mutex_init(&data->mutex);
if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
data->num_phys = 2;
else
data->num_phys = 3;
if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
data->disc_thresh = 3;
else
data->disc_thresh = 2;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
data->base = devm_ioremap_resource(dev, res);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
data->clk = devm_clk_get(dev, "usb_phy");
if (IS_ERR(data->clk)) {
dev_err(dev, "could not get usb_phy clock\n");
return PTR_ERR(data->clk);
}
/* Skip 0, 0 is the phy for otg which is not yet supported. */
for (i = 1; i < data->num_phys; i++) {
snprintf(name, sizeof(name), "usb%d_vbus", i);
vbus = devm_regulator_get_optional(dev, name);
if (IS_ERR(vbus)) {
if (PTR_ERR(vbus) == -EPROBE_DEFER)
return -EPROBE_DEFER;
vbus = NULL;
}
snprintf(name, sizeof(name), "usb%d_reset", i);
reset = devm_reset_control_get(dev, name);
if (IS_ERR(reset)) {
dev_err(dev, "failed to get reset %s\n", name);
return PTR_ERR(reset);
}
if (i) { /* No pmu for usbc0 */
snprintf(name, sizeof(name), "pmu%d", i);
res = platform_get_resource_byname(pdev,
IORESOURCE_MEM, name);
pmu = devm_ioremap_resource(dev, res);
if (IS_ERR(pmu))
return PTR_ERR(pmu);
}
phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY %d\n", i);
return PTR_ERR(phy);
}
data->phys[i].phy = phy;
data->phys[i].pmu = pmu;
data->phys[i].vbus = vbus;
data->phys[i].reset = reset;
data->phys[i].index = i;
phy_set_drvdata(phy, &data->phys[i]);
}
dev_set_drvdata(dev, data);
phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
}
static const struct of_device_id sun4i_usb_phy_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-usb-phy" },
{ .compatible = "allwinner,sun5i-a13-usb-phy" },
{ .compatible = "allwinner,sun7i-a20-usb-phy" },
{ },
};
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
static struct platform_driver sun4i_usb_phy_driver = {
.probe = sun4i_usb_phy_probe,
.driver = {
.of_match_table = sun4i_usb_phy_of_match,
.name = "sun4i-usb-phy",
.owner = THIS_MODULE,
}
};
module_platform_driver(sun4i_usb_phy_driver);
MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_LICENSE("GPL v2");

470
drivers/phy/phy-ti-pipe3.c Normal file
View File

@ -0,0 +1,470 @@
/*
* phy-ti-pipe3 - PIPE3 PHY driver.
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*
* 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/slab.h>
#include <linux/phy/phy.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/phy/omap_control_phy.h>
#include <linux/of_platform.h>
#define PLL_STATUS 0x00000004
#define PLL_GO 0x00000008
#define PLL_CONFIGURATION1 0x0000000C
#define PLL_CONFIGURATION2 0x00000010
#define PLL_CONFIGURATION3 0x00000014
#define PLL_CONFIGURATION4 0x00000020
#define PLL_REGM_MASK 0x001FFE00
#define PLL_REGM_SHIFT 0x9
#define PLL_REGM_F_MASK 0x0003FFFF
#define PLL_REGM_F_SHIFT 0x0
#define PLL_REGN_MASK 0x000001FE
#define PLL_REGN_SHIFT 0x1
#define PLL_SELFREQDCO_MASK 0x0000000E
#define PLL_SELFREQDCO_SHIFT 0x1
#define PLL_SD_MASK 0x0003FC00
#define PLL_SD_SHIFT 10
#define SET_PLL_GO 0x1
#define PLL_LDOPWDN BIT(15)
#define PLL_TICOPWDN BIT(16)
#define PLL_LOCK 0x2
#define PLL_IDLE 0x1
/*
* This is an Empirical value that works, need to confirm the actual
* value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
* to be correctly reflected in the PIPE3PHY_PLL_STATUS register.
*/
#define PLL_IDLE_TIME 100 /* in milliseconds */
#define PLL_LOCK_TIME 100 /* in milliseconds */
struct pipe3_dpll_params {
u16 m;
u8 n;
u8 freq:3;
u8 sd;
u32 mf;
};
struct pipe3_dpll_map {
unsigned long rate;
struct pipe3_dpll_params params;
};
struct ti_pipe3 {
void __iomem *pll_ctrl_base;
struct device *dev;
struct device *control_dev;
struct clk *wkupclk;
struct clk *sys_clk;
struct clk *refclk;
struct pipe3_dpll_map *dpll_map;
};
static struct pipe3_dpll_map dpll_map_usb[] = {
{12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */
{16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */
{19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */
{20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */
{26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */
{38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */
{ }, /* Terminator */
};
static struct pipe3_dpll_map dpll_map_sata[] = {
{12000000, {1000, 7, 4, 6, 0} }, /* 12 MHz */
{16800000, {714, 7, 4, 6, 0} }, /* 16.8 MHz */
{19200000, {625, 7, 4, 6, 0} }, /* 19.2 MHz */
{20000000, {600, 7, 4, 6, 0} }, /* 20 MHz */
{26000000, {461, 7, 4, 6, 0} }, /* 26 MHz */
{38400000, {312, 7, 4, 6, 0} }, /* 38.4 MHz */
{ }, /* Terminator */
};
static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset)
{
return __raw_readl(addr + offset);
}
static inline void ti_pipe3_writel(void __iomem *addr, unsigned offset,
u32 data)
{
__raw_writel(data, addr + offset);
}
static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(struct ti_pipe3 *phy)
{
unsigned long rate;
struct pipe3_dpll_map *dpll_map = phy->dpll_map;
rate = clk_get_rate(phy->sys_clk);
for (; dpll_map->rate; dpll_map++) {
if (rate == dpll_map->rate)
return &dpll_map->params;
}
dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate);
return NULL;
}
static int ti_pipe3_power_off(struct phy *x)
{
struct ti_pipe3 *phy = phy_get_drvdata(x);
omap_control_phy_power(phy->control_dev, 0);
return 0;
}
static int ti_pipe3_power_on(struct phy *x)
{
struct ti_pipe3 *phy = phy_get_drvdata(x);
omap_control_phy_power(phy->control_dev, 1);
return 0;
}
static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy)
{
u32 val;
unsigned long timeout;
timeout = jiffies + msecs_to_jiffies(PLL_LOCK_TIME);
do {
cpu_relax();
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
if (val & PLL_LOCK)
break;
} while (!time_after(jiffies, timeout));
if (!(val & PLL_LOCK)) {
dev_err(phy->dev, "DPLL failed to lock\n");
return -EBUSY;
}
return 0;
}
static int ti_pipe3_dpll_program(struct ti_pipe3 *phy)
{
u32 val;
struct pipe3_dpll_params *dpll_params;
dpll_params = ti_pipe3_get_dpll_params(phy);
if (!dpll_params)
return -EINVAL;
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
val &= ~PLL_REGN_MASK;
val |= dpll_params->n << PLL_REGN_SHIFT;
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
val &= ~PLL_SELFREQDCO_MASK;
val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
val &= ~PLL_REGM_MASK;
val |= dpll_params->m << PLL_REGM_SHIFT;
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
val &= ~PLL_REGM_F_MASK;
val |= dpll_params->mf << PLL_REGM_F_SHIFT;
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
val &= ~PLL_SD_MASK;
val |= dpll_params->sd << PLL_SD_SHIFT;
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
ti_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
return ti_pipe3_dpll_wait_lock(phy);
}
static int ti_pipe3_init(struct phy *x)
{
struct ti_pipe3 *phy = phy_get_drvdata(x);
u32 val;
int ret = 0;
/* Bring it out of IDLE if it is IDLE */
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
if (val & PLL_IDLE) {
val &= ~PLL_IDLE;
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
ret = ti_pipe3_dpll_wait_lock(phy);
}
/* Program the DPLL only if not locked */
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
if (!(val & PLL_LOCK))
if (ti_pipe3_dpll_program(phy))
return -EINVAL;
return ret;
}
static int ti_pipe3_exit(struct phy *x)
{
struct ti_pipe3 *phy = phy_get_drvdata(x);
u32 val;
unsigned long timeout;
/* SATA DPLL can't be powered down due to Errata i783 */
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata"))
return 0;
/* Put DPLL in IDLE mode */
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
val |= PLL_IDLE;
ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
/* wait for LDO and Oscillator to power down */
timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME);
do {
cpu_relax();
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN))
break;
} while (!time_after(jiffies, timeout));
if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) {
dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n",
val);
return -EBUSY;
}
return 0;
}
static struct phy_ops ops = {
.init = ti_pipe3_init,
.exit = ti_pipe3_exit,
.power_on = ti_pipe3_power_on,
.power_off = ti_pipe3_power_off,
.owner = THIS_MODULE,
};
#ifdef CONFIG_OF
static const struct of_device_id ti_pipe3_id_table[];
#endif
static int ti_pipe3_probe(struct platform_device *pdev)
{
struct ti_pipe3 *phy;
struct phy *generic_phy;
struct phy_provider *phy_provider;
struct resource *res;
struct device_node *node = pdev->dev.of_node;
struct device_node *control_node;
struct platform_device *control_pdev;
const struct of_device_id *match;
match = of_match_device(of_match_ptr(ti_pipe3_id_table), &pdev->dev);
if (!match)
return -EINVAL;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy) {
dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n");
return -ENOMEM;
}
phy->dpll_map = (struct pipe3_dpll_map *)match->data;
if (!phy->dpll_map) {
dev_err(&pdev->dev, "no DPLL data\n");
return -EINVAL;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl");
phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(phy->pll_ctrl_base))
return PTR_ERR(phy->pll_ctrl_base);
phy->dev = &pdev->dev;
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
if (IS_ERR(phy->wkupclk)) {
dev_err(&pdev->dev, "unable to get wkupclk\n");
return PTR_ERR(phy->wkupclk);
}
phy->refclk = devm_clk_get(phy->dev, "refclk");
if (IS_ERR(phy->refclk)) {
dev_err(&pdev->dev, "unable to get refclk\n");
return PTR_ERR(phy->refclk);
}
} else {
phy->wkupclk = ERR_PTR(-ENODEV);
phy->refclk = ERR_PTR(-ENODEV);
}
phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
if (IS_ERR(phy->sys_clk)) {
dev_err(&pdev->dev, "unable to get sysclk\n");
return -EINVAL;
}
control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) {
dev_err(&pdev->dev, "Failed to get control device phandle\n");
return -EINVAL;
}
control_pdev = of_find_device_by_node(control_node);
if (!control_pdev) {
dev_err(&pdev->dev, "Failed to get control device\n");
return -EINVAL;
}
phy->control_dev = &control_pdev->dev;
omap_control_phy_power(phy->control_dev, 0);
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);
generic_phy = devm_phy_create(phy->dev, &ops, NULL);
if (IS_ERR(generic_phy))
return PTR_ERR(generic_phy);
phy_set_drvdata(generic_phy, phy);
phy_provider = devm_of_phy_provider_register(phy->dev,
of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
pm_runtime_get(&pdev->dev);
return 0;
}
static int ti_pipe3_remove(struct platform_device *pdev)
{
if (!pm_runtime_suspended(&pdev->dev))
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static int ti_pipe3_runtime_suspend(struct device *dev)
{
struct ti_pipe3 *phy = dev_get_drvdata(dev);
if (!IS_ERR(phy->wkupclk))
clk_disable_unprepare(phy->wkupclk);
if (!IS_ERR(phy->refclk))
clk_disable_unprepare(phy->refclk);
return 0;
}
static int ti_pipe3_runtime_resume(struct device *dev)
{
u32 ret = 0;
struct ti_pipe3 *phy = dev_get_drvdata(dev);
if (!IS_ERR(phy->refclk)) {
ret = clk_prepare_enable(phy->refclk);
if (ret) {
dev_err(phy->dev, "Failed to enable refclk %d\n", ret);
goto err1;
}
}
if (!IS_ERR(phy->wkupclk)) {
ret = clk_prepare_enable(phy->wkupclk);
if (ret) {
dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
goto err2;
}
}
return 0;
err2:
if (!IS_ERR(phy->refclk))
clk_disable_unprepare(phy->refclk);
err1:
return ret;
}
static const struct dev_pm_ops ti_pipe3_pm_ops = {
SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend,
ti_pipe3_runtime_resume, NULL)
};
#define DEV_PM_OPS (&ti_pipe3_pm_ops)
#else
#define DEV_PM_OPS NULL
#endif
#ifdef CONFIG_OF
static const struct of_device_id ti_pipe3_id_table[] = {
{
.compatible = "ti,phy-usb3",
.data = dpll_map_usb,
},
{
.compatible = "ti,omap-usb3",
.data = dpll_map_usb,
},
{
.compatible = "ti,phy-pipe3-sata",
.data = dpll_map_sata,
},
{}
};
MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
#endif
static struct platform_driver ti_pipe3_driver = {
.probe = ti_pipe3_probe,
.remove = ti_pipe3_remove,
.driver = {
.name = "ti-pipe3",
.owner = THIS_MODULE,
.pm = DEV_PM_OPS,
.of_match_table = of_match_ptr(ti_pipe3_id_table),
},
};
module_platform_driver(ti_pipe3_driver);
MODULE_ALIAS("platform: ti_pipe3");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("TI PIPE3 phy driver");
MODULE_LICENSE("GPL v2");

View File

@ -338,7 +338,7 @@ static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
dev_err(twl->dev, "unsupported T2 transceiver mode %d\n",
mode);
break;
};
}
}
static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
@ -661,7 +661,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
struct phy_provider *phy_provider;
struct phy_init_data *init_data = NULL;
twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
if (!twl)
return -ENOMEM;
@ -676,7 +676,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
return -EINVAL;
}
otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL);
otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
if (!otg)
return -ENOMEM;

1750
drivers/phy/phy-xgene.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,7 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/usb/musb-omap.h>
#include <linux/usb/omap_control_usb.h>
#include <linux/phy/omap_control_phy.h>
#include <linux/of_platform.h>
#include "musb_core.h"

View File

@ -75,27 +75,6 @@ config NOP_USB_XCEIV
built-in with usb ip or which are autonomous and doesn't require any
phy programming such as ISP1x04 etc.
config OMAP_CONTROL_USB
tristate "OMAP CONTROL USB Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
help
Enable this to add support for the USB part present in the control
module. This driver has API to power on the USB2 PHY and to write to
the mailbox. The mailbox is present only in omap4 and the register to
power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
additional register to power on USB3 PHY.
config OMAP_USB3
tristate "OMAP USB3 PHY Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
select OMAP_CONTROL_USB
select USB_PHY
help
Enable this to support the USB3 PHY that is part of SOC. This
driver takes care of all the PHY functionality apart from comparator.
This driver interacts with the "OMAP Control USB Driver" to power
on/off the PHY.
config AM335X_CONTROL_USB
tristate

View File

@ -13,11 +13,9 @@ obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o
obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o
obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o
obj-$(CONFIG_TAHVO_USB) += phy-tahvo.o
obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o
obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o
obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o
obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o
obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o
obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o
obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o
obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o

View File

@ -1,361 +0,0 @@
/*
* omap-usb3 - USB PHY, talking to dwc3 controller in OMAP.
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*
* 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/slab.h>
#include <linux/usb/omap_usb.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/usb/omap_control_usb.h>
#include <linux/of_platform.h>
#define PLL_STATUS 0x00000004
#define PLL_GO 0x00000008
#define PLL_CONFIGURATION1 0x0000000C
#define PLL_CONFIGURATION2 0x00000010
#define PLL_CONFIGURATION3 0x00000014
#define PLL_CONFIGURATION4 0x00000020
#define PLL_REGM_MASK 0x001FFE00
#define PLL_REGM_SHIFT 0x9
#define PLL_REGM_F_MASK 0x0003FFFF
#define PLL_REGM_F_SHIFT 0x0
#define PLL_REGN_MASK 0x000001FE
#define PLL_REGN_SHIFT 0x1
#define PLL_SELFREQDCO_MASK 0x0000000E
#define PLL_SELFREQDCO_SHIFT 0x1
#define PLL_SD_MASK 0x0003FC00
#define PLL_SD_SHIFT 0x9
#define SET_PLL_GO 0x1
#define PLL_TICOPWDN 0x10000
#define PLL_LOCK 0x2
#define PLL_IDLE 0x1
/*
* This is an Empirical value that works, need to confirm the actual
* value required for the USB3PHY_PLL_CONFIGURATION2.PLL_IDLE status
* to be correctly reflected in the USB3PHY_PLL_STATUS register.
*/
# define PLL_IDLE_TIME 100;
struct usb_dpll_map {
unsigned long rate;
struct usb_dpll_params params;
};
static struct usb_dpll_map dpll_map[] = {
{12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */
{16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */
{19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */
{20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */
{26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */
{38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */
};
static struct usb_dpll_params *omap_usb3_get_dpll_params(unsigned long rate)
{
int i;
for (i = 0; i < ARRAY_SIZE(dpll_map); i++) {
if (rate == dpll_map[i].rate)
return &dpll_map[i].params;
}
return NULL;
}
static int omap_usb3_suspend(struct usb_phy *x, int suspend)
{
struct omap_usb *phy = phy_to_omapusb(x);
int val;
int timeout = PLL_IDLE_TIME;
if (suspend && !phy->is_suspended) {
val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
val |= PLL_IDLE;
omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
do {
val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
if (val & PLL_TICOPWDN)
break;
udelay(1);
} while (--timeout);
omap_control_usb_phy_power(phy->control_dev, 0);
phy->is_suspended = 1;
} else if (!suspend && phy->is_suspended) {
phy->is_suspended = 0;
val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
val &= ~PLL_IDLE;
omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
do {
val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
if (!(val & PLL_TICOPWDN))
break;
udelay(1);
} while (--timeout);
}
return 0;
}
static void omap_usb_dpll_relock(struct omap_usb *phy)
{
u32 val;
unsigned long timeout;
omap_usb_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
timeout = jiffies + msecs_to_jiffies(20);
do {
val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
if (val & PLL_LOCK)
break;
} while (!WARN_ON(time_after(jiffies, timeout)));
}
static int omap_usb_dpll_lock(struct omap_usb *phy)
{
u32 val;
unsigned long rate;
struct usb_dpll_params *dpll_params;
rate = clk_get_rate(phy->sys_clk);
dpll_params = omap_usb3_get_dpll_params(rate);
if (!dpll_params) {
dev_err(phy->dev,
"No DPLL configuration for %lu Hz SYS CLK\n", rate);
return -EINVAL;
}
val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
val &= ~PLL_REGN_MASK;
val |= dpll_params->n << PLL_REGN_SHIFT;
omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
val &= ~PLL_SELFREQDCO_MASK;
val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
val &= ~PLL_REGM_MASK;
val |= dpll_params->m << PLL_REGM_SHIFT;
omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
val &= ~PLL_REGM_F_MASK;
val |= dpll_params->mf << PLL_REGM_F_SHIFT;
omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
val &= ~PLL_SD_MASK;
val |= dpll_params->sd << PLL_SD_SHIFT;
omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
omap_usb_dpll_relock(phy);
return 0;
}
static int omap_usb3_init(struct usb_phy *x)
{
struct omap_usb *phy = phy_to_omapusb(x);
int ret;
ret = omap_usb_dpll_lock(phy);
if (ret)
return ret;
omap_control_usb_phy_power(phy->control_dev, 1);
return 0;
}
static int omap_usb3_probe(struct platform_device *pdev)
{
struct omap_usb *phy;
struct resource *res;
struct device_node *node = pdev->dev.of_node;
struct device_node *control_node;
struct platform_device *control_pdev;
if (!node)
return -EINVAL;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy) {
dev_err(&pdev->dev, "unable to alloc mem for OMAP USB3 PHY\n");
return -ENOMEM;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl");
phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(phy->pll_ctrl_base))
return PTR_ERR(phy->pll_ctrl_base);
phy->dev = &pdev->dev;
phy->phy.dev = phy->dev;
phy->phy.label = "omap-usb3";
phy->phy.init = omap_usb3_init;
phy->phy.set_suspend = omap_usb3_suspend;
phy->phy.type = USB_PHY_TYPE_USB3;
phy->is_suspended = 1;
phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
if (IS_ERR(phy->wkupclk)) {
dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
return PTR_ERR(phy->wkupclk);
}
clk_prepare(phy->wkupclk);
phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
if (IS_ERR(phy->optclk)) {
dev_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n");
return PTR_ERR(phy->optclk);
}
clk_prepare(phy->optclk);
phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin");
if (IS_ERR(phy->sys_clk)) {
pr_err("%s: unable to get sys_clkin\n", __func__);
return -EINVAL;
}
control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) {
dev_err(&pdev->dev, "Failed to get control device phandle\n");
return -EINVAL;
}
control_pdev = of_find_device_by_node(control_node);
if (!control_pdev) {
dev_err(&pdev->dev, "Failed to get control device\n");
return -EINVAL;
}
phy->control_dev = &control_pdev->dev;
omap_control_usb_phy_power(phy->control_dev, 0);
usb_add_phy_dev(&phy->phy);
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);
pm_runtime_get(&pdev->dev);
return 0;
}
static int omap_usb3_remove(struct platform_device *pdev)
{
struct omap_usb *phy = platform_get_drvdata(pdev);
clk_unprepare(phy->wkupclk);
clk_unprepare(phy->optclk);
usb_remove_phy(&phy->phy);
if (!pm_runtime_suspended(&pdev->dev))
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static int omap_usb3_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct omap_usb *phy = platform_get_drvdata(pdev);
clk_disable(phy->wkupclk);
clk_disable(phy->optclk);
return 0;
}
static int omap_usb3_runtime_resume(struct device *dev)
{
u32 ret = 0;
struct platform_device *pdev = to_platform_device(dev);
struct omap_usb *phy = platform_get_drvdata(pdev);
ret = clk_enable(phy->optclk);
if (ret) {
dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
goto err1;
}
ret = clk_enable(phy->wkupclk);
if (ret) {
dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
goto err2;
}
return 0;
err2:
clk_disable(phy->optclk);
err1:
return ret;
}
static const struct dev_pm_ops omap_usb3_pm_ops = {
SET_RUNTIME_PM_OPS(omap_usb3_runtime_suspend, omap_usb3_runtime_resume,
NULL)
};
#define DEV_PM_OPS (&omap_usb3_pm_ops)
#else
#define DEV_PM_OPS NULL
#endif
#ifdef CONFIG_OF
static const struct of_device_id omap_usb3_id_table[] = {
{ .compatible = "ti,omap-usb3" },
{}
};
MODULE_DEVICE_TABLE(of, omap_usb3_id_table);
#endif
static struct platform_driver omap_usb3_driver = {
.probe = omap_usb3_probe,
.remove = omap_usb3_remove,
.driver = {
.name = "omap-usb3",
.owner = THIS_MODULE,
.pm = DEV_PM_OPS,
.of_match_table = of_match_ptr(omap_usb3_id_table),
},
};
module_platform_driver(omap_usb3_driver);
MODULE_ALIAS("platform: omap_usb3");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("OMAP USB3 phy driver");
MODULE_LICENSE("GPL v2");

View File

@ -27,7 +27,7 @@
#include <linux/io.h>
#include <linux/usb/musb-omap.h>
#include <linux/usb/phy_companion.h>
#include <linux/usb/omap_usb.h>
#include <linux/phy/omap_usb.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>

View File

@ -1,5 +1,5 @@
/*
* omap_control_usb.h - Header file for the USB part of control module.
* omap_control_phy.h - Header file for the PHY part of control module.
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
* This program is free software; you can redistribute it and/or modify
@ -16,10 +16,10 @@
*
*/
#ifndef __OMAP_CONTROL_USB_H__
#define __OMAP_CONTROL_USB_H__
#ifndef __OMAP_CONTROL_PHY_H__
#define __OMAP_CONTROL_PHY_H__
enum omap_control_usb_type {
enum omap_control_phy_type {
OMAP_CTRL_TYPE_OTGHS = 1, /* Mailbox OTGHS_CONTROL */
OMAP_CTRL_TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */
OMAP_CTRL_TYPE_PIPE3, /* PIPE3 PHY, DPLL & seperate Rx/Tx power */
@ -27,7 +27,7 @@ enum omap_control_usb_type {
OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */
};
struct omap_control_usb {
struct omap_control_phy {
struct device *dev;
u32 __iomem *otghs_control;
@ -36,7 +36,7 @@ struct omap_control_usb {
struct clk *sys_clk;
enum omap_control_usb_type type;
enum omap_control_phy_type type;
};
enum omap_control_usb_mode {
@ -54,14 +54,14 @@ enum omap_control_usb_mode {
#define OMAP_CTRL_DEV_SESSEND BIT(3)
#define OMAP_CTRL_DEV_IDDIG BIT(4)
#define OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK 0x003FC000
#define OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT 0xE
#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000
#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 0xE
#define OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK 0xFFC00000
#define OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT 0x16
#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000
#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 0x16
#define OMAP_CTRL_USB3_PHY_TX_RX_POWERON 0x3
#define OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF 0x0
#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON 0x3
#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0
#define OMAP_CTRL_USB2_PHY_PD BIT(28)
@ -70,13 +70,13 @@ enum omap_control_usb_mode {
#define AM437X_CTRL_USB2_OTGVDET_EN BIT(19)
#define AM437X_CTRL_USB2_OTGSESSEND_EN BIT(20)
#if IS_ENABLED(CONFIG_OMAP_CONTROL_USB)
extern void omap_control_usb_phy_power(struct device *dev, int on);
extern void omap_control_usb_set_mode(struct device *dev,
enum omap_control_usb_mode mode);
#if IS_ENABLED(CONFIG_OMAP_CONTROL_PHY)
void omap_control_phy_power(struct device *dev, int on);
void omap_control_usb_set_mode(struct device *dev,
enum omap_control_usb_mode mode);
#else
static inline void omap_control_usb_phy_power(struct device *dev, int on)
static inline void omap_control_phy_power(struct device *dev, int on)
{
}
@ -86,4 +86,4 @@ static inline void omap_control_usb_set_mode(struct device *dev,
}
#endif
#endif /* __OMAP_CONTROL_USB_H__ */
#endif /* __OMAP_CONTROL_PHY_H__ */

View File

@ -34,14 +34,24 @@ struct omap_usb {
struct usb_phy phy;
struct phy_companion *comparator;
void __iomem *pll_ctrl_base;
void __iomem *phy_base;
struct device *dev;
struct device *control_dev;
struct clk *wkupclk;
struct clk *sys_clk;
struct clk *optclk;
u8 is_suspended:1;
u8 flags;
};
struct usb_phy_data {
const char *label;
u8 flags;
};
/* Driver Flags */
#define OMAP_USB2_HAS_START_SRP (1 << 0)
#define OMAP_USB2_HAS_SET_VBUS (1 << 1)
#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT (1 << 2)
#define phy_to_omapusb(x) container_of((x), struct omap_usb, phy)
#if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE)

View File

@ -149,8 +149,11 @@ struct phy *phy_get(struct device *dev, const char *string);
struct phy *phy_optional_get(struct device *dev, const char *string);
struct phy *devm_phy_get(struct device *dev, const char *string);
struct phy *devm_phy_optional_get(struct device *dev, const char *string);
struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
const char *con_id);
void phy_put(struct phy *phy);
void devm_phy_put(struct device *dev, struct phy *phy);
struct phy *of_phy_get(struct device_node *np, const char *con_id);
struct phy *of_phy_simple_xlate(struct device *dev,
struct of_phandle_args *args);
struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
@ -251,6 +254,13 @@ static inline struct phy *devm_phy_optional_get(struct device *dev,
return ERR_PTR(-ENOSYS);
}
static inline struct phy *devm_of_phy_get(struct device *dev,
struct device_node *np,
const char *con_id)
{
return ERR_PTR(-ENOSYS);
}
static inline void phy_put(struct phy *phy)
{
}
@ -259,6 +269,11 @@ static inline void devm_phy_put(struct device *dev, struct phy *phy)
{
}
static inline struct phy *of_phy_get(struct device_node *np, const char *con_id)
{
return ERR_PTR(-ENOSYS);
}
static inline struct phy *of_phy_simple_xlate(struct device *dev,
struct of_phandle_args *args)
{