Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf6579e4 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "USB: HSIC SMSC HUB: Add support for 3502 model"

parents 25a03bb6 690a5a33
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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".
@@ -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
+161 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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);
@@ -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");
@@ -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)
{
@@ -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;
}

@@ -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
@@ -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);

@@ -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;
@@ -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) {
@@ -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;
}

@@ -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) {
@@ -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
+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
@@ -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
@@ -52,6 +53,7 @@ struct smsc_hub_platform_data {
	int hub_reset;
	int refclk_gpio;
	int int_gpio;
	int xo_clk_gpio;
};

#endif