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