| .------------------------------------------------------------------------------+ | 
 | |			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. |