2146 lines
67 KiB
Diff
2146 lines
67 KiB
Diff
From fc843f64f154b843cd7834d6748b409ef1e1487f Mon Sep 17 00:00:00 2001
|
|
From: Lionel Vitte <lionel.vitte@st.com>
|
|
Date: Thu, 14 Oct 2021 16:51:44 +0200
|
|
Subject: [PATCH 08/23] ARM 5.10.61-stm32mp1-r2 I2C-IIO-IRQCHIP
|
|
|
|
---
|
|
drivers/i2c/busses/i2c-stm32f7.c | 285 +++++++++++-------
|
|
drivers/iio/adc/sd_adc_modulator.c | 89 +++++-
|
|
drivers/iio/adc/stm32-adc-core.c | 21 +-
|
|
drivers/iio/adc/stm32-adc-core.h | 8 +
|
|
drivers/iio/adc/stm32-adc.c | 445 ++++++++++++++++++++++++-----
|
|
drivers/iio/adc/stm32-dfsdm-adc.c | 108 ++++++-
|
|
drivers/iio/adc/stm32-dfsdm-core.c | 91 ++++--
|
|
drivers/iio/adc/stm32-dfsdm.h | 69 +++--
|
|
drivers/irqchip/irq-stm32-exti.c | 89 +++++-
|
|
9 files changed, 973 insertions(+), 232 deletions(-)
|
|
|
|
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
|
|
index 1e800b65e..0bc9acef2 100644
|
|
--- a/drivers/i2c/busses/i2c-stm32f7.c
|
|
+++ b/drivers/i2c/busses/i2c-stm32f7.c
|
|
@@ -51,6 +51,7 @@
|
|
|
|
/* STM32F7 I2C control 1 */
|
|
#define STM32F7_I2C_CR1_PECEN BIT(23)
|
|
+#define STM32F7_I2C_CR1_ALERTEN BIT(22)
|
|
#define STM32F7_I2C_CR1_SMBHEN BIT(20)
|
|
#define STM32F7_I2C_CR1_WUPEN BIT(18)
|
|
#define STM32F7_I2C_CR1_SBC BIT(16)
|
|
@@ -125,6 +126,7 @@
|
|
(((n) & STM32F7_I2C_ISR_ADDCODE_MASK) >> 17)
|
|
#define STM32F7_I2C_ISR_DIR BIT(16)
|
|
#define STM32F7_I2C_ISR_BUSY BIT(15)
|
|
+#define STM32F7_I2C_ISR_ALERT BIT(13)
|
|
#define STM32F7_I2C_ISR_PECERR BIT(11)
|
|
#define STM32F7_I2C_ISR_ARLO BIT(9)
|
|
#define STM32F7_I2C_ISR_BERR BIT(8)
|
|
@@ -138,6 +140,7 @@
|
|
#define STM32F7_I2C_ISR_TXE BIT(0)
|
|
|
|
/* STM32F7 I2C Interrupt Clear */
|
|
+#define STM32F7_I2C_ICR_ALERTCF BIT(13)
|
|
#define STM32F7_I2C_ICR_PECCF BIT(11)
|
|
#define STM32F7_I2C_ICR_ARLOCF BIT(9)
|
|
#define STM32F7_I2C_ICR_BERRCF BIT(8)
|
|
@@ -161,10 +164,8 @@ enum {
|
|
STM32F7_I2C_MAX_SLAVE
|
|
};
|
|
|
|
-#define STM32F7_I2C_DNF_DEFAULT 0
|
|
#define STM32F7_I2C_DNF_MAX 15
|
|
|
|
-#define STM32F7_I2C_ANALOG_FILTER_ENABLE 1
|
|
#define STM32F7_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */
|
|
#define STM32F7_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */
|
|
|
|
@@ -223,8 +224,6 @@ struct stm32f7_i2c_spec {
|
|
* @clock_src: I2C clock source frequency (Hz)
|
|
* @rise_time: Rise time (ns)
|
|
* @fall_time: Fall time (ns)
|
|
- * @dnf: Digital filter coefficient (0-16)
|
|
- * @analog_filter: Analog filter delay (On/Off)
|
|
* @fmp_clr_offset: Fast Mode Plus clear register offset from set register
|
|
*/
|
|
struct stm32f7_i2c_setup {
|
|
@@ -232,8 +231,6 @@ struct stm32f7_i2c_setup {
|
|
u32 clock_src;
|
|
u32 rise_time;
|
|
u32 fall_time;
|
|
- u8 dnf;
|
|
- bool analog_filter;
|
|
u32 fmp_clr_offset;
|
|
};
|
|
|
|
@@ -283,6 +280,17 @@ struct stm32f7_i2c_msg {
|
|
u8 smbus_buf[I2C_SMBUS_BLOCK_MAX + 3] __aligned(4);
|
|
};
|
|
|
|
+/**
|
|
+ * struct stm32f7_i2c_alert - SMBus alert specific data
|
|
+ * @setup: platform data for the smbus_alert i2c client
|
|
+ * @ara: I2C slave device used to respond to the SMBus Alert with Alert
|
|
+ * Response Address
|
|
+ */
|
|
+struct stm32f7_i2c_alert {
|
|
+ struct i2c_smbus_alert_setup setup;
|
|
+ struct i2c_client *ara;
|
|
+};
|
|
+
|
|
/**
|
|
* struct stm32f7_i2c_dev - private data of the controller
|
|
* @adap: I2C adapter for this controller
|
|
@@ -312,6 +320,10 @@ struct stm32f7_i2c_msg {
|
|
* @wakeup_src: boolean to know if the device is a wakeup source
|
|
* @smbus_mode: states that the controller is configured in SMBus mode
|
|
* @host_notify_client: SMBus host-notify client
|
|
+ * @alert: SMBus alert specific data
|
|
+ * @analog_filter: boolean to indicate enabling of the analog filter
|
|
+ * @dnf_dt: value of digital filter requested via dt
|
|
+ * @dnf: value of digital filter to apply
|
|
*/
|
|
struct stm32f7_i2c_dev {
|
|
struct i2c_adapter adap;
|
|
@@ -340,6 +352,10 @@ struct stm32f7_i2c_dev {
|
|
bool wakeup_src;
|
|
bool smbus_mode;
|
|
struct i2c_client *host_notify_client;
|
|
+ struct stm32f7_i2c_alert *alert;
|
|
+ bool analog_filter;
|
|
+ u32 dnf_dt;
|
|
+ u32 dnf;
|
|
};
|
|
|
|
/*
|
|
@@ -385,15 +401,11 @@ static struct stm32f7_i2c_spec stm32f7_i2c_specs[] = {
|
|
static const struct stm32f7_i2c_setup stm32f7_setup = {
|
|
.rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
|
|
.fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
|
|
- .dnf = STM32F7_I2C_DNF_DEFAULT,
|
|
- .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE,
|
|
};
|
|
|
|
static const struct stm32f7_i2c_setup stm32mp15_setup = {
|
|
.rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
|
|
.fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
|
|
- .dnf = STM32F7_I2C_DNF_DEFAULT,
|
|
- .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE,
|
|
.fmp_clr_offset = 0x40,
|
|
};
|
|
|
|
@@ -462,27 +474,28 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
|
|
return -EINVAL;
|
|
}
|
|
|
|
- if (setup->dnf > STM32F7_I2C_DNF_MAX) {
|
|
+ i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk);
|
|
+ if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) {
|
|
dev_err(i2c_dev->dev,
|
|
"DNF out of bound %d/%d\n",
|
|
- setup->dnf, STM32F7_I2C_DNF_MAX);
|
|
+ i2c_dev->dnf * i2cclk, STM32F7_I2C_DNF_MAX * i2cclk);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Analog and Digital Filters */
|
|
af_delay_min =
|
|
- (setup->analog_filter ?
|
|
+ (i2c_dev->analog_filter ?
|
|
STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0);
|
|
af_delay_max =
|
|
- (setup->analog_filter ?
|
|
+ (i2c_dev->analog_filter ?
|
|
STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0);
|
|
- dnf_delay = setup->dnf * i2cclk;
|
|
+ dnf_delay = i2c_dev->dnf * i2cclk;
|
|
|
|
sdadel_min = specs->hddat_min + setup->fall_time -
|
|
- af_delay_min - (setup->dnf + 3) * i2cclk;
|
|
+ af_delay_min - (i2c_dev->dnf + 3) * i2cclk;
|
|
|
|
sdadel_max = specs->vddat_max - setup->rise_time -
|
|
- af_delay_max - (setup->dnf + 4) * i2cclk;
|
|
+ af_delay_max - (i2c_dev->dnf + 4) * i2cclk;
|
|
|
|
scldel_min = setup->rise_time + specs->sudat_min;
|
|
|
|
@@ -648,6 +661,7 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
|
|
setup->speed_freq = t->bus_freq_hz;
|
|
i2c_dev->setup.rise_time = t->scl_rise_ns;
|
|
i2c_dev->setup.fall_time = t->scl_fall_ns;
|
|
+ i2c_dev->dnf_dt = t->digital_filter_width_ns;
|
|
setup->clock_src = clk_get_rate(i2c_dev->clk);
|
|
|
|
if (!setup->clock_src) {
|
|
@@ -655,6 +669,9 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
|
|
return -EINVAL;
|
|
}
|
|
|
|
+ if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter"))
|
|
+ i2c_dev->dnf_dt = 0;
|
|
+
|
|
do {
|
|
ret = stm32f7_i2c_compute_timing(i2c_dev, setup,
|
|
&i2c_dev->timing);
|
|
@@ -676,12 +693,15 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
|
|
return ret;
|
|
}
|
|
|
|
+ i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node,
|
|
+ "i2c-analog-filter");
|
|
+
|
|
dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n",
|
|
setup->speed_freq, setup->clock_src);
|
|
dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n",
|
|
setup->rise_time, setup->fall_time);
|
|
dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n",
|
|
- (setup->analog_filter ? "On" : "Off"), setup->dnf);
|
|
+ (i2c_dev->analog_filter ? "On" : "Off"), i2c_dev->dnf);
|
|
|
|
i2c_dev->bus_rate = setup->speed_freq;
|
|
|
|
@@ -720,8 +740,8 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
|
|
timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll);
|
|
writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR);
|
|
|
|
- /* Enable I2C */
|
|
- if (i2c_dev->setup.analog_filter)
|
|
+ /* Configure the Analog Filter */
|
|
+ if (i2c_dev->analog_filter)
|
|
stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
|
|
STM32F7_I2C_CR1_ANFOFF);
|
|
else
|
|
@@ -732,7 +752,7 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
|
|
stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
|
|
STM32F7_I2C_CR1_DNF_MASK);
|
|
stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
|
|
- STM32F7_I2C_CR1_DNF(i2c_dev->setup.dnf));
|
|
+ STM32F7_I2C_CR1_DNF(i2c_dev->dnf));
|
|
|
|
stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
|
|
STM32F7_I2C_CR1_PE);
|
|
@@ -1403,7 +1423,7 @@ static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev)
|
|
status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);
|
|
|
|
/* Slave transmitter mode */
|
|
- if (status & STM32F7_I2C_ISR_TXIS) {
|
|
+ if (i2c_dev->slave_running && (status & STM32F7_I2C_ISR_TXIS)) {
|
|
i2c_slave_event(i2c_dev->slave_running,
|
|
I2C_SLAVE_READ_PROCESSED,
|
|
&val);
|
|
@@ -1413,7 +1433,8 @@ static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev)
|
|
}
|
|
|
|
/* Transfer Complete Reload for Slave receiver mode */
|
|
- if (status & STM32F7_I2C_ISR_TCR || status & STM32F7_I2C_ISR_RXNE) {
|
|
+ if (i2c_dev->slave_running &&
|
|
+ (status & STM32F7_I2C_ISR_TCR || status & STM32F7_I2C_ISR_RXNE)) {
|
|
/*
|
|
* Read data byte then set NBYTES to receive next byte or NACK
|
|
* the current received byte
|
|
@@ -1439,7 +1460,7 @@ static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev)
|
|
}
|
|
|
|
/* STOP received */
|
|
- if (status & STM32F7_I2C_ISR_STOPF) {
|
|
+ if (i2c_dev->slave_running && (status & STM32F7_I2C_ISR_STOPF)) {
|
|
/* Disable interrupts */
|
|
stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_XFER_IRQ_MASK);
|
|
|
|
@@ -1472,6 +1493,7 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
|
|
{
|
|
struct stm32f7_i2c_dev *i2c_dev = data;
|
|
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
|
|
+ struct stm32_i2c_dma *dma = i2c_dev->dma;
|
|
void __iomem *base = i2c_dev->base;
|
|
u32 status, mask;
|
|
int ret = IRQ_HANDLED;
|
|
@@ -1497,6 +1519,10 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
|
|
dev_dbg(i2c_dev->dev, "<%s>: Receive NACK (addr %x)\n",
|
|
__func__, f7_msg->addr);
|
|
writel_relaxed(STM32F7_I2C_ICR_NACKCF, base + STM32F7_I2C_ICR);
|
|
+ if (i2c_dev->use_dma) {
|
|
+ stm32f7_i2c_disable_dma_req(i2c_dev);
|
|
+ dmaengine_terminate_async(dma->chan_using);
|
|
+ }
|
|
f7_msg->result = -ENXIO;
|
|
}
|
|
|
|
@@ -1512,7 +1538,7 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
|
|
/* Clear STOP flag */
|
|
writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR);
|
|
|
|
- if (i2c_dev->use_dma) {
|
|
+ if (i2c_dev->use_dma && !f7_msg->result) {
|
|
ret = IRQ_WAKE_THREAD;
|
|
} else {
|
|
i2c_dev->master_mode = false;
|
|
@@ -1525,7 +1551,7 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
|
|
if (f7_msg->stop) {
|
|
mask = STM32F7_I2C_CR2_STOP;
|
|
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask);
|
|
- } else if (i2c_dev->use_dma) {
|
|
+ } else if (i2c_dev->use_dma && !f7_msg->result) {
|
|
ret = IRQ_WAKE_THREAD;
|
|
} else if (f7_msg->smbus) {
|
|
stm32f7_i2c_smbus_rep_start(i2c_dev);
|
|
@@ -1562,7 +1588,7 @@ static irqreturn_t stm32f7_i2c_isr_event_thread(int irq, void *data)
|
|
if (!ret) {
|
|
dev_dbg(i2c_dev->dev, "<%s>: Timed out\n", __func__);
|
|
stm32f7_i2c_disable_dma_req(i2c_dev);
|
|
- dmaengine_terminate_all(dma->chan_using);
|
|
+ dmaengine_terminate_async(dma->chan_using);
|
|
f7_msg->result = -ETIMEDOUT;
|
|
}
|
|
|
|
@@ -1597,7 +1623,8 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
|
|
|
|
/* Bus error */
|
|
if (status & STM32F7_I2C_ISR_BERR) {
|
|
- dev_err(dev, "<%s>: Bus error\n", __func__);
|
|
+ dev_err(dev, "<%s>: Bus error accessing addr 0x%x\n",
|
|
+ __func__, f7_msg->addr);
|
|
writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
|
|
stm32f7_i2c_release_bus(&i2c_dev->adap);
|
|
f7_msg->result = -EIO;
|
|
@@ -1605,17 +1632,26 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
|
|
|
|
/* Arbitration loss */
|
|
if (status & STM32F7_I2C_ISR_ARLO) {
|
|
- dev_dbg(dev, "<%s>: Arbitration loss\n", __func__);
|
|
+ dev_dbg(dev, "<%s>: Arbitration loss accessing addr 0x%x\n",
|
|
+ __func__, f7_msg->addr);
|
|
writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR);
|
|
f7_msg->result = -EAGAIN;
|
|
}
|
|
|
|
if (status & STM32F7_I2C_ISR_PECERR) {
|
|
- dev_err(dev, "<%s>: PEC error in reception\n", __func__);
|
|
+ dev_err(dev, "<%s>: PEC error in reception accessing addr 0x%x\n",
|
|
+ __func__, f7_msg->addr);
|
|
writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR);
|
|
f7_msg->result = -EINVAL;
|
|
}
|
|
|
|
+ if (status & STM32F7_I2C_ISR_ALERT) {
|
|
+ dev_dbg(dev, "<%s>: SMBus alert received\n", __func__);
|
|
+ writel_relaxed(STM32F7_I2C_ICR_ALERTCF, base + STM32F7_I2C_ICR);
|
|
+ i2c_handle_smbus_alert(i2c_dev->alert->ara);
|
|
+ return IRQ_HANDLED;
|
|
+ }
|
|
+
|
|
if (!i2c_dev->slave_running) {
|
|
u32 mask;
|
|
/* Disable interrupts */
|
|
@@ -1629,7 +1665,7 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
|
|
/* Disable dma */
|
|
if (i2c_dev->use_dma) {
|
|
stm32f7_i2c_disable_dma_req(i2c_dev);
|
|
- dmaengine_terminate_all(dma->chan_using);
|
|
+ dmaengine_terminate_async(dma->chan_using);
|
|
}
|
|
|
|
i2c_dev->master_mode = false;
|
|
@@ -1665,12 +1701,26 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
|
|
time_left = wait_for_completion_timeout(&i2c_dev->complete,
|
|
i2c_dev->adap.timeout);
|
|
ret = f7_msg->result;
|
|
+ if (ret) {
|
|
+ if (i2c_dev->use_dma)
|
|
+ dmaengine_synchronize(dma->chan_using);
|
|
+
|
|
+ /*
|
|
+ * It is possible that some unsent data have already been
|
|
+ * written into TXDR. To avoid sending old data in a
|
|
+ * further transfer, flush TXDR in case of any error
|
|
+ */
|
|
+ writel_relaxed(STM32F7_I2C_ISR_TXE,
|
|
+ i2c_dev->base + STM32F7_I2C_ISR);
|
|
+ goto pm_free;
|
|
+ }
|
|
|
|
if (!time_left) {
|
|
dev_dbg(i2c_dev->dev, "Access to slave 0x%x timed out\n",
|
|
i2c_dev->msg->addr);
|
|
if (i2c_dev->use_dma)
|
|
- dmaengine_terminate_all(dma->chan_using);
|
|
+ dmaengine_terminate_sync(dma->chan_using);
|
|
+ stm32f7_i2c_wait_free_bus(i2c_dev);
|
|
ret = -ETIMEDOUT;
|
|
}
|
|
|
|
@@ -1713,13 +1763,25 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
|
timeout = wait_for_completion_timeout(&i2c_dev->complete,
|
|
i2c_dev->adap.timeout);
|
|
ret = f7_msg->result;
|
|
- if (ret)
|
|
+ if (ret) {
|
|
+ if (i2c_dev->use_dma)
|
|
+ dmaengine_synchronize(dma->chan_using);
|
|
+
|
|
+ /*
|
|
+ * It is possible that some unsent data have already been
|
|
+ * written into TXDR. To avoid sending old data in a
|
|
+ * further transfer, flush TXDR in case of any error
|
|
+ */
|
|
+ writel_relaxed(STM32F7_I2C_ISR_TXE,
|
|
+ i2c_dev->base + STM32F7_I2C_ISR);
|
|
goto pm_free;
|
|
+ }
|
|
|
|
if (!timeout) {
|
|
dev_dbg(dev, "Access to slave 0x%x timed out\n", f7_msg->addr);
|
|
if (i2c_dev->use_dma)
|
|
- dmaengine_terminate_all(dma->chan_using);
|
|
+ dmaengine_terminate_sync(dma->chan_using);
|
|
+ stm32f7_i2c_wait_free_bus(i2c_dev);
|
|
ret = -ETIMEDOUT;
|
|
goto pm_free;
|
|
}
|
|
@@ -1982,6 +2044,42 @@ static void stm32f7_i2c_disable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
|
|
}
|
|
}
|
|
|
|
+static int stm32f7_i2c_enable_smbus_alert(struct stm32f7_i2c_dev *i2c_dev)
|
|
+{
|
|
+ struct stm32f7_i2c_alert *alert;
|
|
+ struct i2c_adapter *adap = &i2c_dev->adap;
|
|
+ struct device *dev = i2c_dev->dev;
|
|
+ void __iomem *base = i2c_dev->base;
|
|
+
|
|
+ alert = devm_kzalloc(dev, sizeof(*alert), GFP_KERNEL);
|
|
+ if (!alert)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ alert->ara = i2c_new_smbus_alert_device(adap, &alert->setup);
|
|
+ if (IS_ERR(alert->ara))
|
|
+ return PTR_ERR(alert->ara);
|
|
+
|
|
+ i2c_dev->alert = alert;
|
|
+
|
|
+ /* Enable SMBus Alert */
|
|
+ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_ALERTEN);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void stm32f7_i2c_disable_smbus_alert(struct stm32f7_i2c_dev *i2c_dev)
|
|
+{
|
|
+ struct stm32f7_i2c_alert *alert = i2c_dev->alert;
|
|
+ void __iomem *base = i2c_dev->base;
|
|
+
|
|
+ if (alert) {
|
|
+ /* Disable SMBus Alert */
|
|
+ stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1,
|
|
+ STM32F7_I2C_CR1_ALERTEN);
|
|
+ i2c_unregister_device(alert->ara);
|
|
+ }
|
|
+}
|
|
+
|
|
static u32 stm32f7_i2c_func(struct i2c_adapter *adap)
|
|
{
|
|
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
|
@@ -2173,6 +2271,16 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
|
}
|
|
}
|
|
|
|
+ if (of_property_read_bool(pdev->dev.of_node, "st,smbus-alert")) {
|
|
+ ret = stm32f7_i2c_enable_smbus_alert(i2c_dev);
|
|
+ if (ret) {
|
|
+ dev_err(i2c_dev->dev,
|
|
+ "failed to enable SMBus alert protocol (%d)\n",
|
|
+ ret);
|
|
+ goto i2c_disable_smbus_host;
|
|
+ }
|
|
+ }
|
|
+
|
|
dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr);
|
|
|
|
pm_runtime_mark_last_busy(i2c_dev->dev);
|
|
@@ -2180,6 +2288,9 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
|
|
|
return 0;
|
|
|
|
+i2c_disable_smbus_host:
|
|
+ stm32f7_i2c_disable_smbus_host(i2c_dev);
|
|
+
|
|
i2c_adapter_remove:
|
|
i2c_del_adapter(adap);
|
|
|
|
@@ -2214,6 +2325,7 @@ static int stm32f7_i2c_remove(struct platform_device *pdev)
|
|
{
|
|
struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
|
|
|
+ stm32f7_i2c_disable_smbus_alert(i2c_dev);
|
|
stm32f7_i2c_disable_smbus_host(i2c_dev);
|
|
|
|
i2c_del_adapter(&i2c_dev->adap);
|
|
@@ -2245,64 +2357,25 @@ static int stm32f7_i2c_remove(struct platform_device *pdev)
|
|
return 0;
|
|
}
|
|
|
|
-static int __maybe_unused stm32f7_i2c_runtime_suspend(struct device *dev)
|
|
+static void __maybe_unused
|
|
+stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
|
|
{
|
|
- struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
|
-
|
|
- if (!stm32f7_i2c_is_slave_registered(i2c_dev))
|
|
- clk_disable_unprepare(i2c_dev->clk);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int __maybe_unused stm32f7_i2c_runtime_resume(struct device *dev)
|
|
-{
|
|
- struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
|
- int ret;
|
|
-
|
|
- if (!stm32f7_i2c_is_slave_registered(i2c_dev)) {
|
|
- ret = clk_prepare_enable(i2c_dev->clk);
|
|
- if (ret) {
|
|
- dev_err(dev, "failed to prepare_enable clock\n");
|
|
- return ret;
|
|
- }
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-#ifdef CONFIG_PM_SLEEP
|
|
-static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
|
|
-{
|
|
- int ret;
|
|
struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
|
|
|
|
- ret = pm_runtime_resume_and_get(i2c_dev->dev);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
-
|
|
backup_regs->cr1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1);
|
|
backup_regs->cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2);
|
|
backup_regs->oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1);
|
|
backup_regs->oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2);
|
|
backup_regs->tmgr = readl_relaxed(i2c_dev->base + STM32F7_I2C_TIMINGR);
|
|
stm32f7_i2c_write_fm_plus_bits(i2c_dev, false);
|
|
-
|
|
- pm_runtime_put_sync(i2c_dev->dev);
|
|
-
|
|
- return ret;
|
|
}
|
|
|
|
-static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
|
|
+static void __maybe_unused
|
|
+stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
|
|
{
|
|
u32 cr1;
|
|
- int ret;
|
|
struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
|
|
|
|
- ret = pm_runtime_resume_and_get(i2c_dev->dev);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
-
|
|
cr1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1);
|
|
if (cr1 & STM32F7_I2C_CR1_PE)
|
|
stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
|
|
@@ -2318,54 +2391,68 @@ static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
|
|
writel_relaxed(backup_regs->oar1, i2c_dev->base + STM32F7_I2C_OAR1);
|
|
writel_relaxed(backup_regs->oar2, i2c_dev->base + STM32F7_I2C_OAR2);
|
|
stm32f7_i2c_write_fm_plus_bits(i2c_dev, true);
|
|
+}
|
|
|
|
- pm_runtime_put_sync(i2c_dev->dev);
|
|
+static int __maybe_unused stm32f7_i2c_runtime_suspend(struct device *dev)
|
|
+{
|
|
+ struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
|
|
|
- return ret;
|
|
+ stm32f7_i2c_regs_backup(i2c_dev);
|
|
+
|
|
+ if (!stm32f7_i2c_is_slave_registered(i2c_dev))
|
|
+ clk_disable_unprepare(i2c_dev->clk);
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
-static int stm32f7_i2c_suspend(struct device *dev)
|
|
+static int __maybe_unused stm32f7_i2c_runtime_resume(struct device *dev)
|
|
{
|
|
struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
- i2c_mark_adapter_suspended(&i2c_dev->adap);
|
|
-
|
|
- if (!device_may_wakeup(dev) && !dev->power.wakeup_path) {
|
|
- ret = stm32f7_i2c_regs_backup(i2c_dev);
|
|
- if (ret < 0) {
|
|
- i2c_mark_adapter_resumed(&i2c_dev->adap);
|
|
+ if (!stm32f7_i2c_is_slave_registered(i2c_dev)) {
|
|
+ ret = clk_prepare_enable(i2c_dev->clk);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "failed to prepare_enable clock\n");
|
|
return ret;
|
|
}
|
|
+ }
|
|
|
|
+ stm32f7_i2c_regs_restore(i2c_dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __maybe_unused stm32f7_i2c_suspend(struct device *dev)
|
|
+{
|
|
+ struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
|
+
|
|
+ i2c_mark_adapter_suspended(&i2c_dev->adap);
|
|
+
|
|
+ if (!device_may_wakeup(dev) && !device_wakeup_path(dev))
|
|
pinctrl_pm_select_sleep_state(dev);
|
|
- pm_runtime_force_suspend(dev);
|
|
- }
|
|
+
|
|
+ pm_runtime_force_suspend(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
-static int stm32f7_i2c_resume(struct device *dev)
|
|
+static int __maybe_unused stm32f7_i2c_resume(struct device *dev)
|
|
{
|
|
struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
- if (!device_may_wakeup(dev) && !dev->power.wakeup_path) {
|
|
- ret = pm_runtime_force_resume(dev);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
- pinctrl_pm_select_default_state(dev);
|
|
+ ret = pm_runtime_force_resume(dev);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
|
|
- ret = stm32f7_i2c_regs_restore(i2c_dev);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
- }
|
|
+ if (!device_may_wakeup(dev) && !device_wakeup_path(dev))
|
|
+ pinctrl_pm_select_default_state(dev);
|
|
|
|
i2c_mark_adapter_resumed(&i2c_dev->adap);
|
|
|
|
return 0;
|
|
}
|
|
-#endif
|
|
|
|
static const struct dev_pm_ops stm32f7_i2c_pm_ops = {
|
|
SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend,
|
|
diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c
|
|
index 327cc2097..ceb150296 100644
|
|
--- a/drivers/iio/adc/sd_adc_modulator.c
|
|
+++ b/drivers/iio/adc/sd_adc_modulator.c
|
|
@@ -9,10 +9,8 @@
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/triggered_buffer.h>
|
|
#include <linux/module.h>
|
|
-#include <linux/mod_devicetable.h>
|
|
-#include <linux/platform_device.h>
|
|
-
|
|
-static const struct iio_info iio_sd_mod_iio_info;
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/regulator/consumer.h>
|
|
|
|
static const struct iio_chan_spec iio_sd_mod_ch = {
|
|
.type = IIO_VOLTAGE,
|
|
@@ -22,34 +20,99 @@ static const struct iio_chan_spec iio_sd_mod_ch = {
|
|
.realbits = 1,
|
|
.shift = 0,
|
|
},
|
|
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
|
+};
|
|
+
|
|
+static const struct iio_chan_spec iio_sd_mod_ch_ads = {
|
|
+ .type = IIO_VOLTAGE,
|
|
+ .indexed = 1,
|
|
+ .scan_type = {
|
|
+ .sign = 'u',
|
|
+ .realbits = 1,
|
|
+ .shift = 0,
|
|
+ },
|
|
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
|
+ .differential = 1,
|
|
+};
|
|
+
|
|
+struct iio_sd_mod_priv {
|
|
+ int vref_mv;
|
|
+};
|
|
+
|
|
+static const struct of_device_id sd_adc_of_match[] = {
|
|
+ { .compatible = "sd-modulator", .data = &iio_sd_mod_ch },
|
|
+ { .compatible = "ads1201", .data = &iio_sd_mod_ch_ads },
|
|
+ { }
|
|
+};
|
|
+
|
|
+static int iio_sd_mod_read_raw(struct iio_dev *indio_dev,
|
|
+ struct iio_chan_spec const *chan, int *val,
|
|
+ int *val2, long mask)
|
|
+{
|
|
+ struct iio_sd_mod_priv *priv = iio_priv(indio_dev);
|
|
+
|
|
+ switch (mask) {
|
|
+ case IIO_CHAN_INFO_SCALE:
|
|
+ *val = priv->vref_mv;
|
|
+ *val2 = chan->scan_type.realbits;
|
|
+ return IIO_VAL_INT;
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static const struct iio_info iio_sd_mod_iio_info = {
|
|
+ .read_raw = iio_sd_mod_read_raw,
|
|
};
|
|
|
|
static int iio_sd_mod_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
+ struct iio_sd_mod_priv *priv;
|
|
+ struct regulator *vref;
|
|
struct iio_dev *iio;
|
|
+ int ret;
|
|
|
|
- iio = devm_iio_device_alloc(dev, 0);
|
|
+ iio = devm_iio_device_alloc(dev, sizeof(*priv));
|
|
if (!iio)
|
|
return -ENOMEM;
|
|
|
|
+ iio->channels = (const struct iio_chan_spec *)
|
|
+ of_match_device(sd_adc_of_match, &pdev->dev)->data;
|
|
+
|
|
+ priv = iio_priv(iio);
|
|
+
|
|
+ iio->dev.parent = dev;
|
|
+ iio->dev.of_node = dev->of_node;
|
|
iio->name = dev_name(dev);
|
|
iio->info = &iio_sd_mod_iio_info;
|
|
iio->modes = INDIO_BUFFER_HARDWARE;
|
|
-
|
|
iio->num_channels = 1;
|
|
- iio->channels = &iio_sd_mod_ch;
|
|
|
|
- platform_set_drvdata(pdev, iio);
|
|
+ vref = devm_regulator_get_optional(dev, "vref");
|
|
+ if (IS_ERR(vref)) {
|
|
+ ret = PTR_ERR(vref);
|
|
+ if (ret != -ENODEV) {
|
|
+ if (ret != -EPROBE_DEFER)
|
|
+ dev_err(dev, "vref get failed, %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!IS_ERR(vref)) {
|
|
+ ret = regulator_get_voltage(vref);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "vref get failed, %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ priv->vref_mv = ret / 1000;
|
|
+ dev_dbg(dev, "vref+=%dmV\n", priv->vref_mv);
|
|
+ }
|
|
|
|
return devm_iio_device_register(&pdev->dev, iio);
|
|
}
|
|
|
|
-static const struct of_device_id sd_adc_of_match[] = {
|
|
- { .compatible = "sd-modulator" },
|
|
- { .compatible = "ads1201" },
|
|
- { }
|
|
-};
|
|
MODULE_DEVICE_TABLE(of, sd_adc_of_match);
|
|
|
|
static struct platform_driver iio_sd_mod_adc = {
|
|
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
|
|
index a83199b21..9d1ad6e38 100644
|
|
--- a/drivers/iio/adc/stm32-adc-core.c
|
|
+++ b/drivers/iio/adc/stm32-adc-core.c
|
|
@@ -200,7 +200,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
|
|
{
|
|
u32 ckmode, presc, val;
|
|
unsigned long rate;
|
|
- int i, div;
|
|
+ int i, div, duty;
|
|
|
|
/* stm32h7 bus clock is common for all ADC instances (mandatory) */
|
|
if (!priv->bclk) {
|
|
@@ -224,6 +224,11 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
|
|
return -EINVAL;
|
|
}
|
|
|
|
+ /* If duty is an error, kindly use at least /2 divider */
|
|
+ duty = clk_get_scaled_duty_cycle(priv->aclk, 100);
|
|
+ if (duty < 0)
|
|
+ dev_warn(&pdev->dev, "adc clock duty: %d\n", duty);
|
|
+
|
|
for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
|
|
ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
|
|
presc = stm32h7_adc_ckmodes_spec[i].presc;
|
|
@@ -232,6 +237,13 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
|
|
if (ckmode)
|
|
continue;
|
|
|
|
+ /*
|
|
+ * For proper operation, clock duty cycle range is 49%
|
|
+ * to 51%. Apply at least /2 prescaler otherwise.
|
|
+ */
|
|
+ if (div == 1 && (duty < 49 || duty > 51))
|
|
+ continue;
|
|
+
|
|
if ((rate / div) <= priv->max_clk_rate)
|
|
goto out;
|
|
}
|
|
@@ -244,6 +256,10 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
|
|
return -EINVAL;
|
|
}
|
|
|
|
+ duty = clk_get_scaled_duty_cycle(priv->bclk, 100);
|
|
+ if (duty < 0)
|
|
+ dev_warn(&pdev->dev, "bus clock duty: %d\n", duty);
|
|
+
|
|
for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
|
|
ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
|
|
presc = stm32h7_adc_ckmodes_spec[i].presc;
|
|
@@ -252,6 +268,9 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
|
|
if (!ckmode)
|
|
continue;
|
|
|
|
+ if (div == 1 && (duty < 49 || duty > 51))
|
|
+ continue;
|
|
+
|
|
if ((rate / div) <= priv->max_clk_rate)
|
|
goto out;
|
|
}
|
|
diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
|
|
index 2322809bf..7c924f463 100644
|
|
--- a/drivers/iio/adc/stm32-adc-core.h
|
|
+++ b/drivers/iio/adc/stm32-adc-core.h
|
|
@@ -102,6 +102,9 @@
|
|
#define STM32H7_ADC_CALFACT 0xC4
|
|
#define STM32H7_ADC_CALFACT2 0xC8
|
|
|
|
+/* STM32MP1 - ADC2 instance option register */
|
|
+#define STM32MP1_ADC2_OR 0xD0
|
|
+
|
|
/* STM32H7 - common registers for all ADC instances */
|
|
#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
|
|
#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
|
|
@@ -168,11 +171,16 @@ enum stm32h7_adc_dmngt {
|
|
#define STM32H7_EOC_MST BIT(2)
|
|
|
|
/* STM32H7_ADC_CCR - bit fields */
|
|
+#define STM32H7_VBATEN BIT(24)
|
|
+#define STM32H7_VREFEN BIT(22)
|
|
#define STM32H7_PRESC_SHIFT 18
|
|
#define STM32H7_PRESC_MASK GENMASK(21, 18)
|
|
#define STM32H7_CKMODE_SHIFT 16
|
|
#define STM32H7_CKMODE_MASK GENMASK(17, 16)
|
|
|
|
+/* STM32MP1_ADC2_OR - bit fields */
|
|
+#define STM32MP1_VDDCOREEN BIT(0)
|
|
+
|
|
/**
|
|
* struct stm32_adc_common - stm32 ADC driver common data (for all instances)
|
|
* @base: control registers base cpu addr
|
|
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
|
|
index 16c02c30d..6e9744944 100644
|
|
--- a/drivers/iio/adc/stm32-adc.c
|
|
+++ b/drivers/iio/adc/stm32-adc.c
|
|
@@ -21,6 +21,7 @@
|
|
#include <linux/io.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/module.h>
|
|
+#include <linux/nvmem-consumer.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/of.h>
|
|
@@ -35,12 +36,14 @@
|
|
#define STM32H7_BOOST_CLKRATE 20000000UL
|
|
|
|
#define STM32_ADC_CH_MAX 20 /* max number of channels */
|
|
-#define STM32_ADC_CH_SZ 10 /* max channel name size */
|
|
+#define STM32_ADC_CH_SZ 16 /* max channel name size */
|
|
#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
|
|
#define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */
|
|
#define STM32_ADC_TIMEOUT_US 100000
|
|
#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
|
|
#define STM32_ADC_HW_STOP_DELAY_MS 100
|
|
+#define STM32_ADC_CHAN_NONE -1
|
|
+#define STM32_ADC_VREFINT_VOLTAGE 3300
|
|
|
|
#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
|
|
|
|
@@ -77,6 +80,30 @@ enum stm32_adc_extsel {
|
|
STM32_EXT20,
|
|
};
|
|
|
|
+enum stm32_adc_int_ch {
|
|
+ STM32_ADC_INT_CH_NONE = -1,
|
|
+ STM32_ADC_INT_CH_VDDCORE,
|
|
+ STM32_ADC_INT_CH_VREFINT,
|
|
+ STM32_ADC_INT_CH_VBAT,
|
|
+ STM32_ADC_INT_CH_NB,
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct stm32_adc_ic - ADC internal channels
|
|
+ * @name: name of the internal channel
|
|
+ * @idx: internal channel enum index
|
|
+ */
|
|
+struct stm32_adc_ic {
|
|
+ const char *name;
|
|
+ u32 idx;
|
|
+};
|
|
+
|
|
+static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
|
|
+ { "vddcore", STM32_ADC_INT_CH_VDDCORE },
|
|
+ { "vrefint", STM32_ADC_INT_CH_VREFINT },
|
|
+ { "vbat", STM32_ADC_INT_CH_VBAT },
|
|
+};
|
|
+
|
|
/**
|
|
* struct stm32_adc_trig_info - ADC trigger info
|
|
* @name: name of the trigger, corresponding to its source
|
|
@@ -113,6 +140,16 @@ struct stm32_adc_regs {
|
|
int shift;
|
|
};
|
|
|
|
+/**
|
|
+ * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
|
|
+ * @vrefint_cal: vrefint calibration value from nvmem
|
|
+ * @vrefint_data: vrefint actual value
|
|
+ */
|
|
+struct stm32_adc_vrefint {
|
|
+ u32 vrefint_cal;
|
|
+ u32 vrefint_data;
|
|
+};
|
|
+
|
|
/**
|
|
* struct stm32_adc_regspec - stm32 registers definition
|
|
* @dr: data register offset
|
|
@@ -126,6 +163,9 @@ struct stm32_adc_regs {
|
|
* @res: resolution selection register & bitfield
|
|
* @smpr: smpr1 & smpr2 registers offset array
|
|
* @smp_bits: smpr1 & smpr2 index and bitfields
|
|
+ * @or_vdd: option register & vddcore bitfield
|
|
+ * @ccr_vbat: common register & vbat bitfield
|
|
+ * @ccr_vref: common register & vrefint bitfield
|
|
*/
|
|
struct stm32_adc_regspec {
|
|
const u32 dr;
|
|
@@ -139,6 +179,9 @@ struct stm32_adc_regspec {
|
|
const struct stm32_adc_regs res;
|
|
const u32 smpr[2];
|
|
const struct stm32_adc_regs *smp_bits;
|
|
+ const struct stm32_adc_regs or_vdd;
|
|
+ const struct stm32_adc_regs ccr_vbat;
|
|
+ const struct stm32_adc_regs ccr_vref;
|
|
};
|
|
|
|
struct stm32_adc;
|
|
@@ -156,6 +199,7 @@ struct stm32_adc;
|
|
* @unprepare: optional unprepare routine (disable, power-down)
|
|
* @irq_clear: routine to clear irqs
|
|
* @smp_cycles: programmable sampling time (ADC clock cycles)
|
|
+ * @ts_vrefint_ns: vrefint minimum sampling time in ns
|
|
*/
|
|
struct stm32_adc_cfg {
|
|
const struct stm32_adc_regspec *regs;
|
|
@@ -169,6 +213,7 @@ struct stm32_adc_cfg {
|
|
void (*unprepare)(struct iio_dev *);
|
|
void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
|
|
const unsigned int *smp_cycles;
|
|
+ const unsigned int ts_vrefint_ns;
|
|
};
|
|
|
|
/**
|
|
@@ -193,7 +238,10 @@ struct stm32_adc_cfg {
|
|
* @pcsel: bitmask to preselect channels on some devices
|
|
* @smpr_val: sampling time settings (e.g. smpr1 / smpr2)
|
|
* @cal: optional calibration data on some devices
|
|
+ * @vrefint: internal reference voltage data
|
|
* @chan_name: channel name array
|
|
+ * @num_diff: number of differential channels
|
|
+ * @int_ch: internal channel indexes array
|
|
*/
|
|
struct stm32_adc {
|
|
struct stm32_adc_common *common;
|
|
@@ -216,7 +264,10 @@ struct stm32_adc {
|
|
u32 pcsel;
|
|
u32 smpr_val[2];
|
|
struct stm32_adc_calib cal;
|
|
+ struct stm32_adc_vrefint vrefint;
|
|
char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
|
|
+ u32 num_diff;
|
|
+ int int_ch[STM32_ADC_INT_CH_NB];
|
|
};
|
|
|
|
struct stm32_adc_diff_channel {
|
|
@@ -449,6 +500,24 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = {
|
|
.smp_bits = stm32h7_smp_bits,
|
|
};
|
|
|
|
+static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
|
|
+ .dr = STM32H7_ADC_DR,
|
|
+ .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
|
|
+ .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
|
|
+ .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
|
|
+ .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
|
|
+ .sqr = stm32h7_sq,
|
|
+ .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
|
|
+ .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
|
|
+ STM32H7_EXTSEL_SHIFT },
|
|
+ .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
|
|
+ .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
|
|
+ .smp_bits = stm32h7_smp_bits,
|
|
+ .or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
|
|
+ .ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
|
|
+ .ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
|
|
+};
|
|
+
|
|
/**
|
|
* STM32 ADC registers access routines
|
|
* @adc: stm32 adc instance
|
|
@@ -581,6 +650,61 @@ static int stm32_adc_hw_start(struct device *dev)
|
|
return ret;
|
|
}
|
|
|
|
+static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev)
|
|
+{
|
|
+ struct stm32_adc *adc = iio_priv(indio_dev);
|
|
+ u32 i, val, bits = 0, offset = 0;
|
|
+
|
|
+ for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
|
|
+ if (adc->int_ch[i] == STM32_ADC_CHAN_NONE)
|
|
+ continue;
|
|
+
|
|
+ switch (i) {
|
|
+ case STM32_ADC_INT_CH_VDDCORE:
|
|
+ dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
|
|
+ stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
|
|
+ adc->cfg->regs->or_vdd.mask);
|
|
+ break;
|
|
+ case STM32_ADC_INT_CH_VREFINT:
|
|
+ dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
|
|
+ offset = adc->cfg->regs->ccr_vref.reg;
|
|
+ bits |= adc->cfg->regs->ccr_vref.mask;
|
|
+ break;
|
|
+ case STM32_ADC_INT_CH_VBAT:
|
|
+ dev_dbg(&indio_dev->dev, "Enable VBAT\n");
|
|
+ offset = adc->cfg->regs->ccr_vbat.reg;
|
|
+ bits |= adc->cfg->regs->ccr_vbat.mask;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (bits) {
|
|
+ val = readl_relaxed(adc->common->base + offset);
|
|
+ val |= bits;
|
|
+ writel_relaxed(val, adc->common->base + offset);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void stm32_adc_int_ch_disable(struct stm32_adc *adc)
|
|
+{
|
|
+ u32 val, bits, offset, i;
|
|
+
|
|
+ stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
|
|
+ adc->cfg->regs->or_vdd.mask);
|
|
+
|
|
+ for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
|
|
+ if (adc->int_ch[i] != STM32_ADC_CHAN_NONE) {
|
|
+ offset = adc->cfg->regs->ccr_vref.reg;
|
|
+ bits = adc->cfg->regs->ccr_vref.mask |
|
|
+ adc->cfg->regs->ccr_vbat.mask;
|
|
+ val = readl_relaxed(adc->common->base + offset);
|
|
+ val &= ~bits;
|
|
+ writel_relaxed(bits, adc->common->base + offset);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
* stm32f4_adc_start_conv() - Start conversions for regular channels.
|
|
* @indio_dev: IIO device instance
|
|
@@ -949,11 +1073,13 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
|
|
goto pwr_dwn;
|
|
calib = ret;
|
|
|
|
+ stm32_adc_int_ch_enable(indio_dev);
|
|
+
|
|
stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
|
|
|
|
ret = stm32h7_adc_enable(indio_dev);
|
|
if (ret)
|
|
- goto pwr_dwn;
|
|
+ goto ch_disable;
|
|
|
|
/* Either restore or read calibration result for future reference */
|
|
if (calib)
|
|
@@ -969,6 +1095,8 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
|
|
|
|
disable:
|
|
stm32h7_adc_disable(indio_dev);
|
|
+ch_disable:
|
|
+ stm32_adc_int_ch_disable(adc);
|
|
pwr_dwn:
|
|
stm32h7_adc_enter_pwr_down(adc);
|
|
|
|
@@ -979,7 +1107,9 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)
|
|
{
|
|
struct stm32_adc *adc = iio_priv(indio_dev);
|
|
|
|
+ stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0);
|
|
stm32h7_adc_disable(indio_dev);
|
|
+ stm32_adc_int_ch_disable(adc);
|
|
stm32h7_adc_enter_pwr_down(adc);
|
|
}
|
|
|
|
@@ -1225,15 +1355,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
|
|
ret = stm32_adc_single_conv(indio_dev, chan, val);
|
|
else
|
|
ret = -EINVAL;
|
|
+
|
|
+ /* If channel mask corresponds to vrefint, store data */
|
|
+ if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
|
|
+ adc->vrefint.vrefint_data = *val;
|
|
+
|
|
iio_device_release_direct_mode(indio_dev);
|
|
return ret;
|
|
|
|
case IIO_CHAN_INFO_SCALE:
|
|
if (chan->differential) {
|
|
- *val = adc->common->vref_mv * 2;
|
|
+ if (adc->vrefint.vrefint_data &&
|
|
+ adc->vrefint.vrefint_cal) {
|
|
+ *val = STM32_ADC_VREFINT_VOLTAGE * 2 *
|
|
+ adc->vrefint.vrefint_cal /
|
|
+ adc->vrefint.vrefint_data;
|
|
+ } else {
|
|
+ *val = adc->common->vref_mv * 2;
|
|
+ }
|
|
*val2 = chan->scan_type.realbits;
|
|
} else {
|
|
- *val = adc->common->vref_mv;
|
|
+ /* Use vrefint data if available */
|
|
+ if (adc->vrefint.vrefint_data &&
|
|
+ adc->vrefint.vrefint_cal) {
|
|
+ *val = STM32_ADC_VREFINT_VOLTAGE *
|
|
+ adc->vrefint.vrefint_cal /
|
|
+ adc->vrefint.vrefint_data;
|
|
+ } else {
|
|
+ *val = adc->common->vref_mv;
|
|
+ }
|
|
*val2 = chan->scan_type.realbits;
|
|
}
|
|
return IIO_VAL_FRACTIONAL_LOG2;
|
|
@@ -1353,7 +1503,7 @@ static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val)
|
|
* dma cyclic transfers are used, buffer is split into two periods.
|
|
* There should be :
|
|
* - always one buffer (period) dma is working on
|
|
- * - one buffer (period) driver can push with iio_trigger_poll().
|
|
+ * - one buffer (period) driver can push data.
|
|
*/
|
|
watermark = min(watermark, val * (unsigned)(sizeof(u16)));
|
|
adc->rx_buf_sz = min(rx_buf_sz, watermark * 2 * adc->num_conv);
|
|
@@ -1616,31 +1766,14 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
|
|
|
|
dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
|
|
|
|
- if (!adc->dma_chan) {
|
|
- /* reset buffer index */
|
|
- adc->bufi = 0;
|
|
- iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
|
|
- pf->timestamp);
|
|
- } else {
|
|
- int residue = stm32_adc_dma_residue(adc);
|
|
-
|
|
- while (residue >= indio_dev->scan_bytes) {
|
|
- u16 *buffer = (u16 *)&adc->rx_buf[adc->bufi];
|
|
-
|
|
- iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
|
- pf->timestamp);
|
|
- residue -= indio_dev->scan_bytes;
|
|
- adc->bufi += indio_dev->scan_bytes;
|
|
- if (adc->bufi >= adc->rx_buf_sz)
|
|
- adc->bufi = 0;
|
|
- }
|
|
- }
|
|
-
|
|
+ /* reset buffer index */
|
|
+ adc->bufi = 0;
|
|
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
|
|
+ pf->timestamp);
|
|
iio_trigger_notify_done(indio_dev->trig);
|
|
|
|
/* re-enable eoc irq */
|
|
- if (!adc->dma_chan)
|
|
- stm32_adc_conv_irq_enable(adc);
|
|
+ stm32_adc_conv_irq_enable(adc);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
@@ -1686,6 +1819,14 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
|
|
u32 period_ns, shift = smpr->shift, mask = smpr->mask;
|
|
unsigned int smp, r = smpr->reg;
|
|
|
|
+ /*
|
|
+ * For vrefint channel, ensure that the sampling time cannot
|
|
+ * be lower than the one specified in the datasheet
|
|
+ */
|
|
+ if (channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
|
|
+ smp_ns < adc->cfg->ts_vrefint_ns)
|
|
+ smp_ns = adc->cfg->ts_vrefint_ns;
|
|
+
|
|
/* Determine sampling time (ADC clock cycles) */
|
|
period_ns = NSEC_PER_SEC / adc->common->rate;
|
|
for (smp = 0; smp <= STM32_ADC_MAX_SMP; smp++)
|
|
@@ -1735,17 +1876,11 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
|
|
}
|
|
}
|
|
|
|
-static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
|
+static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm32_adc *adc)
|
|
{
|
|
struct device_node *node = indio_dev->dev.of_node;
|
|
- struct stm32_adc *adc = iio_priv(indio_dev);
|
|
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
|
|
- struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
|
|
- struct property *prop;
|
|
- const __be32 *cur;
|
|
- struct iio_chan_spec *channels;
|
|
- int scan_index = 0, num_channels = 0, num_diff = 0, ret, i;
|
|
- u32 val, smp = 0;
|
|
+ int num_channels = 0, ret;
|
|
|
|
ret = of_property_count_u32_elems(node, "st,adc-channels");
|
|
if (ret > adc_info->max_channels) {
|
|
@@ -1756,23 +1891,17 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
|
}
|
|
|
|
ret = of_property_count_elems_of_size(node, "st,adc-diff-channels",
|
|
- sizeof(*diff));
|
|
+ sizeof(struct stm32_adc_diff_channel));
|
|
if (ret > adc_info->max_channels) {
|
|
dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n");
|
|
return -EINVAL;
|
|
} else if (ret > 0) {
|
|
- int size = ret * sizeof(*diff) / sizeof(u32);
|
|
-
|
|
- num_diff = ret;
|
|
+ adc->num_diff = ret;
|
|
num_channels += ret;
|
|
- ret = of_property_read_u32_array(node, "st,adc-diff-channels",
|
|
- (u32 *)diff, size);
|
|
- if (ret)
|
|
- return ret;
|
|
}
|
|
|
|
if (!num_channels) {
|
|
- dev_err(&indio_dev->dev, "No channels configured\n");
|
|
+ dev_err(indio_dev->dev.parent, "No channel found\n");
|
|
return -ENODATA;
|
|
}
|
|
|
|
@@ -1783,10 +1912,45 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
|
return -EINVAL;
|
|
}
|
|
|
|
- channels = devm_kcalloc(&indio_dev->dev, num_channels,
|
|
- sizeof(struct iio_chan_spec), GFP_KERNEL);
|
|
- if (!channels)
|
|
- return -ENOMEM;
|
|
+ return num_channels;
|
|
+}
|
|
+
|
|
+static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
|
|
+ struct stm32_adc *adc,
|
|
+ struct iio_chan_spec *channels)
|
|
+{
|
|
+ struct device_node *node = indio_dev->dev.of_node;
|
|
+ const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
|
|
+ struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
|
|
+ u32 num_diff = adc->num_diff;
|
|
+ int size = num_diff * sizeof(*diff) / sizeof(u32);
|
|
+ int scan_index = 0, val, ret, i;
|
|
+ struct property *prop;
|
|
+ const __be32 *cur;
|
|
+ u32 smp = 0;
|
|
+
|
|
+ if (num_diff) {
|
|
+ ret = of_property_read_u32_array(node, "st,adc-diff-channels",
|
|
+ (u32 *)diff, size);
|
|
+ if (ret) {
|
|
+ dev_err(&indio_dev->dev, "Failed to get diff channels %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < num_diff; i++) {
|
|
+ if (diff[i].vinp >= adc_info->max_channels ||
|
|
+ diff[i].vinn >= adc_info->max_channels) {
|
|
+ dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
|
|
+ diff[i].vinp, diff[i].vinn);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
|
|
+ diff[i].vinp, diff[i].vinn,
|
|
+ scan_index, true);
|
|
+ scan_index++;
|
|
+ }
|
|
+ }
|
|
|
|
of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
|
|
if (val >= adc_info->max_channels) {
|
|
@@ -1797,8 +1961,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
|
/* Channel can't be configured both as single-ended & diff */
|
|
for (i = 0; i < num_diff; i++) {
|
|
if (val == diff[i].vinp) {
|
|
- dev_err(&indio_dev->dev,
|
|
- "channel %d miss-configured\n", val);
|
|
+ dev_err(&indio_dev->dev, "channel %d misconfigured\n", val);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
@@ -1807,19 +1970,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
|
scan_index++;
|
|
}
|
|
|
|
- for (i = 0; i < num_diff; i++) {
|
|
- if (diff[i].vinp >= adc_info->max_channels ||
|
|
- diff[i].vinn >= adc_info->max_channels) {
|
|
- dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
|
|
- diff[i].vinp, diff[i].vinn);
|
|
- return -EINVAL;
|
|
- }
|
|
- stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
|
|
- diff[i].vinp, diff[i].vinn, scan_index,
|
|
- true);
|
|
- scan_index++;
|
|
- }
|
|
-
|
|
for (i = 0; i < scan_index; i++) {
|
|
/*
|
|
* Using of_property_read_u32_index(), smp value will only be
|
|
@@ -1827,13 +1977,173 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
|
* get either no value, 1 shared value for all indexes, or one
|
|
* value per channel.
|
|
*/
|
|
- of_property_read_u32_index(node, "st,min-sample-time-nsecs",
|
|
- i, &smp);
|
|
+ of_property_read_u32_index(node, "st,min-sample-time-nsecs", i, &smp);
|
|
+
|
|
/* Prepare sampling time settings */
|
|
stm32_adc_smpr_init(adc, channels[i].channel, smp);
|
|
}
|
|
|
|
- indio_dev->num_channels = scan_index;
|
|
+ return scan_index;
|
|
+}
|
|
+
|
|
+static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
|
|
+ int chan)
|
|
+{
|
|
+ struct stm32_adc *adc = iio_priv(indio_dev);
|
|
+ u16 vrefint;
|
|
+ int i, ret;
|
|
+
|
|
+ for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
|
|
+ if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
|
|
+ adc->int_ch[i] = chan;
|
|
+ /* If channel is vrefint get calibration data. */
|
|
+ if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {
|
|
+ ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
|
|
+ if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
|
|
+ dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ if (ret == -ENOENT)
|
|
+ dev_dbg(&indio_dev->dev,
|
|
+ "vrefint calibration not found\n");
|
|
+ else
|
|
+ adc->vrefint.vrefint_cal = vrefint;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
|
|
+ struct stm32_adc *adc,
|
|
+ struct iio_chan_spec *channels)
|
|
+{
|
|
+ struct device_node *node = indio_dev->dev.of_node;
|
|
+ const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
|
|
+ struct device_node *child;
|
|
+ const char *name;
|
|
+ int val, scan_index = 0, ret, i;
|
|
+ bool differential;
|
|
+ u32 vin[2];
|
|
+
|
|
+ for (i = 0; i < STM32_ADC_INT_CH_NB; i++)
|
|
+ adc->int_ch[i] = STM32_ADC_CHAN_NONE;
|
|
+
|
|
+ for_each_available_child_of_node(node, child) {
|
|
+ ret = of_property_read_u32(child, "reg", &val);
|
|
+ if (ret) {
|
|
+ dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_string(child, "label", &name);
|
|
+ /* label is optional */
|
|
+ if (!ret) {
|
|
+ if (strlen(name) >= STM32_ADC_CH_SZ) {
|
|
+ dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
|
|
+ name, STM32_ADC_CH_SZ);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
|
|
+ ret = stm32_adc_get_int_ch(indio_dev, name, val);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ } else if (ret != -EINVAL) {
|
|
+ dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ if (val >= adc_info->max_channels) {
|
|
+ dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
|
|
+ ret = -EINVAL;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ differential = false;
|
|
+ ret = of_property_read_u32_array(child, "diff-channels", vin, 2);
|
|
+ /* diff-channels is optional */
|
|
+ if (!ret) {
|
|
+ differential = true;
|
|
+ if (vin[0] != val || vin[1] >= adc_info->max_channels) {
|
|
+ dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
|
|
+ vin[0], vin[1]);
|
|
+ goto err;
|
|
+ }
|
|
+ } else if (ret != -EINVAL) {
|
|
+ dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
|
|
+ vin[1], scan_index, differential);
|
|
+
|
|
+ ret = of_property_read_u32(child, "st,min-sample-time-nsecs", &val);
|
|
+ /* st,min-sample-time-nsecs is optional */
|
|
+ if (!ret) {
|
|
+ stm32_adc_smpr_init(adc, channels[scan_index].channel, val);
|
|
+ if (differential)
|
|
+ stm32_adc_smpr_init(adc, vin[1], val);
|
|
+ } else if (ret != -EINVAL) {
|
|
+ dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs property %d\n",
|
|
+ ret);
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ scan_index++;
|
|
+ }
|
|
+
|
|
+ return scan_index;
|
|
+
|
|
+err:
|
|
+ of_node_put(child);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
|
+{
|
|
+ struct device_node *node = indio_dev->dev.of_node;
|
|
+ struct stm32_adc *adc = iio_priv(indio_dev);
|
|
+ const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
|
|
+ struct iio_chan_spec *channels;
|
|
+ int num_channels = 0, ret;
|
|
+ bool legacy = false;
|
|
+
|
|
+ num_channels = of_get_available_child_count(node);
|
|
+ /*
|
|
+ * If no channels have been found, fallback to channels legacy properties.
|
|
+ * Legacy channel properties will be ignored, if some channels are
|
|
+ * already defined using the standard binding.
|
|
+ */
|
|
+ if (!num_channels) {
|
|
+ ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ legacy = true;
|
|
+ num_channels = ret;
|
|
+ }
|
|
+
|
|
+ if (num_channels > adc_info->max_channels) {
|
|
+ dev_err(&indio_dev->dev, "Channel number [%d] exceeds %d\n",
|
|
+ num_channels, adc_info->max_channels);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ channels = devm_kcalloc(&indio_dev->dev, num_channels,
|
|
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
|
|
+ if (!channels)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ if (legacy)
|
|
+ ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
|
|
+ else
|
|
+ ret = stm32_adc_generic_chan_init(indio_dev, adc, channels);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ indio_dev->num_channels = ret;
|
|
indio_dev->channels = channels;
|
|
|
|
return 0;
|
|
@@ -2105,7 +2415,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
|
|
};
|
|
|
|
static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
|
|
- .regs = &stm32h7_adc_regspec,
|
|
+ .regs = &stm32mp1_adc_regspec,
|
|
.adc_info = &stm32h7_adc_info,
|
|
.trigs = stm32h7_adc_trigs,
|
|
.has_vregready = true,
|
|
@@ -2115,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
|
|
.unprepare = stm32h7_adc_unprepare,
|
|
.smp_cycles = stm32h7_adc_smp_cycles,
|
|
.irq_clear = stm32h7_adc_irq_clear,
|
|
+ .ts_vrefint_ns = 4300,
|
|
};
|
|
|
|
static const struct of_device_id stm32_adc_of_match[] = {
|
|
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
|
|
index 9234f1416..10e5a48cd 100644
|
|
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
|
|
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
|
|
@@ -10,6 +10,7 @@
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/iio/adc/stm32-dfsdm-adc.h>
|
|
#include <linux/iio/buffer.h>
|
|
+#include <linux/iio/consumer.h>
|
|
#include <linux/iio/hw-consumer.h>
|
|
#include <linux/iio/sysfs.h>
|
|
#include <linux/iio/timer/stm32-lptim-trigger.h>
|
|
@@ -67,6 +68,13 @@ struct stm32_dfsdm_dev_data {
|
|
const struct regmap_config *regmap_cfg;
|
|
};
|
|
|
|
+struct stm32_dfsdm_sd_chan_info {
|
|
+ int scale_val;
|
|
+ int scale_val2;
|
|
+ int offset;
|
|
+ unsigned int differential;
|
|
+};
|
|
+
|
|
struct stm32_dfsdm_adc {
|
|
struct stm32_dfsdm *dfsdm;
|
|
const struct stm32_dfsdm_dev_data *dev_data;
|
|
@@ -79,6 +87,7 @@ struct stm32_dfsdm_adc {
|
|
struct iio_hw_consumer *hwc;
|
|
struct completion completion;
|
|
u32 *buffer;
|
|
+ struct stm32_dfsdm_sd_chan_info *sd_chan;
|
|
|
|
/* Audio specific */
|
|
unsigned int spi_freq; /* SPI bus clock frequency */
|
|
@@ -1224,7 +1233,13 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
|
|
int *val2, long mask)
|
|
{
|
|
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
|
- int ret;
|
|
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
|
|
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
|
|
+ u32 max = flo->max << (flo->lshift - chan->scan_type.shift);
|
|
+ int ret, idx = chan->scan_index;
|
|
+
|
|
+ if (flo->lshift < chan->scan_type.shift)
|
|
+ max = flo->max >> (chan->scan_type.shift - flo->lshift);
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
@@ -1260,6 +1275,39 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
|
|
*val = adc->sample_freq;
|
|
|
|
return IIO_VAL_INT;
|
|
+
|
|
+ case IIO_CHAN_INFO_SCALE:
|
|
+ /*
|
|
+ * Scale is expressed in mV.
|
|
+ * When fast mode is disabled, actual resolution may be lower
|
|
+ * than 2^n, where n=realbits-1.
|
|
+ * This leads to underestimating input voltage. To
|
|
+ * compensate this deviation, the voltage reference can be
|
|
+ * corrected with a factor = realbits resolution / actual max
|
|
+ */
|
|
+ *val = div_u64((u64)adc->sd_chan[idx].scale_val *
|
|
+ (u64)BIT(DFSDM_DATA_RES - 1), max);
|
|
+ *val2 = chan->scan_type.realbits;
|
|
+ if (adc->sd_chan[idx].differential)
|
|
+ *val *= 2;
|
|
+ return IIO_VAL_FRACTIONAL_LOG2;
|
|
+
|
|
+ case IIO_CHAN_INFO_OFFSET:
|
|
+ /*
|
|
+ * DFSDM output data are in the range [-2^n,2^n-1],
|
|
+ * with n=realbits-1.
|
|
+ * - Differential modulator:
|
|
+ * Offset correspond to SD modulator offset.
|
|
+ * - Single ended modulator:
|
|
+ * Input is in [0V,Vref] range, where 0V corresponds to -2^n.
|
|
+ * Add 2^n to offset. (i.e. middle of input range)
|
|
+ * offset = offset(sd) * vref / res(sd) * max / vref.
|
|
+ */
|
|
+ *val = div_u64((u64)max * adc->sd_chan[idx].offset,
|
|
+ BIT(adc->sd_chan[idx].scale_val2 - 1));
|
|
+ if (!adc->sd_chan[idx].differential)
|
|
+ *val += max;
|
|
+ return IIO_VAL_INT;
|
|
}
|
|
|
|
return -EINVAL;
|
|
@@ -1384,7 +1432,9 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
|
|
* IIO_CHAN_INFO_RAW: used to compute regular conversion
|
|
* IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
|
|
*/
|
|
- ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
|
+ ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
|
+ BIT(IIO_CHAN_INFO_SCALE) |
|
|
+ BIT(IIO_CHAN_INFO_OFFSET);
|
|
ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
|
|
BIT(IIO_CHAN_INFO_SAMP_FREQ);
|
|
|
|
@@ -1394,7 +1444,7 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
|
|
ch->scan_type.shift = 8;
|
|
}
|
|
ch->scan_type.sign = 's';
|
|
- ch->scan_type.realbits = 24;
|
|
+ ch->scan_type.realbits = DFSDM_DATA_RES;
|
|
ch->scan_type.storagebits = 32;
|
|
|
|
return stm32_dfsdm_chan_configure(adc->dfsdm,
|
|
@@ -1435,8 +1485,10 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
|
|
{
|
|
struct iio_chan_spec *ch;
|
|
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
|
+ struct iio_channel *channels, *chan;
|
|
+ struct stm32_dfsdm_sd_chan_info *sd_chan;
|
|
int num_ch;
|
|
- int ret, chan_idx;
|
|
+ int ret, chan_idx, val2;
|
|
|
|
adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
|
|
ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp);
|
|
@@ -1460,6 +1512,21 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
|
|
if (!ch)
|
|
return -ENOMEM;
|
|
|
|
+ /* Get SD modulator channels */
|
|
+ channels = iio_channel_get_all(&indio_dev->dev);
|
|
+ if (IS_ERR(channels)) {
|
|
+ dev_err(&indio_dev->dev, "Failed to get channel %ld\n",
|
|
+ PTR_ERR(channels));
|
|
+ return PTR_ERR(channels);
|
|
+ }
|
|
+ chan = &channels[0];
|
|
+
|
|
+ adc->sd_chan = devm_kzalloc(&indio_dev->dev,
|
|
+ sizeof(*adc->sd_chan) * num_ch, GFP_KERNEL);
|
|
+ if (!adc->sd_chan)
|
|
+ return -ENOMEM;
|
|
+ sd_chan = adc->sd_chan;
|
|
+
|
|
for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
|
|
ch[chan_idx].scan_index = chan_idx;
|
|
ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &ch[chan_idx]);
|
|
@@ -1467,6 +1534,38 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
|
|
dev_err(&indio_dev->dev, "Channels init failed\n");
|
|
return ret;
|
|
}
|
|
+
|
|
+ if (!chan->indio_dev)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = iio_read_channel_scale(chan, &sd_chan->scale_val,
|
|
+ &sd_chan->scale_val2);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&indio_dev->dev,
|
|
+ "Failed to get channel %d scale\n", chan_idx);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_OFFSET)) {
|
|
+ ret = iio_read_channel_offset(chan, &sd_chan->offset,
|
|
+ &val2);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&indio_dev->dev,
|
|
+ "Failed to get channel %d offset\n",
|
|
+ chan_idx);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sd_chan->differential = chan->channel->differential;
|
|
+
|
|
+ dev_dbg(&indio_dev->dev, "Channel %d %s scale ref=%d offset=%d",
|
|
+ chan_idx, chan->channel->differential ?
|
|
+ "differential" : "single-ended",
|
|
+ sd_chan->scale_val, sd_chan->offset);
|
|
+
|
|
+ chan++;
|
|
+ sd_chan++;
|
|
}
|
|
|
|
indio_dev->num_channels = num_ch;
|
|
@@ -1521,6 +1620,7 @@ static const struct of_device_id stm32_dfsdm_adc_match[] = {
|
|
},
|
|
{}
|
|
};
|
|
+MODULE_DEVICE_TABLE(of, stm32_dfsdm_adc_match);
|
|
|
|
static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
|
|
{
|
|
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
|
|
index 42a737770..627557d8e 100644
|
|
--- a/drivers/iio/adc/stm32-dfsdm-core.c
|
|
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
|
|
@@ -6,6 +6,7 @@
|
|
* Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.
|
|
*/
|
|
|
|
+#include <linux/bitfield.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/sysfs.h>
|
|
@@ -20,6 +21,7 @@
|
|
#include "stm32-dfsdm.h"
|
|
|
|
struct stm32_dfsdm_dev_data {
|
|
+ u32 ipid;
|
|
unsigned int num_filters;
|
|
unsigned int num_channels;
|
|
const struct regmap_config *regmap_cfg;
|
|
@@ -27,8 +29,6 @@ struct stm32_dfsdm_dev_data {
|
|
|
|
#define STM32H7_DFSDM_NUM_FILTERS 4
|
|
#define STM32H7_DFSDM_NUM_CHANNELS 8
|
|
-#define STM32MP1_DFSDM_NUM_FILTERS 6
|
|
-#define STM32MP1_DFSDM_NUM_CHANNELS 8
|
|
|
|
static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
|
|
{
|
|
@@ -75,8 +75,7 @@ static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = {
|
|
};
|
|
|
|
static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = {
|
|
- .num_filters = STM32MP1_DFSDM_NUM_FILTERS,
|
|
- .num_channels = STM32MP1_DFSDM_NUM_CHANNELS,
|
|
+ .ipid = STM32MP15_IPIDR_NUMBER,
|
|
.regmap_cfg = &stm32mp1_dfsdm_regmap_cfg,
|
|
};
|
|
|
|
@@ -298,6 +297,64 @@ static const struct of_device_id stm32_dfsdm_of_match[] = {
|
|
};
|
|
MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
|
|
|
|
+static int stm32_dfsdm_config_check(struct platform_device *pdev,
|
|
+ struct dfsdm_priv *priv,
|
|
+ const struct stm32_dfsdm_dev_data *dev_data)
|
|
+{
|
|
+ struct device_node *np = pdev->dev.of_node;
|
|
+ struct device_node *child;
|
|
+ struct stm32_dfsdm *dfsdm = &priv->dfsdm;
|
|
+ const char *compat;
|
|
+ int ret, count = 0;
|
|
+ u32 id, val;
|
|
+
|
|
+ if (!dev_data->ipid) {
|
|
+ dfsdm->num_fls = dev_data->num_filters;
|
|
+ dfsdm->num_chs = dev_data->num_channels;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = regmap_read(dfsdm->regmap, DFSDM_IPIDR, &val);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ id = FIELD_GET(DFSDM_IPIDR_MASK, val);
|
|
+ if (id != dev_data->ipid) {
|
|
+ dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ for_each_child_of_node(np, child) {
|
|
+ ret = of_property_read_string(child, "compatible", &compat);
|
|
+ if (ret)
|
|
+ continue;
|
|
+ count++;
|
|
+ }
|
|
+
|
|
+ ret = regmap_read(dfsdm->regmap, DFSDM_HWCFGR, &val);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ dfsdm->num_fls = FIELD_GET(DFSDM_HWCFGR_NBF_MASK, val);
|
|
+ dfsdm->num_chs = FIELD_GET(DFSDM_HWCFGR_NBT_MASK, val);
|
|
+
|
|
+ if (count > dfsdm->num_fls) {
|
|
+ dev_err(&pdev->dev, "Unexpected child number: %d", count);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = regmap_read(dfsdm->regmap, DFSDM_VERR, &val);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ dev_dbg(&pdev->dev, "DFSDM version: %lu.%lu. %d channels/%d filters\n",
|
|
+ FIELD_GET(DFSDM_VERR_MAJREV_MASK, val),
|
|
+ FIELD_GET(DFSDM_VERR_MINREV_MASK, val),
|
|
+ dfsdm->num_chs, dfsdm->num_fls);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int stm32_dfsdm_probe(struct platform_device *pdev)
|
|
{
|
|
struct dfsdm_priv *priv;
|
|
@@ -314,18 +371,6 @@ static int stm32_dfsdm_probe(struct platform_device *pdev)
|
|
dev_data = of_device_get_match_data(&pdev->dev);
|
|
|
|
dfsdm = &priv->dfsdm;
|
|
- dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
|
|
- sizeof(*dfsdm->fl_list), GFP_KERNEL);
|
|
- if (!dfsdm->fl_list)
|
|
- return -ENOMEM;
|
|
-
|
|
- dfsdm->num_fls = dev_data->num_filters;
|
|
- dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
|
|
- sizeof(*dfsdm->ch_list),
|
|
- GFP_KERNEL);
|
|
- if (!dfsdm->ch_list)
|
|
- return -ENOMEM;
|
|
- dfsdm->num_chs = dev_data->num_channels;
|
|
|
|
ret = stm32_dfsdm_parse_of(pdev, priv);
|
|
if (ret < 0)
|
|
@@ -341,6 +386,20 @@ static int stm32_dfsdm_probe(struct platform_device *pdev)
|
|
return ret;
|
|
}
|
|
|
|
+ ret = stm32_dfsdm_config_check(pdev, priv, dev_data);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ dfsdm->fl_list = devm_kcalloc(&pdev->dev, dfsdm->num_fls,
|
|
+ sizeof(*dfsdm->fl_list), GFP_KERNEL);
|
|
+ if (!dfsdm->fl_list)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ dfsdm->ch_list = devm_kcalloc(&pdev->dev, dfsdm->num_chs,
|
|
+ sizeof(*dfsdm->ch_list), GFP_KERNEL);
|
|
+ if (!dfsdm->ch_list)
|
|
+ return -ENOMEM;
|
|
+
|
|
platform_set_drvdata(pdev, dfsdm);
|
|
|
|
ret = stm32_dfsdm_clk_prepare_enable(dfsdm);
|
|
diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
|
|
index 4afc1f528..4f230e2a7 100644
|
|
--- a/drivers/iio/adc/stm32-dfsdm.h
|
|
+++ b/drivers/iio/adc/stm32-dfsdm.h
|
|
@@ -13,25 +13,28 @@
|
|
|
|
/*
|
|
* STM32 DFSDM - global register map
|
|
- * ________________________________________________________
|
|
- * | Offset | Registers block |
|
|
- * --------------------------------------------------------
|
|
- * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS |
|
|
- * --------------------------------------------------------
|
|
- * | 0x020 | CHANNEL 1 |
|
|
- * --------------------------------------------------------
|
|
- * | ... | ..... |
|
|
- * --------------------------------------------------------
|
|
- * | 0x0E0 | CHANNEL 7 |
|
|
- * --------------------------------------------------------
|
|
- * | 0x100 | FILTER 0 + COMMON FILTER FIELDs |
|
|
- * --------------------------------------------------------
|
|
- * | 0x200 | FILTER 1 |
|
|
- * --------------------------------------------------------
|
|
- * | 0x300 | FILTER 2 |
|
|
- * --------------------------------------------------------
|
|
- * | 0x400 | FILTER 3 |
|
|
- * --------------------------------------------------------
|
|
+ * __________________________________________________________
|
|
+ * | Offset | Registers block |
|
|
+ * ----------------------------------------------------------
|
|
+ * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS |
|
|
+ * ----------------------------------------------------------
|
|
+ * | 0x020 | CHANNEL 1 |
|
|
+ * ----------------------------------------------------------
|
|
+ * | ... | ..... |
|
|
+ * ----------------------------------------------------------
|
|
+ * | 0x20 x n | CHANNEL n |
|
|
+ * ----------------------------------------------------------
|
|
+ * | 0x100 | FILTER 0 + COMMON FILTER FIELDs |
|
|
+ * ----------------------------------------------------------
|
|
+ * | 0x200 | FILTER 1 |
|
|
+ * ----------------------------------------------------------
|
|
+ * | | ..... |
|
|
+ * ----------------------------------------------------------
|
|
+ * | 0x100 x m| FILTER m |
|
|
+ * ----------------------------------------------------------
|
|
+ * ----------------------------------------------------------
|
|
+ * | 0x7F0-7FC| Identification registers |
|
|
+ * ----------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
@@ -231,6 +234,34 @@
|
|
#define DFSDM_AWCFR_AWHTF_MASK GENMASK(15, 8)
|
|
#define DFSDM_AWCFR_AWHTF(v) FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
|
|
|
|
+/*
|
|
+ * Identification register definitions
|
|
+ */
|
|
+#define DFSDM_HWCFGR 0x7F0
|
|
+#define DFSDM_VERR 0x7F4
|
|
+#define DFSDM_IPIDR 0x7F8
|
|
+#define DFSDM_SIDR 0x7FC
|
|
+
|
|
+/* HWCFGR: Hardware configuration register */
|
|
+#define DFSDM_HWCFGR_NBT_SHIFT 0
|
|
+#define DFSDM_HWCFGR_NBT_MASK GENMASK(7, 0)
|
|
+#define DFSDM_HWCFGR_NBF_SHIFT 8
|
|
+#define DFSDM_HWCFGR_NBF_MASK GENMASK(15, 8)
|
|
+
|
|
+/* VERR: Version register */
|
|
+#define DFSDM_VERR_MINREV_SHIFT 0
|
|
+#define DFSDM_VERR_MINREV_MASK GENMASK(3, 0)
|
|
+#define DFSDM_VERR_MAJREV_SHIFT 4
|
|
+#define DFSDM_VERR_MAJREV_MASK GENMASK(7, 4)
|
|
+
|
|
+/* IPDR: Identification register */
|
|
+#define DFSDM_IPIDR_MASK GENMASK(31, 0)
|
|
+
|
|
+/* SIDR: Size identification register */
|
|
+#define DFSDM_SIDR_MASK GENMASK(31, 0)
|
|
+
|
|
+#define STM32MP15_IPIDR_NUMBER 0x00110031
|
|
+
|
|
/* DFSDM filter order */
|
|
enum stm32_dfsdm_sinc_order {
|
|
DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
|
|
diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c
|
|
index 8662d7b7b..d38896125 100644
|
|
--- a/drivers/irqchip/irq-stm32-exti.c
|
|
+++ b/drivers/irqchip/irq-stm32-exti.c
|
|
@@ -132,7 +132,6 @@ static const struct stm32_exti_drv_data stm32h7xx_drv_data = {
|
|
|
|
static const struct stm32_exti_bank stm32mp1_exti_b1 = {
|
|
.imr_ofst = 0x80,
|
|
- .emr_ofst = 0x84,
|
|
.rtsr_ofst = 0x00,
|
|
.ftsr_ofst = 0x04,
|
|
.swier_ofst = 0x08,
|
|
@@ -142,7 +141,6 @@ static const struct stm32_exti_bank stm32mp1_exti_b1 = {
|
|
|
|
static const struct stm32_exti_bank stm32mp1_exti_b2 = {
|
|
.imr_ofst = 0x90,
|
|
- .emr_ofst = 0x94,
|
|
.rtsr_ofst = 0x20,
|
|
.ftsr_ofst = 0x24,
|
|
.swier_ofst = 0x28,
|
|
@@ -152,7 +150,6 @@ static const struct stm32_exti_bank stm32mp1_exti_b2 = {
|
|
|
|
static const struct stm32_exti_bank stm32mp1_exti_b3 = {
|
|
.imr_ofst = 0xA0,
|
|
- .emr_ofst = 0xA4,
|
|
.rtsr_ofst = 0x40,
|
|
.ftsr_ofst = 0x44,
|
|
.swier_ofst = 0x48,
|
|
@@ -193,7 +190,16 @@ static const struct stm32_desc_irq stm32mp1_desc_irq[] = {
|
|
{ .exti = 23, .irq_parent = 72, .chip = &stm32_exti_h_chip_direct },
|
|
{ .exti = 24, .irq_parent = 95, .chip = &stm32_exti_h_chip_direct },
|
|
{ .exti = 25, .irq_parent = 107, .chip = &stm32_exti_h_chip_direct },
|
|
+ { .exti = 26, .irq_parent = 37, .chip = &stm32_exti_h_chip_direct },
|
|
+ { .exti = 27, .irq_parent = 38, .chip = &stm32_exti_h_chip_direct },
|
|
+ { .exti = 28, .irq_parent = 39, .chip = &stm32_exti_h_chip_direct },
|
|
+ { .exti = 29, .irq_parent = 71, .chip = &stm32_exti_h_chip_direct },
|
|
{ .exti = 30, .irq_parent = 52, .chip = &stm32_exti_h_chip_direct },
|
|
+ { .exti = 31, .irq_parent = 53, .chip = &stm32_exti_h_chip_direct },
|
|
+ { .exti = 32, .irq_parent = 82, .chip = &stm32_exti_h_chip_direct },
|
|
+ { .exti = 33, .irq_parent = 83, .chip = &stm32_exti_h_chip_direct },
|
|
+ { .exti = 43, .irq_parent = 75, .chip = &stm32_exti_h_chip_direct },
|
|
+ { .exti = 44, .irq_parent = 98, .chip = &stm32_exti_h_chip_direct },
|
|
{ .exti = 47, .irq_parent = 93, .chip = &stm32_exti_h_chip_direct },
|
|
{ .exti = 48, .irq_parent = 138, .chip = &stm32_exti_h_chip_direct },
|
|
{ .exti = 50, .irq_parent = 139, .chip = &stm32_exti_h_chip_direct },
|
|
@@ -534,6 +540,9 @@ static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type)
|
|
unlock:
|
|
raw_spin_unlock(&chip_data->rlock);
|
|
|
|
+ if (d->parent_data->chip)
|
|
+ irq_chip_set_type_parent(d, type);
|
|
+
|
|
return err;
|
|
}
|
|
|
|
@@ -551,6 +560,9 @@ static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on)
|
|
|
|
raw_spin_unlock(&chip_data->rlock);
|
|
|
|
+ if (d->parent_data->chip)
|
|
+ irq_chip_set_wake_parent(d, on);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -560,7 +572,13 @@ static int stm32_exti_h_set_affinity(struct irq_data *d,
|
|
if (d->parent_data->chip)
|
|
return irq_chip_set_affinity_parent(d, dest, force);
|
|
|
|
- return -EINVAL;
|
|
+ return IRQ_SET_MASK_OK_DONE;
|
|
+}
|
|
+
|
|
+static void stm32_exti_h_ack(struct irq_data *d)
|
|
+{
|
|
+ if (d->parent_data->chip)
|
|
+ irq_chip_ack_parent(d);
|
|
}
|
|
|
|
static int __maybe_unused stm32_exti_h_suspend(void)
|
|
@@ -624,6 +642,7 @@ static int stm32_exti_h_retrigger(struct irq_data *d)
|
|
static struct irq_chip stm32_exti_h_chip = {
|
|
.name = "stm32-exti-h",
|
|
.irq_eoi = stm32_exti_h_eoi,
|
|
+ .irq_ack = stm32_exti_h_ack,
|
|
.irq_mask = stm32_exti_h_mask,
|
|
.irq_unmask = stm32_exti_h_unmask,
|
|
.irq_retrigger = stm32_exti_h_retrigger,
|
|
@@ -637,8 +656,8 @@ static struct irq_chip stm32_exti_h_chip_direct = {
|
|
.name = "stm32-exti-h-direct",
|
|
.irq_eoi = irq_chip_eoi_parent,
|
|
.irq_ack = irq_chip_ack_parent,
|
|
- .irq_mask = irq_chip_mask_parent,
|
|
- .irq_unmask = irq_chip_unmask_parent,
|
|
+ .irq_mask = stm32_exti_h_mask,
|
|
+ .irq_unmask = stm32_exti_h_unmask,
|
|
.irq_retrigger = irq_chip_retrigger_hierarchy,
|
|
.irq_set_type = irq_chip_set_type_parent,
|
|
.irq_set_wake = stm32_exti_h_set_wake,
|
|
@@ -669,14 +688,28 @@ static int stm32_exti_h_domain_alloc(struct irq_domain *dm,
|
|
|
|
irq_domain_set_hwirq_and_chip(dm, virq, hwirq, desc->chip,
|
|
chip_data);
|
|
- if (desc->irq_parent) {
|
|
+ /*
|
|
+ * EXTI 55 to 60 are mapped to PWR interrupt controller.
|
|
+ * The hwirq translation is done diferently than for GIC.
|
|
+ */
|
|
+ if (hwirq >= 55 && hwirq <= 60) {
|
|
p_fwspec.fwnode = dm->parent->fwnode;
|
|
- p_fwspec.param_count = 3;
|
|
- p_fwspec.param[0] = GIC_SPI;
|
|
- p_fwspec.param[1] = desc->irq_parent;
|
|
- p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH;
|
|
+ p_fwspec.param_count = 2;
|
|
+ p_fwspec.param[0] = hwirq - 55;
|
|
+ p_fwspec.param[1] = fwspec->param[1];
|
|
|
|
return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec);
|
|
+ } else {
|
|
+ if (desc->irq_parent) {
|
|
+ p_fwspec.fwnode = dm->parent->fwnode;
|
|
+ p_fwspec.param_count = 3;
|
|
+ p_fwspec.param[0] = GIC_SPI;
|
|
+ p_fwspec.param[1] = desc->irq_parent;
|
|
+ p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH;
|
|
+
|
|
+ return irq_domain_alloc_irqs_parent(dm, virq, 1,
|
|
+ &p_fwspec);
|
|
+ }
|
|
}
|
|
|
|
return 0;
|
|
@@ -738,7 +771,8 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
|
|
* clear registers to avoid residue
|
|
*/
|
|
writel_relaxed(0, base + stm32_bank->imr_ofst);
|
|
- writel_relaxed(0, base + stm32_bank->emr_ofst);
|
|
+ if (stm32_bank->emr_ofst)
|
|
+ writel_relaxed(0, base + stm32_bank->emr_ofst);
|
|
|
|
pr_info("%pOF: bank%d\n", node, bank_idx);
|
|
|
|
@@ -841,11 +875,12 @@ static int stm32_exti_probe(struct platform_device *pdev)
|
|
{
|
|
int ret, i;
|
|
struct device *dev = &pdev->dev;
|
|
- struct device_node *np = dev->of_node;
|
|
+ struct device_node *child, *np = dev->of_node;
|
|
struct irq_domain *parent_domain, *domain;
|
|
struct stm32_exti_host_data *host_data;
|
|
const struct stm32_exti_drv_data *drv_data;
|
|
struct resource *res;
|
|
+ u32 nirqs;
|
|
|
|
host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL);
|
|
if (!host_data)
|
|
@@ -913,6 +948,34 @@ static int stm32_exti_probe(struct platform_device *pdev)
|
|
if (ret)
|
|
return ret;
|
|
|
|
+ for_each_child_of_node(np, child) {
|
|
+ parent_domain = irq_find_host(of_irq_find_parent(child));
|
|
+ if (!parent_domain) {
|
|
+ dev_err(dev, "child interrupt-parent not found\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_u32(child, "st,irq-number", &nirqs);
|
|
+ if (ret || !nirqs) {
|
|
+ dev_err(dev, "Missing or bad irq-number property\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ domain = irq_domain_add_hierarchy(parent_domain, 0, nirqs,
|
|
+ child,
|
|
+ &stm32_exti_h_domain_ops,
|
|
+ host_data);
|
|
+ if (!domain) {
|
|
+ dev_err(dev, "Could not register exti domain\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq,
|
|
+ domain);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
stm32_exti_h_syscore_init(host_data);
|
|
|
|
return 0;
|
|
--
|
|
2.17.1
|
|
|