meta-st-stm32mp/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0053-ARM-stm32mp1-r0-rc4-ho...

1146 lines
34 KiB
Diff

From 0ab8d3145b982810bf5b1c1d2388f5c3820151a8 Mon Sep 17 00:00:00 2001
From: christophe montaud <christophe.montaud@st.com>
Date: Mon, 14 Jan 2019 17:18:56 +0100
Subject: [PATCH 53/55] ARM stm32mp1 r0 rc4 hotfix-w903.1 DRIVERS
---
.../bindings/connector/usb-connector.txt | 2 +
.../devicetree/bindings/usb/st,typec-stusb.txt | 32 ++
drivers/bluetooth/hci_bcm.c | 3 +-
drivers/gpu/drm/stm/dw_mipi_dsi-stm.c | 9 +-
drivers/iio/adc/stm32-dfsdm-adc.c | 5 +-
drivers/media/platform/stm32/stm32-dcmi.c | 17 +
drivers/mfd/stm32-pwr.c | 14 -
.../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 5 +
drivers/remoteproc/remoteproc_core.c | 10 +
drivers/remoteproc/remoteproc_virtio.c | 2 +
drivers/remoteproc/stm32_rproc.c | 63 ++-
drivers/usb/typec/Kconfig | 9 +
drivers/usb/typec/Makefile | 1 +
drivers/usb/typec/class.c | 15 +
drivers/usb/typec/typec_stusb.c | 589 +++++++++++++++++++++
include/linux/usb/typec.h | 1 +
sound/soc/stm/stm32_adfsdm.c | 21 +-
17 files changed, 761 insertions(+), 37 deletions(-)
create mode 100644 Documentation/devicetree/bindings/usb/st,typec-stusb.txt
create mode 100644 drivers/usb/typec/typec_stusb.c
diff --git a/Documentation/devicetree/bindings/connector/usb-connector.txt b/Documentation/devicetree/bindings/connector/usb-connector.txt
index 8855bfc..bf43ee9 100644
--- a/Documentation/devicetree/bindings/connector/usb-connector.txt
+++ b/Documentation/devicetree/bindings/connector/usb-connector.txt
@@ -18,6 +18,8 @@ Optional properties:
Optional properties for usb-c-connector:
- power-role: should be one of "source", "sink" or "dual"(DRP) if typec
connector has power support.
+- power-opmode: should be one of "default", "1.5A", "3.0A" or
+ "usb_power_delivery" if typec connector has power support.
- try-power-role: preferred power role if "dual"(DRP) can support Try.SNK
or Try.SRC, should be "sink" for Try.SNK or "source" for Try.SRC.
- data-role: should be one of "host", "device", "dual"(DRD) if typec
diff --git a/Documentation/devicetree/bindings/usb/st,typec-stusb.txt b/Documentation/devicetree/bindings/usb/st,typec-stusb.txt
new file mode 100644
index 0000000..817360d
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/st,typec-stusb.txt
@@ -0,0 +1,32 @@
+STMicroelectronics STUSB Type-C Controller family
+
+Required properties:
+ - compatible: should be "st,stusb1600".
+ - reg: I2C slave address of the device.
+
+Optional properties:
+ - vdd-supply: main power supply [4.1V;22V].
+ - vsys-supply: low power supply [3.0V;5.5V].
+ - vconn-supply: power supply [2.7;5.5V] used to supply VConn on CC pin in
+ source or dual power role.
+
+USB-C connector attached to STUSB Type-C port controller can be described in
+an optional connector sub-node. Refer to ../connector/usb-connector.txt.
+
+Example :
+
+ typec: stusb1600@28 {
+ compatible = "st,stusb1600";
+ reg = <0x28>;
+ vdd-supply = <&vbus_drd>;
+ vsys-supply = <&vdd_usb>;
+
+ usb_con: connector {
+ compatible = "usb-c-connector";
+ label = "USB-C";
+ power-role = "dual";
+ power-opmode = "1.5A";
+ data-role = "dual";
+ };
+ };
+
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index ddbd8c6..7a23fee 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -1295,7 +1295,8 @@ static int bcm_serdev_probe(struct serdev_device *serdev)
if (!bcmdev->shutdown) {
dev_warn(&serdev->dev,
"No reset resource, using default baud rate\n");
- bcmdev->oper_speed = bcmdev->init_speed;
+ if (!bcmdev->oper_speed)
+ bcmdev->oper_speed = bcmdev->init_speed;
}
err = bcm_gpio_set_power(bcmdev, false);
diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
index a6edd86..a373651 100644
--- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
+++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
@@ -227,7 +227,6 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode,
u32 val;
/* Update lane capabilities according to hw version */
- dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
dsi->lane_min_kbps = LANE_MIN_KBPS;
dsi->lane_max_kbps = LANE_MAX_KBPS;
if (dsi->hw_version == HWVER_131) {
@@ -347,6 +346,14 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
return ret;
}
+ dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
+ if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) {
+ dev_err(dev, "bad dsi hardware version\n");
+ clk_disable_unprepare(dsi->pllref_clk);
+ regulator_disable(dsi->vdd_supply);
+ return -ENODEV;
+ }
+
dw_mipi_dsi_stm_plat_data.base = dsi->base;
dw_mipi_dsi_stm_plat_data.priv_data = dsi;
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index c97d9ee..13854b7 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -569,8 +569,9 @@ static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev,
oversamp = DIV_ROUND_CLOSEST(spi_freq, sample_freq);
if (spi_freq % sample_freq)
- dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
- spi_freq / oversamp);
+ dev_dbg(&indio_dev->dev,
+ "Rate not accurate. requested (%u), actual (%u)\n",
+ sample_freq, spi_freq / oversamp);
ret = stm32_dfsdm_set_osrs(fl, 0, oversamp);
if (ret < 0) {
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
index 49849e6..6f6dc66 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -167,6 +167,9 @@ struct stm32_dcmi {
int errors_count;
int overrun_count;
int buffers_count;
+
+ /* Ensure DMA operations atomicity */
+ struct mutex dma_lock;
};
static inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n)
@@ -317,6 +320,13 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
return ret;
}
+ /*
+ * Avoid call of dmaengine_terminate_all() between
+ * dmaengine_prep_slave_single() and dmaengine_submit()
+ * by locking the whole DMA submission sequence
+ */
+ mutex_lock(&dcmi->dma_lock);
+
/* Prepare a DMA transaction */
desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr,
buf->size,
@@ -325,6 +335,7 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
if (!desc) {
dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer phy=%pad size=%zu\n",
__func__, &buf->paddr, buf->size);
+ mutex_unlock(&dcmi->dma_lock);
return -EINVAL;
}
@@ -336,9 +347,12 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
dcmi->dma_cookie = dmaengine_submit(desc);
if (dma_submit_error(dcmi->dma_cookie)) {
dev_err(dcmi->dev, "%s: DMA submission failed\n", __func__);
+ mutex_unlock(&dcmi->dma_lock);
return -ENXIO;
}
+ mutex_unlock(&dcmi->dma_lock);
+
dma_async_issue_pending(dcmi->dma_chan);
return 0;
@@ -746,7 +760,9 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
spin_unlock_irq(&dcmi->irqlock);
/* Stop all pending DMA operations */
+ mutex_lock(&dcmi->dma_lock);
dmaengine_terminate_all(dcmi->dma_chan);
+ mutex_unlock(&dcmi->dma_lock);
pm_runtime_put(dcmi->dev);
@@ -1740,6 +1756,7 @@ static int dcmi_probe(struct platform_device *pdev)
spin_lock_init(&dcmi->irqlock);
mutex_init(&dcmi->lock);
+ mutex_init(&dcmi->dma_lock);
init_completion(&dcmi->complete);
INIT_LIST_HEAD(&dcmi->buffers);
diff --git a/drivers/mfd/stm32-pwr.c b/drivers/mfd/stm32-pwr.c
index 206a933..088b3d6 100644
--- a/drivers/mfd/stm32-pwr.c
+++ b/drivers/mfd/stm32-pwr.c
@@ -70,19 +70,6 @@ static void stm32_pwr_irq_unmask(struct irq_data *d)
SMC(STM32_SVC_PWR, STM32_SET_BITS, MPUWKUPENR, BIT(d->hwirq));
}
-static int stm32_pwr_irq_set_wake(struct irq_data *d, unsigned int on)
-{
- struct stm32_pwr_data *priv = d->domain->host_data;
-
- pr_debug("irq:%lu on:%d\n", d->hwirq, on);
- if (on)
- enable_irq_wake(priv->irq);
- else
- disable_irq_wake(priv->irq);
-
- return 0;
-}
-
static int stm32_pwr_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
struct stm32_pwr_data *priv = d->domain->host_data;
@@ -125,7 +112,6 @@ static struct irq_chip stm32_pwr_irq_chip = {
.irq_mask = stm32_pwr_irq_mask,
.irq_unmask = stm32_pwr_irq_unmask,
.irq_set_type = stm32_pwr_irq_set_type,
- .irq_set_wake = stm32_pwr_irq_set_wake,
};
static int stm32_pwr_irq_set_pull_config(struct irq_domain *d, int pin_id,
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
index ffa243e..55974a4 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
@@ -496,6 +496,11 @@ int brcmf_pno_stop_sched_scan(struct brcmf_if *ifp, u64 reqid)
brcmf_dbg(TRACE, "reqid=%llu\n", reqid);
pi = ifp_to_pno(ifp);
+
+ /* No PNO reqeuset */
+ if (!pi->n_reqs)
+ return 0;
+
err = brcmf_pno_remove_request(pi, reqid);
if (err)
return err;
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index dd7e348..6430747 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -1456,6 +1456,9 @@ static int rproc_stop(struct rproc *rproc, bool crashed)
struct device *dev = &rproc->dev;
int ret;
+ if (rproc->state == RPROC_OFFLINE)
+ return 0;
+
/* Stop any subdevices for the remote processor */
rproc_stop_subdevices(rproc, crashed);
@@ -1614,6 +1617,13 @@ int rproc_trigger_recovery(struct rproc *rproc)
/* generate coredump */
rproc_coredump(rproc);
+ if (!rproc->firmware) {
+ /* we don't know how to recover it, so try to shutdown it*/
+ mutex_unlock(&rproc->lock);
+ rproc_shutdown(rproc);
+ return 0;
+ }
+
/* load firmware */
ret = request_firmware(&firmware_p, rproc->firmware, dev);
if (ret < 0) {
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
index 9ee63c0..78462f5 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -359,6 +359,8 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
}
}
+ /* Reset vdev struct as you don't know how it has been previously allocated */
+ memset(vdev, 0, sizeof(struct virtio_device));
vdev->id.device = id,
vdev->config = &rproc_virtio_config_ops,
vdev->dev.parent = dev;
diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
index 70b7e55c..1d2be11 100644
--- a/drivers/remoteproc/stm32_rproc.c
+++ b/drivers/remoteproc/stm32_rproc.c
@@ -67,7 +67,7 @@ struct stm32_rproc {
struct stm32_rproc_mem *rmems;
struct stm32_mbox mb[MBOX_NB_MBX];
bool secured_soc;
- u32 rsc_addr;
+ void __iomem *rsc_va;
u32 rsc_len;
};
@@ -84,7 +84,27 @@ static int stm32_rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da)
pa >= p_mem->bus_addr + p_mem->size)
continue;
*da = pa - p_mem->bus_addr + p_mem->dev_addr;
- dev_dbg(rproc->dev.parent, "da %llx to pa %#x\n", *da, pa);
+ dev_dbg(rproc->dev.parent, "pa %#x to da %llx\n", pa, *da);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int stm32_rproc_da_to_pa(struct rproc *rproc, u64 da, phys_addr_t *pa)
+{
+ unsigned int i;
+ struct stm32_rproc *ddata = rproc->priv;
+ struct stm32_rproc_mem *p_mem;
+
+ for (i = 0; i < ddata->nb_rmems; i++) {
+ p_mem = &ddata->rmems[i];
+
+ if (da < p_mem->dev_addr ||
+ da >= p_mem->dev_addr + p_mem->size)
+ continue;
+ *pa = da - p_mem->dev_addr + p_mem->bus_addr;
+ dev_err(rproc->dev.parent, "da %llx to pa %#x\n", da, *pa);
return 0;
}
@@ -209,11 +229,9 @@ static int stm32_rproc_elf_load_rsc_table(struct rproc *rproc,
return 0;
}
- if (ddata->rsc_addr) {
+ if (ddata->rsc_va) {
tablesz = ddata->rsc_len;
- table = (struct resource_table *)
- rproc_da_to_va(rproc, (u64)ddata->rsc_addr,
- ddata->rsc_len);
+ table = (struct resource_table *)ddata->rsc_va;
rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL);
if (!rproc->cached_table)
return -ENOMEM;
@@ -297,12 +315,7 @@ stm32_rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
if (!rproc->early_boot)
return rproc_elf_find_loaded_rsc_table(rproc, fw);
- if (ddata->rsc_addr)
- return (struct resource_table *)
- rproc_da_to_va(rproc, (u64)ddata->rsc_addr,
- ddata->rsc_len);
-
- return NULL;
+ return (struct resource_table *)ddata->rsc_va;
}
static int stm32_rproc_elf_sanity_check(struct rproc *rproc,
@@ -582,6 +595,8 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev)
struct rproc *rproc = platform_get_drvdata(pdev);
struct stm32_rproc *ddata = rproc->priv;
struct stm32_syscon tz;
+ phys_addr_t rsc_pa;
+ u32 rsc_da;
unsigned int tzen;
int err, irq;
@@ -636,10 +651,14 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev)
rproc->auto_boot = of_property_read_bool(np, "auto_boot");
rproc->recovery_disabled = !of_property_read_bool(np, "recovery");
+ err = stm32_rproc_of_memory_translations(rproc);
+ if (err)
+ return err;
+
if (of_property_read_bool(np, "early-booted")) {
rproc->early_boot = true;
- err = of_property_read_u32(np, "rsc-address", &ddata->rsc_addr);
+ err = of_property_read_u32(np, "rsc-address", &rsc_da);
if (!err) {
err = of_property_read_u32(np, "rsc-size",
&ddata->rsc_len);
@@ -649,9 +668,19 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev)
return err;
}
}
+ err = stm32_rproc_da_to_pa(rproc, rsc_da, &rsc_pa);
+ if (err)
+ return err;
+ ddata->rsc_va = ioremap_wc(rsc_pa, ddata->rsc_len);
+ if (IS_ERR_OR_NULL(ddata->rsc_va)) {
+ dev_err(dev, "Unable to map memory region: %pa+%zx\n",
+ &rsc_pa, ddata->rsc_len);
+ ddata->rsc_va = NULL;
+ return -ENOMEM;
+ }
}
- return stm32_rproc_of_memory_translations(rproc);
+ return 0;
}
static int stm32_rproc_probe(struct platform_device *pdev)
@@ -699,13 +728,15 @@ static int stm32_rproc_probe(struct platform_device *pdev)
static int stm32_rproc_remove(struct platform_device *pdev)
{
struct rproc *rproc = platform_get_drvdata(pdev);
- struct device *dev = &pdev->dev;
+ struct stm32_rproc *ddata = rproc->priv;
if (atomic_read(&rproc->power) > 0)
- dev_warn(dev, "Releasing rproc while firmware running!\n");
+ rproc_shutdown(rproc);
rproc_del(rproc);
stm32_rproc_free_mbox(rproc);
+ if (ddata->rsc_va)
+ iounmap(ddata->rsc_va);
rproc_free(rproc);
return 0;
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 00878c3..1dbbf16 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -102,6 +102,15 @@ config TYPEC_TPS6598X
If you choose to build this driver as a dynamically linked module, the
module will be called tps6598x.ko.
+config TYPEC_STUSB
+ tristate "STMicroelectronics STUSB Type-C controller driver"
+ depends on I2C
+ select EXTCON
+ help
+ The STMicroelectronics STUSB Type-C controller driver that works
+ with Type-C Port Controller Manager to provide USB Type-C
+ functionalities.
+
source "drivers/usb/typec/mux/Kconfig"
source "drivers/usb/typec/altmodes/Kconfig"
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 45b0aef..aedb153 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -7,6 +7,7 @@ obj-y += fusb302/
obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
obj-$(CONFIG_TYPEC_UCSI) += ucsi/
obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o
+obj-$(CONFIG_TYPEC_STUSB) += typec_stusb.o
obj-$(CONFIG_TYPEC) += mux/
obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index e61dffb..b518360 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1382,6 +1382,21 @@ void typec_set_pwr_opmode(struct typec_port *port,
EXPORT_SYMBOL_GPL(typec_set_pwr_opmode);
/**
+ * typec_find_power_opmode - Get the typec port power operation mode
+ * @name: port power operation mode string
+ *
+ * This routine is used to find the typec_pwr_opmodes by its string name.
+ *
+ * Returns typec_pwr_opmodes if success, otherwise negative error code.
+ */
+int typec_find_port_power_opmode(const char *name)
+{
+ return match_string(typec_pwr_opmodes,
+ ARRAY_SIZE(typec_pwr_opmodes), name);
+}
+EXPORT_SYMBOL_GPL(typec_find_port_power_opmode);
+
+/**
* typec_find_port_power_role - Get the typec port power capability
* @name: port power capability string
*
diff --git a/drivers/usb/typec/typec_stusb.c b/drivers/usb/typec/typec_stusb.c
new file mode 100644
index 0000000..a2884fd
--- /dev/null
+++ b/drivers/usb/typec/typec_stusb.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STUSB Type-C controller family driver
+ *
+ * Copyright (C) 2019, STMicroelectronics
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/typec.h>
+
+#define STUSB_ALERT_STATUS 0x0B /* RC */
+#define STUSB_ALERT_STATUS_MASK_CTRL 0x0C /* RW */
+#define STUSB_CC_CONNECTION_STATUS_TRANS 0x0D /* RC */
+#define STUSB_CC_CONNECTION_STATUS 0x0E /* RO */
+#define STUSB_MONITORING_STATUS_TRANS 0x0F /* RC */
+#define STUSB_MONITORING_STATUS 0x10 /* RO */
+#define STUSB_CC_OPERATION_STATUS 0x11 /* RO */
+#define STUSB_HW_FAULT_STATUS_TRANS 0x12 /* RC */
+#define STUSB_HW_FAULT_STATUS 0x13 /* RO */
+#define STUSB_CC_CAPABILITY_CTRL 0x18 /* RW */
+#define STUSB_CC_VCONN_SWITCH_CTRL 0x1E /* RW */
+#define STUSB_VCONN_MONITORING_CTRL 0x20 /* RW */
+#define STUSB_VBUS_MONITORING_RANGE_CTRL 0x22 /* RW */
+#define STUSB_RESET_CTRL 0x23 /* RW */
+#define STUSB_VBUS_DISCHARGE_TIME_CTRL 0x25 /* RW */
+#define STUSB_VBUS_DISCHARGE_STATUS 0x26 /* RO */
+#define STUSB_VBUS_ENABLE_STATUS 0x27 /* RO */
+#define STUSB_CC_POWER_MODE_CTRL 0x28 /* RW */
+#define STUSB_VBUS_MONITORING_CTRL 0x2E /* RW */
+#define STUSB1600_REG_MAX 0x2F /* RO - Reserved */
+
+/* STUSB_ALERT_STATUS/STUSB_ALERT_STATUS_MASK_CTRL bitfields */
+#define STUSB_HW_FAULT BIT(4)
+#define STUSB_MONITORING BIT(5)
+#define STUSB_CC_CONNECTION BIT(6)
+#define STUSB_ALL_ALERTS GENMASK(6, 4)
+
+/* STUSB_CC_CONNECTION_STATUS_TRANS bitfields */
+#define STUSB_CC_ATTACH_TRANS BIT(0)
+
+/* STUSB_CC_CONNECTION_STATUS bitfields */
+#define STUSB_CC_ATTACH BIT(0)
+#define STUSB_CC_VCONN_SUPPLY BIT(1)
+#define STUSB_CC_DATA_ROLE(s) (!!((s) & BIT(2)))
+#define STUSB_CC_POWER_ROLE(s) (!!((s) & BIT(3)))
+#define STUSB_CC_ATTACHED_MODE GENMASK(7, 5)
+
+/* STUSB_MONITORING_STATUS_TRANS bitfields */
+#define STUSB_VCONN_PRESENCE_TRANS BIT(0)
+#define STUSB_VBUS_PRESENCE_TRANS BIT(1)
+#define STUSB_VBUS_VSAFE0V_TRANS BIT(2)
+#define STUSB_VBUS_VALID_TRANS BIT(3)
+
+/* STUSB_MONITORING_STATUS bitfields */
+#define STUSB_VCONN_PRESENCE BIT(0)
+#define STUSB_VBUS_PRESENCE BIT(1)
+#define STUSB_VBUS_VSAFE0V BIT(2)
+#define STUSB_VBUS_VALID BIT(3)
+
+/* STUSB_CC_OPERATION_STATUS bitfields */
+#define STUSB_TYPEC_FSM_STATE GENMASK(4, 0)
+#define STUSB_SINK_POWER_STATE GENMASK(6, 5)
+#define STUSB_CC_ATTACHED BIT(7)
+
+/* STUSB_HW_FAULT_STATUS_TRANS bitfields */
+#define STUSB_VCONN_SW_OVP_FAULT_TRANS BIT(0)
+#define STUSB_VCONN_SW_OCP_FAULT_TRANS BIT(1)
+#define STUSB_VCONN_SW_RVP_FAULT_TRANS BIT(2)
+#define STUSB_VPU_VALID_TRANS BIT(4)
+#define STUSB_VPU_OVP_FAULT_TRANS BIT(5)
+#define STUSB_THERMAL_FAULT BIT(7)
+
+/* STUSB_HW_FAULT_STATUS bitfields */
+#define STUSB_VCONN_SW_OVP_FAULT_CC2 BIT(0)
+#define STUSB_VCONN_SW_OVP_FAULT_CC1 BIT(1)
+#define STUSB_VCONN_SW_OCP_FAULT_CC2 BIT(2)
+#define STUSB_VCONN_SW_OCP_FAULT_CC1 BIT(3)
+#define STUSB_VCONN_SW_RVP_FAULT_CC2 BIT(4)
+#define STUSB_VCONN_SW_RVP_FAULT_CC1 BIT(5)
+#define STUSB_VPU_VALID BIT(6)
+#define STUSB_VPU_OVP_FAULT BIT(7)
+
+/* STUSB_CC_CAPABILITY_CTRL bitfields */
+#define STUSB_CC_VCONN_SUPPLY_EN BIT(0)
+#define STUSB_CC_VCONN_DISCHARGE_EN BIT(4)
+#define STUSB_CC_CURRENT_ADVERTISED GENMASK(7, 6)
+
+/* STUSB_VCONN_SWITCH_CTRL bitfields */
+#define STUSB_CC_VCONN_SWITCH_ILIM GENMASK(3, 0)
+
+/* STUSB_VCONN_MONITORING_CTRL bitfields */
+#define STUSB_VCONN_UVLO_THRESHOLD BIT(6)
+#define STUSB_VCONN_MONITORING_EN BIT(7)
+
+/* STUSB_VBUS_MONITORING_RANGE_CTRL bitfields */
+#define STUSB_SHIFT_LOW_VBUS_LIMIT GENMASK(3, 0)
+#define STUSB_SHIFT_HIGH_VBUS_LIMIT GENMASK(7, 4)
+
+/* STUSB_RESET_CTRL bitfields */
+#define STUSB_SW_RESET_EN BIT(0)
+
+/* STUSB_VBUS_DISCHARGE_TIME_CTRL bitfields */
+#define STUSBXX02_VBUS_DISCHARGE_TIME_TO_PDO GENMASK(3, 0)
+#define STUSB_VBUS_DISCHARGE_TIME_TO_0V GENMASK(7, 4)
+
+/* STUSB_VBUS_DISCHARGE_STATUS bitfields */
+#define STUSB_VBUS_DISCHARGE_EN BIT(7)
+
+/* STUSB_VBUS_ENABLE_STATUS bitfields */
+#define STUSB_VBUS_SOURCE_EN BIT(0)
+#define STUSB_VBUS_SINK_EN BIT(1)
+
+/* STUSB_CC_POWER_MODE_CTRL bitfields */
+#define STUSB_CC_POWER_MODE GENMASK(2, 0)
+
+/* STUSB_VBUS_MONITORING_CTRL bitfields */
+#define STUSB_VDD_UVLO_DISABLE BIT(0)
+#define STUSB_VBUS_VSAFE0V_THRESHOLD GENMASK(2, 1)
+#define STUSB_VBUS_RANGE_DISABLE BIT(4)
+#define STUSB_VDD_OVLO_DISABLE BIT(6)
+
+enum stusb_pwr_mode {
+ SOURCE_WITH_ACCESSORY,
+ SINK_WITH_ACCESSORY,
+ SINK_WITHOUT_ACCESSORY,
+ DUAL_WITH_ACCESSORY,
+ DUAL_WITH_ACCESSORY_AND_TRY_SRC,
+ DUAL_WITH_ACCESSORY_AND_TRY_SNK,
+};
+
+struct stusb {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator *vdd_supply;
+ struct regulator *vsys_supply;
+ struct regulator *vconn_supply;
+
+ struct typec_port *port;
+ struct typec_capability capability;
+
+ enum typec_port_type port_type;
+ enum typec_pwr_opmode pwr_opmode;
+};
+
+static bool stusb_reg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STUSB_ALERT_STATUS_MASK_CTRL:
+ case STUSB_CC_CAPABILITY_CTRL:
+ case STUSB_CC_VCONN_SWITCH_CTRL:
+ case STUSB_VCONN_MONITORING_CTRL:
+ case STUSB_VBUS_MONITORING_RANGE_CTRL:
+ case STUSB_RESET_CTRL:
+ case STUSB_VBUS_DISCHARGE_TIME_CTRL:
+ case STUSB_CC_POWER_MODE_CTRL:
+ case STUSB_VBUS_MONITORING_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool stusb_reg_readable(struct device *dev, unsigned int reg)
+{
+ if ((reg >= 0x00 && reg <= 0x0A) ||
+ (reg >= 0x14 && reg <= 0x17) ||
+ (reg >= 0x19 && reg <= 0x1D) ||
+ (reg >= 0x29 && reg <= 0x2D) ||
+ (reg == 0x1F || reg == 0x21 || reg == 0x24 || reg == 0x2F))
+ return false;
+ else
+ return true;
+}
+
+static bool stusb_reg_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STUSB_ALERT_STATUS:
+ case STUSB_CC_CONNECTION_STATUS_TRANS:
+ case STUSB_CC_CONNECTION_STATUS:
+ case STUSB_MONITORING_STATUS_TRANS:
+ case STUSB_MONITORING_STATUS:
+ case STUSB_CC_OPERATION_STATUS:
+ case STUSB_HW_FAULT_STATUS_TRANS:
+ case STUSB_HW_FAULT_STATUS:
+ case STUSB_VBUS_DISCHARGE_STATUS:
+ case STUSB_VBUS_ENABLE_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool stusb_reg_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STUSB_ALERT_STATUS:
+ case STUSB_CC_CONNECTION_STATUS_TRANS:
+ case STUSB_MONITORING_STATUS_TRANS:
+ case STUSB_HW_FAULT_STATUS_TRANS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config stusb1600_regmap_config = {
+ .reg_bits = 8,
+ .reg_stride = 1,
+ .val_bits = 8,
+ .max_register = STUSB1600_REG_MAX,
+ .writeable_reg = stusb_reg_writeable,
+ .readable_reg = stusb_reg_readable,
+ .volatile_reg = stusb_reg_volatile,
+ .precious_reg = stusb_reg_precious,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static bool stusb_get_vconn(struct stusb *chip)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(chip->regmap, STUSB_CC_CAPABILITY_CTRL, &val);
+ if (ret) {
+ dev_err(chip->dev, "Unable to get Vconn status: %d\n", ret);
+ return false;
+ }
+
+ return !!FIELD_GET(STUSB_CC_VCONN_SUPPLY_EN, val);;
+}
+
+static int stusb_set_vconn(struct stusb *chip, bool on)
+{
+ int ret;
+
+ /* Manage VCONN input supply */
+ if (chip->vconn_supply) {
+ if (on) {
+ ret = regulator_enable(chip->vconn_supply);
+ if (ret) {
+ dev_err(chip->dev,
+ "failed to enable vconn supply: %d\n",
+ ret);
+ return ret;
+ }
+ } else {
+ regulator_disable(chip->vconn_supply);
+ }
+ }
+
+ /* Manage VCONN monitoring and power path */
+ ret = regmap_update_bits(chip->regmap, STUSB_VCONN_MONITORING_CTRL,
+ STUSB_VCONN_MONITORING_EN,
+ on ? STUSB_VCONN_MONITORING_EN : 0);
+ if (ret)
+ goto vconn_reg_disable;
+
+ return 0;
+
+vconn_reg_disable:
+ if (chip->vconn_supply && on)
+ regulator_disable(chip->vconn_supply);
+
+ return ret;
+}
+
+static enum typec_pwr_opmode stusb_get_pwr_opmode(struct stusb *chip)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(chip->regmap, STUSB_CC_CAPABILITY_CTRL, &val);
+ if (ret) {
+ dev_err(chip->dev, "Unable to get pwr opmode: %d\n", ret);
+ return TYPEC_PWR_MODE_USB;
+ }
+
+ return FIELD_GET(STUSB_CC_CURRENT_ADVERTISED, val);
+}
+
+static int stusb_init(struct stusb *chip)
+{
+ int ret;
+
+ /* Change the default Type-C power mode */
+ if (chip->port_type == TYPEC_PORT_SRC)
+ ret = regmap_update_bits(chip->regmap,
+ STUSB_CC_POWER_MODE_CTRL,
+ STUSB_CC_POWER_MODE,
+ SOURCE_WITH_ACCESSORY);
+ else if (chip->port_type == TYPEC_PORT_SNK)
+ ret = regmap_update_bits(chip->regmap,
+ STUSB_CC_POWER_MODE_CTRL,
+ STUSB_CC_POWER_MODE,
+ SINK_WITH_ACCESSORY);
+ else /* (capability->type == TYPEC_PORT_DRP) */
+ ret = regmap_update_bits(chip->regmap,
+ STUSB_CC_POWER_MODE_CTRL,
+ STUSB_CC_POWER_MODE,
+ DUAL_WITH_ACCESSORY);
+ if (ret)
+ return ret;
+
+ if (chip->port_type == TYPEC_PORT_SNK)
+ return 0;
+
+ /* Change the default Type-C Source power operation mode capability */
+ ret = regmap_update_bits(chip->regmap, STUSB_CC_CAPABILITY_CTRL,
+ STUSB_CC_CURRENT_ADVERTISED,
+ FIELD_PREP(STUSB_CC_CURRENT_ADVERTISED,
+ chip->pwr_opmode));
+ if (ret)
+ return ret;
+
+ /* Manage Type-C Source Vconn supply */
+ if (stusb_get_vconn(chip)) {
+ ret = stusb_set_vconn(chip, true);
+ if (ret)
+ return ret;
+ }
+
+ /* Mask all events interrupts - to be unmasked with interrupt support */
+ ret = regmap_update_bits(chip->regmap, STUSB_ALERT_STATUS_MASK_CTRL,
+ STUSB_ALL_ALERTS, STUSB_ALL_ALERTS);
+
+ return ret;
+}
+
+static int stusb_fw_get_caps(struct stusb *chip)
+{
+ struct fwnode_handle *fwnode = device_get_named_child_node(chip->dev,
+ "connector");
+ const char *cap_str;
+ int ret;
+
+ if (!fwnode)
+ return -EINVAL;
+
+ chip->capability.fwnode = fwnode;
+
+ ret = fwnode_property_read_string(fwnode, "power-role", &cap_str);
+ if (!ret) {
+ chip->port_type = typec_find_port_power_role(cap_str);
+ if (chip->port_type < 0)
+ return -EINVAL;
+
+ chip->capability.type = chip->port_type;
+ }
+
+ if (chip->port_type == TYPEC_PORT_SNK)
+ goto sink;
+
+ if (chip->port_type == TYPEC_PORT_DRP)
+ chip->capability.prefer_role = TYPEC_SINK;
+
+ ret = fwnode_property_read_string(fwnode, "power-opmode", &cap_str);
+ if (!ret) {
+ chip->pwr_opmode = typec_find_port_power_opmode(cap_str);
+
+ /* Power delivery not yet supported */
+ if (chip->pwr_opmode < 0 ||
+ chip->pwr_opmode == TYPEC_PWR_MODE_PD) {
+ dev_err(chip->dev, "bad power operation mode: %d\n",
+ chip->pwr_opmode);
+ return -EINVAL;
+ }
+
+ } else {
+ chip->pwr_opmode = stusb_get_pwr_opmode(chip);
+ }
+
+sink:
+ return 0;
+}
+
+static int stusb_get_caps(struct stusb *chip, bool *try)
+{
+ enum typec_port_type *type = &chip->capability.type;
+ enum typec_port_data *data = &chip->capability.data;
+ enum typec_accessory *accessory = chip->capability.accessory;
+ u32 val;
+ int ret;
+
+ chip->capability.revision = USB_TYPEC_REV_1_2;
+
+ ret = regmap_read(chip->regmap, STUSB_CC_POWER_MODE_CTRL, &val);
+ if (ret)
+ return ret;
+
+ *try = false;
+
+ switch (FIELD_GET(STUSB_CC_POWER_MODE, val)) {
+ case SOURCE_WITH_ACCESSORY:
+ *type = TYPEC_PORT_SRC;
+ *data = TYPEC_PORT_DFP;
+ *accessory++ = TYPEC_ACCESSORY_AUDIO;
+ *accessory++ = TYPEC_ACCESSORY_DEBUG;
+ break;
+ case SINK_WITH_ACCESSORY:
+ *type = TYPEC_PORT_SNK;
+ *data = TYPEC_PORT_UFP;
+ *accessory++ = TYPEC_ACCESSORY_AUDIO;
+ *accessory++ = TYPEC_ACCESSORY_DEBUG;
+ break;
+ case SINK_WITHOUT_ACCESSORY:
+ *type = TYPEC_PORT_SNK;
+ *data = TYPEC_PORT_UFP;
+ break;
+ case DUAL_WITH_ACCESSORY:
+ *type = TYPEC_PORT_DRP;
+ *data = TYPEC_PORT_DRD;
+ *accessory++ = TYPEC_ACCESSORY_AUDIO;
+ *accessory++ = TYPEC_ACCESSORY_DEBUG;
+ break;
+ case DUAL_WITH_ACCESSORY_AND_TRY_SRC:
+ case DUAL_WITH_ACCESSORY_AND_TRY_SNK:
+ *type = TYPEC_PORT_DRP;
+ *data = TYPEC_PORT_DRD;
+ *accessory++ = TYPEC_ACCESSORY_AUDIO;
+ *accessory++ = TYPEC_ACCESSORY_DEBUG;
+ *try = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ chip->port_type = *type;
+
+ return stusb_fw_get_caps(chip);
+}
+
+static const struct of_device_id stusb_of_match[] = {
+ { .compatible = "st,stusb1600", .data = &stusb1600_regmap_config},
+ {},
+};
+
+static int stusb_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct stusb *chip;
+ const struct of_device_id *match;
+ struct regmap_config *regmap_config;
+ bool try_role;
+ int ret;
+
+ chip = devm_kzalloc(&client->dev, sizeof(struct stusb), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, chip);
+
+ match = i2c_of_match_device(stusb_of_match, client);
+ regmap_config = (struct regmap_config *)match->data;
+ chip->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ ret = PTR_ERR(chip->regmap);
+ dev_err(&client->dev,
+ "Failed to allocate register map:%d\n", ret);
+ return ret;
+ }
+
+ chip->dev = &client->dev;
+
+ chip->vdd_supply = devm_regulator_get_optional(chip->dev, "vdd");
+ if (IS_ERR(chip->vdd_supply)) {
+ ret = PTR_ERR(chip->vdd_supply);
+ if (ret != -ENODEV)
+ return ret;
+ chip->vdd_supply = NULL;
+ } else {
+ ret = regulator_enable(chip->vdd_supply);
+ if (ret) {
+ dev_err(chip->dev,
+ "Failed to enable vdd supply: %d\n", ret);
+ return ret;
+ }
+ }
+
+ chip->vsys_supply = devm_regulator_get_optional(chip->dev, "vsys");
+ if (IS_ERR(chip->vsys_supply)) {
+ ret = PTR_ERR(chip->vsys_supply);
+ if (ret != -ENODEV)
+ goto vdd_reg_disable;
+ chip->vsys_supply = NULL;
+ } else {
+ ret = regulator_enable(chip->vsys_supply);
+ if (ret) {
+ dev_err(chip->dev,
+ "Failed to enable vsys supply: %d\n", ret);
+ goto vdd_reg_disable;
+ }
+ }
+
+ chip->vconn_supply = devm_regulator_get_optional(chip->dev, "vconn");
+ if (IS_ERR(chip->vconn_supply)) {
+ ret = PTR_ERR(chip->vconn_supply);
+ if (ret != -ENODEV)
+ goto vsys_reg_disable;
+ chip->vconn_supply = NULL;
+ }
+
+ ret = stusb_get_caps(chip, &try_role);
+ if (ret) {
+ dev_err(chip->dev, "failed to get port caps: %d\n", ret);
+ goto vsys_reg_disable;
+ }
+
+ ret = stusb_init(chip);
+ if (ret) {
+ dev_err(chip->dev, "failed to init port: %d\n", ret);
+ goto vsys_reg_disable;
+ }
+
+ chip->port = typec_register_port(chip->dev, &chip->capability);
+ if (!chip->port) {
+ ret = -ENODEV;
+ goto all_reg_disable;
+ }
+
+ /* To be moved in attach/detach procedure with interrupt support */
+ typec_set_pwr_opmode(chip->port, chip->pwr_opmode);
+
+ dev_info(chip->dev, "STUSB driver registered\n");
+
+ return 0;
+
+all_reg_disable:
+ if (stusb_get_vconn(chip))
+ stusb_set_vconn(chip, false);
+vsys_reg_disable:
+ if (chip->vsys_supply)
+ regulator_disable(chip->vsys_supply);
+vdd_reg_disable:
+ if (chip->vdd_supply)
+ regulator_disable(chip->vdd_supply);
+
+ return ret;
+}
+
+static int stusb_remove(struct i2c_client *client)
+{
+ struct stusb *chip = i2c_get_clientdata(client);
+
+ typec_unregister_port(chip->port);
+
+ if (stusb_get_vconn(chip))
+ stusb_set_vconn(chip, false);
+
+ if (chip->vdd_supply)
+ regulator_disable(chip->vdd_supply);
+
+ if (chip->vsys_supply)
+ regulator_disable(chip->vsys_supply);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stusb_resume(struct device *dev)
+{
+ struct stusb *chip = dev_get_drvdata(dev);
+
+ return stusb_init(chip);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stusb_pm_ops, NULL, stusb_resume);
+
+static struct i2c_driver stusb_driver = {
+ .driver = {
+ .name = "typec_stusb",
+ .pm = &stusb_pm_ops,
+ .of_match_table = stusb_of_match,
+ },
+ .probe = stusb_probe,
+ .remove = stusb_remove,
+};
+module_i2c_driver(stusb_driver);
+
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STUSB Type-C controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index 7df4eca..2671776 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -241,6 +241,7 @@ int typec_set_orientation(struct typec_port *port,
enum typec_orientation typec_get_orientation(struct typec_port *port);
int typec_set_mode(struct typec_port *port, int mode);
+int typec_find_port_power_opmode(const char *name);
int typec_find_port_power_role(const char *name);
int typec_find_power_role(const char *name);
int typec_find_port_data_role(const char *name);
diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
index 71d341b..24948b9 100644
--- a/sound/soc/stm/stm32_adfsdm.c
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -304,6 +304,7 @@ MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match);
static int stm32_adfsdm_probe(struct platform_device *pdev)
{
struct stm32_adfsdm_priv *priv;
+ struct snd_soc_component *component;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@@ -331,9 +332,15 @@ static int stm32_adfsdm_probe(struct platform_device *pdev)
if (IS_ERR(priv->iio_cb))
return PTR_ERR(priv->iio_cb);
- ret = devm_snd_soc_register_component(&pdev->dev,
- &stm32_adfsdm_soc_platform,
- NULL, 0);
+ component = devm_kzalloc(&pdev->dev, sizeof(*component), GFP_KERNEL);
+ if (!component)
+ return -ENOMEM;
+#ifdef CONFIG_DEBUG_FS
+ component->debugfs_prefix = "pcm";
+#endif
+
+ ret = snd_soc_add_component(&pdev->dev, component,
+ &stm32_adfsdm_soc_platform, NULL, 0);
if (ret < 0)
dev_err(&pdev->dev, "%s: Failed to register PCM platform\n",
__func__);
@@ -341,12 +348,20 @@ static int stm32_adfsdm_probe(struct platform_device *pdev)
return ret;
}
+static int stm32_adfsdm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+
+ return 0;
+}
+
static struct platform_driver stm32_adfsdm_driver = {
.driver = {
.name = STM32_ADFSDM_DRV_NAME,
.of_match_table = stm32_adfsdm_of_match,
},
.probe = stm32_adfsdm_probe,
+ .remove = stm32_adfsdm_remove,
};
module_platform_driver(stm32_adfsdm_driver);
--
2.7.4