@@ -22,8 +22,12 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
-
-#include <asm/io.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/usb/phy.h>
#include "soc.h"
#include "omap_device.h"
@@ -526,3 +530,172 @@ void __init usbhs_init(struct usbhs_omap_platform_data *pdata)
}
#endif
+
+/* Template for PHY regulators */
+static struct regulator_consumer_supply hsusb_reg_supplies[] = {
+ { /* .supply & .dev_name filled later */ },
+};
+
+static struct regulator_init_data hsusb_reg_data = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = hsusb_reg_supplies,
+ .num_consumer_supplies = ARRAY_SIZE(hsusb_reg_supplies),
+};
+
+static struct fixed_voltage_config hsusb_reg_config = {
+ /* .supply_name filled later */
+ .microvolts = 3300000,
+ .gpio = -1, /* updated later */
+ .startup_delay = 70000, /* 70msec */
+ .enable_high = 1, /* updated later */
+ .enabled_at_boot = 0, /* keep in RESET */
+ /* .init_data filled later */
+};
+
+static struct platform_device_info hsusb_reg_pdev_info = {
+ .name = "reg-fixed-voltage",
+ .id = PLATFORM_DEVID_AUTO,
+};
+
+static const char *reset_supply = "reset";
+static const char *vcc_supply = "vcc";
+static const char *nop_name = "nop_usb_xceiv"; /* NOP PHY driver */
+
+int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys)
+{
+ struct regulator_consumer_supply *supplies;
+ struct regulator_init_data *reg_data;
+ struct fixed_voltage_config *config;
+ char *supply_name;
+ int i, len;
+ struct platform_device *pdev;
+ char *phy_id;
+
+ /* the phy_id will be something like "nop_usb_xceiv.1" */
+ len = strlen(nop_name) + 3; /* 3 -> ".1" and NULL terminator */
+
+ for (i = 0; i < num_phys; i++) {
+
+ if (!phy->port) {
+ pr_err("%s: Invalid port 0. Must start from 1\n",
+ __func__);
+ continue;
+ }
+
+ /* do we need a NOP PHY device ? */
+ if (!gpio_is_valid(phy->reset_gpio) &&
+ !gpio_is_valid(phy->vcc_gpio))
+ continue;
+
+ /* create a NOP PHY device */
+ pdev = kzalloc(sizeof(*pdev), GFP_KERNEL);
+ if (!pdev)
+ return -ENOMEM;
+
+ pdev->id = phy->port;
+ pdev->name = nop_name;
+ pdev->dev.platform_data = phy->platform_data;
+
+ phy_id = kmalloc(len, GFP_KERNEL);
+ if (!phy_id)
+ return -ENOMEM;
+
+ scnprintf(phy_id, len, "nop_usb_xceiv.%d\n",
+ pdev->id);
+
+ if (platform_device_register(pdev)) {
+ pr_err("%s: Failed to register device %s\n",
+ __func__, phy_id);
+ continue;
+ }
+
+ usb_bind_phy("ehci-omap.0", phy->port - 1, phy_id);
+
+ /* Do we need RESET regulator ? */
+ if (!gpio_is_valid(phy->reset_gpio))
+ goto check_vcc;
+
+ supplies = kmemdup(hsusb_reg_supplies,
+ ARRAY_SIZE(hsusb_reg_supplies) *
+ sizeof(struct regulator_consumer_supply),
+ GFP_KERNEL);
+ if (!supplies)
+ return -ENOMEM;
+
+ supplies->supply = reset_supply;
+ supplies->dev_name = phy_id;
+
+ reg_data = kmemdup(&hsusb_reg_data, sizeof(hsusb_reg_data),
+ GFP_KERNEL);
+ if (!reg_data)
+ return -ENOMEM;
+
+ reg_data->consumer_supplies = supplies;
+
+ config = kmemdup(&hsusb_reg_config, sizeof(hsusb_reg_config),
+ GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+
+ supply_name = kmalloc(13, GFP_KERNEL);
+ if (!supply_name)
+ return -ENOMEM;
+
+ scnprintf(supply_name, 13, "hsusb%d_reset", phy->port);
+ config->supply_name = supply_name;
+ config->gpio = phy->reset_gpio;
+ config->init_data = reg_data;
+
+ hsusb_reg_pdev_info.data = config;
+ hsusb_reg_pdev_info.size_data = sizeof(hsusb_reg_config);
+ platform_device_register_full(&hsusb_reg_pdev_info);
+
+check_vcc:
+ /* Do we need VCC regulator? */
+ if (!gpio_is_valid(phy->vcc_gpio))
+ goto next;
+
+ supplies = kmemdup(hsusb_reg_supplies,
+ ARRAY_SIZE(hsusb_reg_supplies) *
+ sizeof(struct regulator_consumer_supply),
+ GFP_KERNEL);
+ if (!supplies)
+ return -ENOMEM;
+
+ supplies->supply = vcc_supply;
+ supplies->dev_name = phy_id;
+
+ reg_data = kmemdup(&hsusb_reg_data, sizeof(hsusb_reg_data),
+ GFP_KERNEL);
+ if (!reg_data)
+ return -ENOMEM;
+
+ reg_data->consumer_supplies = supplies;
+
+ config = kmemdup(&hsusb_reg_config, sizeof(hsusb_reg_config),
+ GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+
+ supply_name = kmalloc(13, GFP_KERNEL);
+ if (!supply_name)
+ return -ENOMEM;
+
+ scnprintf(supply_name, 13, "hsusb%d_vcc", phy->port);
+ config->supply_name = supply_name;
+ config->gpio = phy->vcc_gpio;
+ config->enable_high = phy->vcc_polarity;
+ config->init_data = reg_data;
+
+ hsusb_reg_pdev_info.data = config;
+ hsusb_reg_pdev_info.size_data = sizeof(hsusb_reg_config);
+ platform_device_register_full(&hsusb_reg_pdev_info);
+
+next:
+ phy++;
+ }
+
+ return 0;
+}
@@ -53,8 +53,17 @@
#define USBPHY_OTGSESSEND_EN (1 << 20)
#define USBPHY_DATA_POLARITY (1 << 23)
+struct usbhs_phy_data {
+ int port; /* 1 indexed port number */
+ int reset_gpio;
+ int vcc_gpio;
+ bool vcc_polarity; /* 1 active high, 0 active low */
+ void *platform_data;
+};
+
extern void usb_musb_init(struct omap_musb_board_data *board_data);
extern void usbhs_init(struct usbhs_omap_platform_data *pdata);
+extern int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys);
extern void am35x_musb_reset(void);
extern void am35x_musb_phy_power(u8 on);
This helper allows board support code to add the PHY's VCC and RESET regulators which are GPIO controlled as well as the NOP PHY device. Signed-off-by: Roger Quadros <rogerq@ti.com> --- arch/arm/mach-omap2/usb-host.c | 177 +++++++++++++++++++++++++++++++++++++++- arch/arm/mach-omap2/usb.h | 9 ++ 2 files changed, 184 insertions(+), 2 deletions(-)