Loading Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt +3 −1 Original line number Diff line number Diff line Loading @@ -134,7 +134,7 @@ SMSC HSIC HUB Required properties : - compatible : should be "qcom,hsic-smsc-hub" - smsc,model-id : should be either <3503> or <4604> depending on hub model. It - smsc,model-id : should be <3502>/<3503>/<4604> depending on hub model. It will be 0 for standalone HSIC controller configuration. - smsc,reset-gpio: this output gpio is used to assert/de-assert the hub reset - Sub node for "MSM HSIC EHCI controller". Loading @@ -144,6 +144,8 @@ Optional properties : - smsc,int-gpio: this input gpio indicate HUB suspend status and signal remote wakeup interrupt - smsc,refclk-gpio: this gpio is used to supply the reference clock - smsc,xo-clk-gio: this output gpio is used to control the external XO clock which is supplied to the hub as a reference clock - hub-vbus-supply: this regulator is used to supply the power to downstream ports - hub-int-supply: this regulator is used to bias the interrupt gpio Loading drivers/misc/smsc_hub.c +161 −2 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ struct hsic_hub { struct clk *ref_clk; struct regulator *hsic_hub_reg; struct regulator *int_pad_reg, *hub_vbus_reg; bool enabled; }; static struct hsic_hub *smsc_hub; static struct platform_driver smsc_hub_driver; Loading Loading @@ -191,6 +192,14 @@ static int msm_hsic_hub_init_clock(struct hsic_hub *hub, int init) { int ret; /* * xo_clk_gpio controls an external xo clock which feeds * the hub reference clock. When this gpio is present, * assume that no other clocks are required. */ if (hub->pdata->xo_clk_gpio) return 0; if (!init) { if (!IS_ERR(hub->ref_clk)) clk_disable_unprepare(hub->ref_clk); Loading Loading @@ -257,6 +266,15 @@ static int msm_hsic_hub_init_gpio(struct hsic_hub *hub, int init) dev_err(hub->dev, "gpio request failed (CLK GPIO)\n"); } if (pdata->xo_clk_gpio) { ret = devm_gpio_request(hub->dev, pdata->xo_clk_gpio, "HSIC_HUB_XO_CLK"); if (ret < 0) { dev_err(hub->dev, "gpio request failed(XO CLK GPIO)\n"); return ret; } } if (pdata->int_gpio) { ret = devm_gpio_request(hub->dev, pdata->int_gpio, "HSIC_HUB_INT"); Loading Loading @@ -348,6 +366,122 @@ reg_optimum_mode_fail: return ret; } static int smsc_hub_enable(struct hsic_hub *hub) { struct smsc_hub_platform_data *pdata = hub->pdata; struct of_dev_auxdata *hsic_host_auxdata = dev_get_platdata(hub->dev); struct device_node *node = hub->dev->of_node; int ret; ret = gpio_direction_output(pdata->xo_clk_gpio, 1); if (ret < 0) { dev_err(hub->dev, "fail to enable xo clk\n"); return ret; } ret = gpio_direction_output(pdata->hub_reset, 0); if (ret < 0) { dev_err(hub->dev, "fail to assert reset\n"); goto disable_xo; } udelay(5); ret = gpio_direction_output(pdata->hub_reset, 1); if (ret < 0) { dev_err(hub->dev, "fail to de-assert reset\n"); goto disable_xo; } ret = of_platform_populate(node, NULL, hsic_host_auxdata, hub->dev); if (ret < 0) { dev_err(smsc_hub->dev, "fail to add child with %d\n", ret); goto reset; } pm_runtime_allow(hub->dev); reset: gpio_direction_output(pdata->hub_reset, 0); disable_xo: gpio_direction_output(pdata->xo_clk_gpio, 0); return ret; } static int sms_hub_remove_child(struct device *dev, void *data) { struct platform_device *pdev = to_platform_device(dev); /* * Runtime PM is disabled before the driver's remove method * is called. So resume the device before unregistering * the device. Don't worry about the PM usage counter as * the device will be freed. */ pm_runtime_get_sync(dev); of_device_unregister(pdev); return 0; } static int smsc_hub_disable(struct hsic_hub *hub) { struct smsc_hub_platform_data *pdata = hub->pdata; pm_runtime_forbid(hub->dev); device_for_each_child(hub->dev, NULL, sms_hub_remove_child); gpio_direction_output(pdata->hub_reset, 0); gpio_direction_output(pdata->xo_clk_gpio, 0); return 0; } static ssize_t smsc_hub_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", smsc_hub->enabled ? "enabled" : "disabled"); } static ssize_t smsc_hub_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { bool enable; int val; int ret = size; if (sscanf(buf, "%d", &val) == 1) { enable = !!val; } else { ret = -EINVAL; goto out; } if (smsc_hub->enabled == enable) goto out; if (enable) ret = smsc_hub_enable(smsc_hub); else ret = smsc_hub_disable(smsc_hub); pr_debug("smsc hub %s status %d\n", enable ? "Enable" : "Disable", ret); if (!ret) { ret = size; smsc_hub->enabled = enable; } out: return ret; } static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, smsc_hub_enable_show, smsc_hub_enable_store); struct smsc_hub_platform_data *msm_hub_dt_to_pdata( struct platform_device *pdev) { Loading Loading @@ -384,6 +518,10 @@ struct smsc_hub_platform_data *msm_hub_dt_to_pdata( if (pdata->int_gpio < 0) pdata->int_gpio = 0; pdata->xo_clk_gpio = of_get_named_gpio(node, "smsc,xo-clk-gpio", 0); if (pdata->xo_clk_gpio < 0) pdata->xo_clk_gpio = 0; return pdata; } Loading Loading @@ -453,6 +591,16 @@ static int smsc_hub_probe(struct platform_device *pdev) goto uninit_clock; } if (pdata->model_id == SMSC3502_ID) { ret = device_create_file(&pdev->dev, &dev_attr_enable); if (ret < 0) { dev_err(&pdev->dev, "fail to create sysfs file\n"); goto uninit_gpio; } pm_runtime_forbid(&pdev->dev); goto done; } gpio_direction_output(pdata->hub_reset, 0); /* * Hub reset should be asserted for minimum 2microsec Loading Loading @@ -515,10 +663,13 @@ i2c_add_fail: goto uninit_gpio; } smsc_hub->enabled = true; if (!smsc_hub->client) dev_err(&pdev->dev, "failed to connect to smsc_hub through I2C\n"); done: pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); Loading @@ -542,6 +693,8 @@ static int smsc_hub_remove(struct platform_device *pdev) return 0; pdata = smsc_hub->pdata; if (pdata->model_id == SMSC3502_ID) device_remove_file(&pdev->dev, &dev_attr_enable); if (smsc_hub->client) { i2c_unregister_device(smsc_hub->client); smsc_hub->client = NULL; Loading Loading @@ -570,7 +723,7 @@ static int smsc_hub_lpm_enter(struct device *dev) { int ret = 0; if (!smsc_hub) if (!smsc_hub || !smsc_hub->enabled) return 0; if (smsc_hub->xo_handle) { Loading @@ -579,7 +732,10 @@ static int smsc_hub_lpm_enter(struct device *dev) pr_err("%s: failed to devote for TCXO\n" "D1 buffer%d\n", __func__, ret); } } else if (smsc_hub->pdata->xo_clk_gpio) { gpio_direction_output(smsc_hub->pdata->xo_clk_gpio, 0); } return ret; } Loading @@ -587,7 +743,7 @@ static int smsc_hub_lpm_exit(struct device *dev) { int ret = 0; if (!smsc_hub) if (!smsc_hub || !smsc_hub->enabled) return 0; if (smsc_hub->xo_handle) { Loading @@ -596,7 +752,10 @@ static int smsc_hub_lpm_exit(struct device *dev) pr_err("%s: failed to vote for TCXO\n" "D1 buffer%d\n", __func__, ret); } } else if (smsc_hub->pdata->xo_clk_gpio) { gpio_direction_output(smsc_hub->pdata->xo_clk_gpio, 1); } return ret; } #endif Loading include/linux/smsc_hub.h +3 −1 Original line number Diff line number Diff line /* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and Loading @@ -14,6 +14,7 @@ #ifndef __LINUX_SMSC3503_H__ #define __LINUX_SMSC3503_H__ #define SMSC3502_ID 3502 #define SMSC3503_ID 3503 #define SMSC4604_ID 4604 #define SMSC3503_I2C_ADDR 0x08 Loading Loading @@ -52,6 +53,7 @@ struct smsc_hub_platform_data { int hub_reset; int refclk_gpio; int int_gpio; int xo_clk_gpio; }; #endif Loading
Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt +3 −1 Original line number Diff line number Diff line Loading @@ -134,7 +134,7 @@ SMSC HSIC HUB Required properties : - compatible : should be "qcom,hsic-smsc-hub" - smsc,model-id : should be either <3503> or <4604> depending on hub model. It - smsc,model-id : should be <3502>/<3503>/<4604> depending on hub model. It will be 0 for standalone HSIC controller configuration. - smsc,reset-gpio: this output gpio is used to assert/de-assert the hub reset - Sub node for "MSM HSIC EHCI controller". Loading @@ -144,6 +144,8 @@ Optional properties : - smsc,int-gpio: this input gpio indicate HUB suspend status and signal remote wakeup interrupt - smsc,refclk-gpio: this gpio is used to supply the reference clock - smsc,xo-clk-gio: this output gpio is used to control the external XO clock which is supplied to the hub as a reference clock - hub-vbus-supply: this regulator is used to supply the power to downstream ports - hub-int-supply: this regulator is used to bias the interrupt gpio Loading
drivers/misc/smsc_hub.c +161 −2 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ struct hsic_hub { struct clk *ref_clk; struct regulator *hsic_hub_reg; struct regulator *int_pad_reg, *hub_vbus_reg; bool enabled; }; static struct hsic_hub *smsc_hub; static struct platform_driver smsc_hub_driver; Loading Loading @@ -191,6 +192,14 @@ static int msm_hsic_hub_init_clock(struct hsic_hub *hub, int init) { int ret; /* * xo_clk_gpio controls an external xo clock which feeds * the hub reference clock. When this gpio is present, * assume that no other clocks are required. */ if (hub->pdata->xo_clk_gpio) return 0; if (!init) { if (!IS_ERR(hub->ref_clk)) clk_disable_unprepare(hub->ref_clk); Loading Loading @@ -257,6 +266,15 @@ static int msm_hsic_hub_init_gpio(struct hsic_hub *hub, int init) dev_err(hub->dev, "gpio request failed (CLK GPIO)\n"); } if (pdata->xo_clk_gpio) { ret = devm_gpio_request(hub->dev, pdata->xo_clk_gpio, "HSIC_HUB_XO_CLK"); if (ret < 0) { dev_err(hub->dev, "gpio request failed(XO CLK GPIO)\n"); return ret; } } if (pdata->int_gpio) { ret = devm_gpio_request(hub->dev, pdata->int_gpio, "HSIC_HUB_INT"); Loading Loading @@ -348,6 +366,122 @@ reg_optimum_mode_fail: return ret; } static int smsc_hub_enable(struct hsic_hub *hub) { struct smsc_hub_platform_data *pdata = hub->pdata; struct of_dev_auxdata *hsic_host_auxdata = dev_get_platdata(hub->dev); struct device_node *node = hub->dev->of_node; int ret; ret = gpio_direction_output(pdata->xo_clk_gpio, 1); if (ret < 0) { dev_err(hub->dev, "fail to enable xo clk\n"); return ret; } ret = gpio_direction_output(pdata->hub_reset, 0); if (ret < 0) { dev_err(hub->dev, "fail to assert reset\n"); goto disable_xo; } udelay(5); ret = gpio_direction_output(pdata->hub_reset, 1); if (ret < 0) { dev_err(hub->dev, "fail to de-assert reset\n"); goto disable_xo; } ret = of_platform_populate(node, NULL, hsic_host_auxdata, hub->dev); if (ret < 0) { dev_err(smsc_hub->dev, "fail to add child with %d\n", ret); goto reset; } pm_runtime_allow(hub->dev); reset: gpio_direction_output(pdata->hub_reset, 0); disable_xo: gpio_direction_output(pdata->xo_clk_gpio, 0); return ret; } static int sms_hub_remove_child(struct device *dev, void *data) { struct platform_device *pdev = to_platform_device(dev); /* * Runtime PM is disabled before the driver's remove method * is called. So resume the device before unregistering * the device. Don't worry about the PM usage counter as * the device will be freed. */ pm_runtime_get_sync(dev); of_device_unregister(pdev); return 0; } static int smsc_hub_disable(struct hsic_hub *hub) { struct smsc_hub_platform_data *pdata = hub->pdata; pm_runtime_forbid(hub->dev); device_for_each_child(hub->dev, NULL, sms_hub_remove_child); gpio_direction_output(pdata->hub_reset, 0); gpio_direction_output(pdata->xo_clk_gpio, 0); return 0; } static ssize_t smsc_hub_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", smsc_hub->enabled ? "enabled" : "disabled"); } static ssize_t smsc_hub_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { bool enable; int val; int ret = size; if (sscanf(buf, "%d", &val) == 1) { enable = !!val; } else { ret = -EINVAL; goto out; } if (smsc_hub->enabled == enable) goto out; if (enable) ret = smsc_hub_enable(smsc_hub); else ret = smsc_hub_disable(smsc_hub); pr_debug("smsc hub %s status %d\n", enable ? "Enable" : "Disable", ret); if (!ret) { ret = size; smsc_hub->enabled = enable; } out: return ret; } static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, smsc_hub_enable_show, smsc_hub_enable_store); struct smsc_hub_platform_data *msm_hub_dt_to_pdata( struct platform_device *pdev) { Loading Loading @@ -384,6 +518,10 @@ struct smsc_hub_platform_data *msm_hub_dt_to_pdata( if (pdata->int_gpio < 0) pdata->int_gpio = 0; pdata->xo_clk_gpio = of_get_named_gpio(node, "smsc,xo-clk-gpio", 0); if (pdata->xo_clk_gpio < 0) pdata->xo_clk_gpio = 0; return pdata; } Loading Loading @@ -453,6 +591,16 @@ static int smsc_hub_probe(struct platform_device *pdev) goto uninit_clock; } if (pdata->model_id == SMSC3502_ID) { ret = device_create_file(&pdev->dev, &dev_attr_enable); if (ret < 0) { dev_err(&pdev->dev, "fail to create sysfs file\n"); goto uninit_gpio; } pm_runtime_forbid(&pdev->dev); goto done; } gpio_direction_output(pdata->hub_reset, 0); /* * Hub reset should be asserted for minimum 2microsec Loading Loading @@ -515,10 +663,13 @@ i2c_add_fail: goto uninit_gpio; } smsc_hub->enabled = true; if (!smsc_hub->client) dev_err(&pdev->dev, "failed to connect to smsc_hub through I2C\n"); done: pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); Loading @@ -542,6 +693,8 @@ static int smsc_hub_remove(struct platform_device *pdev) return 0; pdata = smsc_hub->pdata; if (pdata->model_id == SMSC3502_ID) device_remove_file(&pdev->dev, &dev_attr_enable); if (smsc_hub->client) { i2c_unregister_device(smsc_hub->client); smsc_hub->client = NULL; Loading Loading @@ -570,7 +723,7 @@ static int smsc_hub_lpm_enter(struct device *dev) { int ret = 0; if (!smsc_hub) if (!smsc_hub || !smsc_hub->enabled) return 0; if (smsc_hub->xo_handle) { Loading @@ -579,7 +732,10 @@ static int smsc_hub_lpm_enter(struct device *dev) pr_err("%s: failed to devote for TCXO\n" "D1 buffer%d\n", __func__, ret); } } else if (smsc_hub->pdata->xo_clk_gpio) { gpio_direction_output(smsc_hub->pdata->xo_clk_gpio, 0); } return ret; } Loading @@ -587,7 +743,7 @@ static int smsc_hub_lpm_exit(struct device *dev) { int ret = 0; if (!smsc_hub) if (!smsc_hub || !smsc_hub->enabled) return 0; if (smsc_hub->xo_handle) { Loading @@ -596,7 +752,10 @@ static int smsc_hub_lpm_exit(struct device *dev) pr_err("%s: failed to vote for TCXO\n" "D1 buffer%d\n", __func__, ret); } } else if (smsc_hub->pdata->xo_clk_gpio) { gpio_direction_output(smsc_hub->pdata->xo_clk_gpio, 1); } return ret; } #endif Loading
include/linux/smsc_hub.h +3 −1 Original line number Diff line number Diff line /* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and Loading @@ -14,6 +14,7 @@ #ifndef __LINUX_SMSC3503_H__ #define __LINUX_SMSC3503_H__ #define SMSC3502_ID 3502 #define SMSC3503_ID 3503 #define SMSC4604_ID 4604 #define SMSC3503_I2C_ADDR 0x08 Loading Loading @@ -52,6 +53,7 @@ struct smsc_hub_platform_data { int hub_reset; int refclk_gpio; int int_gpio; int xo_clk_gpio; }; #endif