summaryrefslogtreecommitdiff
path: root/drivers/usb/host/max3421-hcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/max3421-hcd.c')
-rw-r--r--drivers/usb/host/max3421-hcd.c81
1 files changed, 78 insertions, 3 deletions
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 0ece9a9341e5..afa321ab55fc 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* MAX3421 Host Controller driver for USB.
*
@@ -60,6 +61,7 @@
#include <linux/spi/spi.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/of.h>
#include <linux/platform_data/max3421-hcd.h>
@@ -85,6 +87,8 @@
USB_PORT_STAT_C_OVERCURRENT | \
USB_PORT_STAT_C_RESET) << 16)
+#define MAX3421_GPOUT_COUNT 8
+
enum max3421_rh_state {
MAX3421_RH_RESET,
MAX3421_RH_SUSPENDED,
@@ -1672,7 +1676,7 @@ max3421_gpout_set_value(struct usb_hcd *hcd, u8 pin_number, u8 value)
u8 mask, idx;
--pin_number;
- if (pin_number > 7)
+ if (pin_number >= MAX3421_GPOUT_COUNT)
return;
mask = 1u << (pin_number % 4);
@@ -1696,10 +1700,10 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
unsigned long flags;
int retval = 0;
- spin_lock_irqsave(&max3421_hcd->lock, flags);
-
pdata = spi->dev.platform_data;
+ spin_lock_irqsave(&max3421_hcd->lock, flags);
+
switch (type_req) {
case ClearHubFeature:
break;
@@ -1832,10 +1836,34 @@ static const struct hc_driver max3421_hcd_desc = {
};
static int
+max3421_of_vbus_en_pin(struct device *dev, struct max3421_hcd_platform_data *pdata)
+{
+ int retval;
+ uint32_t value[2];
+
+ if (!pdata)
+ return -EINVAL;
+
+ retval = of_property_read_u32_array(dev->of_node, "maxim,vbus-en-pin", value, 2);
+ if (retval) {
+ dev_err(dev, "device tree node property 'maxim,vbus-en-pin' is missing\n");
+ return retval;
+ }
+ dev_info(dev, "property 'maxim,vbus-en-pin' value is <%d %d>\n", value[0], value[1]);
+
+ pdata->vbus_gpout = value[0];
+ pdata->vbus_active_level = value[1];
+
+ return 0;
+}
+
+static int
max3421_probe(struct spi_device *spi)
{
+ struct device *dev = &spi->dev;
struct max3421_hcd *max3421_hcd;
struct usb_hcd *hcd = NULL;
+ struct max3421_hcd_platform_data *pdata = NULL;
int retval = -ENOMEM;
if (spi_setup(spi) < 0) {
@@ -1843,6 +1871,41 @@ max3421_probe(struct spi_device *spi)
return -EFAULT;
}
+ if (!spi->irq) {
+ dev_err(dev, "Failed to get SPI IRQ");
+ return -EFAULT;
+ }
+
+ if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
+ pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ retval = -ENOMEM;
+ goto error;
+ }
+ retval = max3421_of_vbus_en_pin(dev, pdata);
+ if (retval)
+ goto error;
+
+ spi->dev.platform_data = pdata;
+ }
+
+ pdata = spi->dev.platform_data;
+ if (!pdata) {
+ dev_err(&spi->dev, "driver configuration data is not provided\n");
+ retval = -EFAULT;
+ goto error;
+ }
+ if (pdata->vbus_active_level > 1) {
+ dev_err(&spi->dev, "vbus active level value %d is out of range (0/1)\n", pdata->vbus_active_level);
+ retval = -EINVAL;
+ goto error;
+ }
+ if (pdata->vbus_gpout < 1 || pdata->vbus_gpout > MAX3421_GPOUT_COUNT) {
+ dev_err(&spi->dev, "vbus gpout value %d is out of range (1..8)\n", pdata->vbus_gpout);
+ retval = -EINVAL;
+ goto error;
+ }
+
hcd = usb_create_hcd(&max3421_hcd_desc, &spi->dev,
dev_name(&spi->dev));
if (!hcd) {
@@ -1885,6 +1948,11 @@ max3421_probe(struct spi_device *spi)
return 0;
error:
+ if (IS_ENABLED(CONFIG_OF) && dev->of_node && pdata) {
+ devm_kfree(&spi->dev, pdata);
+ spi->dev.platform_data = NULL;
+ }
+
if (hcd) {
kfree(max3421_hcd->tx);
kfree(max3421_hcd->rx);
@@ -1929,11 +1997,18 @@ max3421_remove(struct spi_device *spi)
return 0;
}
+static const struct of_device_id max3421_of_match_table[] = {
+ { .compatible = "maxim,max3421", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max3421_of_match_table);
+
static struct spi_driver max3421_driver = {
.probe = max3421_probe,
.remove = max3421_remove,
.driver = {
.name = "max3421-hcd",
+ .of_match_table = of_match_ptr(max3421_of_match_table),
},
};