aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2021-09-30 14:12:27 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2021-09-30 14:12:27 +1000
commitf55f6b5e6c32231229a249b9f5db91ccb1f17352 (patch)
treefb96a4622ca415663ed319553f67f7576de18a3a
parent0c3a5e8a45e83183bf8cc177784b1f96b4ebcc14 (diff)
parentf83d7033d4ec1edd02c0289e71dc77bf3f41fee6 (diff)
downloaddevel-f55f6b5e6c32231229a249b9f5db91ccb1f17352.tar.gz
Merge branch 'extcon-next' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon.git
-rw-r--r--Documentation/devicetree/bindings/extcon/extcon-usbc-tusb320.yaml4
-rw-r--r--drivers/extcon/Kconfig2
-rw-r--r--drivers/extcon/extcon-axp288.c31
-rw-r--r--drivers/extcon/extcon-max3355.c1
-rw-r--r--drivers/extcon/extcon-usb-gpio.c3
-rw-r--r--drivers/extcon/extcon-usbc-tusb320.c163
6 files changed, 192 insertions, 12 deletions
diff --git a/Documentation/devicetree/bindings/extcon/extcon-usbc-tusb320.yaml b/Documentation/devicetree/bindings/extcon/extcon-usbc-tusb320.yaml
index 9875b4d5c356e4..71a9f2e5d0dc18 100644
--- a/Documentation/devicetree/bindings/extcon/extcon-usbc-tusb320.yaml
+++ b/Documentation/devicetree/bindings/extcon/extcon-usbc-tusb320.yaml
@@ -11,7 +11,9 @@ maintainers:
properties:
compatible:
- const: ti,tusb320
+ enum:
+ - ti,tusb320
+ - ti,tusb320l
reg:
maxItems: 1
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index c69d40ae5619a8..aab87c9b35c8a7 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -23,7 +23,7 @@ config EXTCON_ADC_JACK
config EXTCON_AXP288
tristate "X-Power AXP288 EXTCON support"
- depends on MFD_AXP20X && USB_SUPPORT && X86 && ACPI
+ depends on MFD_AXP20X && USB_SUPPORT && X86 && ACPI && IOSF_MBI
select USB_ROLE_SWITCH
help
Say Y here to enable support for USB peripheral detection
diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c
index fdb31954cf2b6f..7c6d5857ff25e3 100644
--- a/drivers/extcon/extcon-axp288.c
+++ b/drivers/extcon/extcon-axp288.c
@@ -24,6 +24,7 @@
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
+#include <asm/iosf_mbi.h>
/* Power source status register */
#define PS_STAT_VBUS_TRIGGER BIT(0)
@@ -215,6 +216,10 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
unsigned int cable = info->previous_cable;
bool vbus_attach = false;
+ ret = iosf_mbi_block_punit_i2c_access();
+ if (ret < 0)
+ return ret;
+
vbus_attach = axp288_get_vbus_attach(info);
if (!vbus_attach)
goto no_vbus;
@@ -253,6 +258,8 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
}
no_vbus:
+ iosf_mbi_unblock_punit_i2c_access();
+
extcon_set_state_sync(info->edev, info->previous_cable, false);
if (info->previous_cable == EXTCON_CHG_USB_SDP)
extcon_set_state_sync(info->edev, EXTCON_USB, false);
@@ -275,6 +282,8 @@ no_vbus:
return 0;
dev_det_ret:
+ iosf_mbi_unblock_punit_i2c_access();
+
if (ret < 0)
dev_err(info->dev, "failed to detect BC Mod\n");
@@ -305,13 +314,23 @@ static irqreturn_t axp288_extcon_isr(int irq, void *data)
return IRQ_HANDLED;
}
-static void axp288_extcon_enable(struct axp288_extcon_info *info)
+static int axp288_extcon_enable(struct axp288_extcon_info *info)
{
+ int ret = 0;
+
+ ret = iosf_mbi_block_punit_i2c_access();
+ if (ret < 0)
+ return ret;
+
regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
BC_GLOBAL_RUN, 0);
/* Enable the charger detection logic */
regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
BC_GLOBAL_RUN, BC_GLOBAL_RUN);
+
+ iosf_mbi_unblock_punit_i2c_access();
+
+ return ret;
}
static void axp288_put_role_sw(void *data)
@@ -384,10 +403,16 @@ static int axp288_extcon_probe(struct platform_device *pdev)
}
}
+ ret = iosf_mbi_block_punit_i2c_access();
+ if (ret < 0)
+ return ret;
+
info->vbus_attach = axp288_get_vbus_attach(info);
axp288_extcon_log_rsi(info);
+ iosf_mbi_unblock_punit_i2c_access();
+
/* Initialize extcon device */
info->edev = devm_extcon_dev_allocate(&pdev->dev,
axp288_extcon_cables);
@@ -441,7 +466,9 @@ static int axp288_extcon_probe(struct platform_device *pdev)
}
/* Start charger cable type detection */
- axp288_extcon_enable(info);
+ ret = axp288_extcon_enable(info);
+ if (ret < 0)
+ return ret;
device_init_wakeup(dev, true);
platform_set_drvdata(pdev, info);
diff --git a/drivers/extcon/extcon-max3355.c b/drivers/extcon/extcon-max3355.c
index fa01926c09f168..d7795607f6935d 100644
--- a/drivers/extcon/extcon-max3355.c
+++ b/drivers/extcon/extcon-max3355.c
@@ -7,7 +7,6 @@
*/
#include <linux/extcon-provider.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c
index f06be6d4e2a9cf..0cb440bdd5cbed 100644
--- a/drivers/extcon/extcon-usb-gpio.c
+++ b/drivers/extcon/extcon-usb-gpio.c
@@ -7,18 +7,17 @@
*/
#include <linux/extcon-provider.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/mod_devicetable.h>
#define USB_GPIO_DEBOUNCE_MS 20 /* ms */
diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c
index 805af73b41521c..6ba3d89b106d05 100644
--- a/drivers/extcon/extcon-usbc-tusb320.c
+++ b/drivers/extcon/extcon-usbc-tusb320.c
@@ -19,15 +19,42 @@
#define TUSB320_REG9_ATTACHED_STATE_MASK 0x3
#define TUSB320_REG9_CABLE_DIRECTION BIT(5)
#define TUSB320_REG9_INTERRUPT_STATUS BIT(4)
-#define TUSB320_ATTACHED_STATE_NONE 0x0
-#define TUSB320_ATTACHED_STATE_DFP 0x1
-#define TUSB320_ATTACHED_STATE_UFP 0x2
-#define TUSB320_ATTACHED_STATE_ACC 0x3
+
+#define TUSB320_REGA 0xa
+#define TUSB320L_REGA_DISABLE_TERM BIT(0)
+#define TUSB320_REGA_I2C_SOFT_RESET BIT(3)
+#define TUSB320_REGA_MODE_SELECT_SHIFT 4
+#define TUSB320_REGA_MODE_SELECT_MASK 0x3
+
+#define TUSB320L_REGA0_REVISION 0xa0
+
+enum tusb320_attached_state {
+ TUSB320_ATTACHED_STATE_NONE,
+ TUSB320_ATTACHED_STATE_DFP,
+ TUSB320_ATTACHED_STATE_UFP,
+ TUSB320_ATTACHED_STATE_ACC,
+};
+
+enum tusb320_mode {
+ TUSB320_MODE_PORT,
+ TUSB320_MODE_UFP,
+ TUSB320_MODE_DFP,
+ TUSB320_MODE_DRP,
+};
+
+struct tusb320_priv;
+
+struct tusb320_ops {
+ int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode);
+ int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision);
+};
struct tusb320_priv {
struct device *dev;
struct regmap *regmap;
struct extcon_dev *edev;
+ struct tusb320_ops *ops;
+ enum tusb320_attached_state state;
};
static const char * const tusb_attached_states[] = {
@@ -62,6 +89,101 @@ static int tusb320_check_signature(struct tusb320_priv *priv)
return 0;
}
+static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
+{
+ int ret;
+
+ /* Mode cannot be changed while cable is attached */
+ if (priv->state != TUSB320_ATTACHED_STATE_NONE)
+ return -EBUSY;
+
+ /* Write mode */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
+ mode << TUSB320_REGA_MODE_SELECT_SHIFT);
+ if (ret) {
+ dev_err(priv->dev, "failed to write mode: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
+{
+ int ret;
+
+ /* Disable CC state machine */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320L_REGA_DISABLE_TERM, 1);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to disable CC state machine: %d\n", ret);
+ return ret;
+ }
+
+ /* Write mode */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
+ mode << TUSB320_REGA_MODE_SELECT_SHIFT);
+ if (ret) {
+ dev_err(priv->dev, "failed to write mode: %d\n", ret);
+ goto err;
+ }
+
+ msleep(5);
+err:
+ /* Re-enable CC state machine */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320L_REGA_DISABLE_TERM, 0);
+ if (ret)
+ dev_err(priv->dev,
+ "failed to re-enable CC state machine: %d\n", ret);
+
+ return ret;
+}
+
+static int tusb320_reset(struct tusb320_priv *priv)
+{
+ int ret;
+
+ /* Set mode to default (follow PORT pin) */
+ ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT);
+ if (ret && ret != -EBUSY) {
+ dev_err(priv->dev,
+ "failed to set mode to PORT: %d\n", ret);
+ return ret;
+ }
+
+ /* Perform soft reset */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320_REGA_I2C_SOFT_RESET, 1);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to write soft reset bit: %d\n", ret);
+ return ret;
+ }
+
+ /* Wait for chip to go through reset */
+ msleep(95);
+
+ return 0;
+}
+
+static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision)
+{
+ return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision);
+}
+
+static struct tusb320_ops tusb320_ops = {
+ .set_mode = tusb320_set_mode,
+};
+
+static struct tusb320_ops tusb320l_ops = {
+ .set_mode = tusb320l_set_mode,
+ .get_revision = tusb320l_get_revision,
+};
+
static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
{
struct tusb320_priv *priv = dev_id;
@@ -96,6 +218,8 @@ static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
extcon_sync(priv->edev, EXTCON_USB);
extcon_sync(priv->edev, EXTCON_USB_HOST);
+ priv->state = state;
+
regmap_write(priv->regmap, TUSB320_REG9, reg);
return IRQ_HANDLED;
@@ -110,6 +234,8 @@ static int tusb320_extcon_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tusb320_priv *priv;
+ const void *match_data;
+ unsigned int revision;
int ret;
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
@@ -125,12 +251,27 @@ static int tusb320_extcon_probe(struct i2c_client *client,
if (ret)
return ret;
+ match_data = device_get_match_data(&client->dev);
+ if (!match_data)
+ return -EINVAL;
+
+ priv->ops = (struct tusb320_ops*)match_data;
+
priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable);
if (IS_ERR(priv->edev)) {
dev_err(priv->dev, "failed to allocate extcon device\n");
return PTR_ERR(priv->edev);
}
+ if (priv->ops->get_revision) {
+ ret = priv->ops->get_revision(priv, &revision);
+ if (ret)
+ dev_warn(priv->dev,
+ "failed to read revision register: %d\n", ret);
+ else
+ dev_info(priv->dev, "chip revision %d\n", revision);
+ }
+
ret = devm_extcon_dev_register(priv->dev, priv->edev);
if (ret < 0) {
dev_err(priv->dev, "failed to register extcon device\n");
@@ -145,6 +286,17 @@ static int tusb320_extcon_probe(struct i2c_client *client,
/* update initial state */
tusb320_irq_handler(client->irq, priv);
+ /* Reset chip to its default state */
+ ret = tusb320_reset(priv);
+ if (ret)
+ dev_warn(priv->dev, "failed to reset chip: %d\n", ret);
+ else
+ /*
+ * State and polarity might change after a reset, so update
+ * them again and make sure the interrupt status bit is cleared.
+ */
+ tusb320_irq_handler(client->irq, priv);
+
ret = devm_request_threaded_irq(priv->dev, client->irq, NULL,
tusb320_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
@@ -154,7 +306,8 @@ static int tusb320_extcon_probe(struct i2c_client *client,
}
static const struct of_device_id tusb320_extcon_dt_match[] = {
- { .compatible = "ti,tusb320", },
+ { .compatible = "ti,tusb320", .data = &tusb320_ops, },
+ { .compatible = "ti,tusb320l", .data = &tusb320l_ops, },
{ }
};
MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match);