[PATCH v3] iio: adc: mcp320x: refactor driver to use bitfield API
Gustavo Pagnotta Faria
gustavo.pagnotta em ime.usp.br
Ter Abr 21 20:52:25 -03 2026
Update the mcp320x driver to use the standard Linux
bitfield API (<linux/bitfield.h>) instead of manual
bitwise shifts and masks.
This replaces the hardcoded shift operations in the TX
data preparation (mcp320x_channel_to_tx_data) with FIELD_PREP()
and replaces the manual masking in the RX data extraction
(mcp320x_adc_conversion) with FIELD_GET(). Explicit masks using
GENMASK() and BIT() were also introduced for both transmit
configurations and receive extractions.
Signed-off-by: Gustavo Pagnotta Faria <gustavo.pagnotta em ime.usp.br>
Co-developed-by: Eduardo Augusto <eduardoaugustoabc em ime.usp.br>
Signed-off-by: Eduardo Augusto <eduardoaugustoabc em ime.usp.br>
Co-developed-by: Christian Barry <christian.barry em ime.usp.br>
Signed-off-by: Christian Barry <christian.barry em ime.usp.br>
---
v2 -> v3:
- Removed wildcard 'X' from all macros (including MCP355X -> MCP3550),
using lowest part number instead.
- Separated Tx channel masks for 4-channel (MCP3004) and
8-channel (MCP3008).
- Added comment explaining MCP3550 Data and Status bits usage.
- Replaced be16_to_cpup() and be32_to_cpup() with get_unaligned_be16()
and get_unaligned_be32(), moving them inside the relevant switch cases.
- Fixed accidental removal of '~' operator in MCP3550 overrange clearing.
v1 -> v2:
- Sent previously as an unversioned reply to the v1 thread.
- Fixed typo in author's email address.
---
drivers/iio/adc/mcp320x.c | 70 +++++++++++++++++++++++++++++----------
1 file changed, 52 insertions(+), 18 deletions(-)
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index 57cff3772ebe..e7146c6af4f9 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -44,6 +44,38 @@
#include <linux/mod_devicetable.h>
#include <linux/iio/iio.h>
#include <linux/regulator/consumer.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/unaligned.h>
+
+/* MCP3002 Tx Data configuration */
+#define MCP3002_START_BIT BIT(4)
+#define MCP3002_SGL_DIFF BIT(3)
+#define MCP3002_CH_MASK BIT(2)
+
+/* MCP3004 Tx Data configuration */
+#define MCP3004_START_BIT BIT(6)
+#define MCP3004_SGL_DIFF BIT(5)
+#define MCP3004_CH_MASK GENMASK(3, 2)
+
+/* MCP3008 Tx Data configuration */
+#define MCP3008_START_BIT BIT(6)
+#define MCP3008_SGL_DIFF BIT(5)
+#define MCP3008_CH_MASK GENMASK(4, 2)
+
+/* MCP Rx Data extraction masks */
+#define MCP3001_DATA_MASK GENMASK(12, 3)
+#define MCP3002_DATA_MASK GENMASK(15, 6)
+#define MCP3201_DATA_MASK GENMASK(12, 1)
+#define MCP3202_DATA_MASK GENMASK(15, 4)
+#define MCP3301_DATA_MASK GENMASK(12, 0)
+
+/* * MCP3550 Data and Status bits
+ * See mcp320x_adc_conversion() for details on when these are used.
+ */
+#define MCP3550_DATA_MASK GENMASK(31, 8)
+#define MCP3550_SGN BIT(23)
+#define MCP3550_OVR BIT(22)
enum {
mcp3001,
@@ -99,19 +131,22 @@ struct mcp320x {
static int mcp320x_channel_to_tx_data(int device_index,
const unsigned int channel, bool differential)
{
- int start_bit = 1;
-
switch (device_index) {
case mcp3002:
case mcp3202:
- return ((start_bit << 4) | (!differential << 3) |
- (channel << 2));
+ return FIELD_PREP(MCP3002_START_BIT, 1) |
+ FIELD_PREP(MCP3002_SGL_DIFF, !differential) |
+ FIELD_PREP(MCP3002_CH_MASK, channel);
case mcp3004:
case mcp3204:
+ return FIELD_PREP(MCP3004_START_BIT, 1) |
+ FIELD_PREP(MCP3004_SGL_DIFF, !differential) |
+ FIELD_PREP(MCP3004_CH_MASK, channel);
case mcp3008:
case mcp3208:
- return ((start_bit << 6) | (!differential << 5) |
- (channel << 2));
+ return FIELD_PREP(MCP3008_START_BIT, 1) |
+ FIELD_PREP(MCP3008_SGL_DIFF, !differential) |
+ FIELD_PREP(MCP3008_CH_MASK, channel);
default:
return -EINVAL;
}
@@ -142,45 +177,44 @@ static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
switch (device_index) {
case mcp3001:
- *val = (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
+ *val = FIELD_GET(MCP3001_DATA_MASK, get_unaligned_be16(adc->rx_buf));
return 0;
case mcp3002:
case mcp3004:
case mcp3008:
- *val = (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
+ *val = FIELD_GET(MCP3002_DATA_MASK, get_unaligned_be16(adc->rx_buf));
return 0;
case mcp3201:
- *val = (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
+ *val = FIELD_GET(MCP3201_DATA_MASK, get_unaligned_be16(adc->rx_buf));
return 0;
case mcp3202:
case mcp3204:
case mcp3208:
- *val = (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
+ *val = FIELD_GET(MCP3202_DATA_MASK, get_unaligned_be16(adc->rx_buf));
return 0;
case mcp3301:
- *val = sign_extend32((adc->rx_buf[0] & 0x1f) << 8
- | adc->rx_buf[1], 12);
+ *val = sign_extend32(FIELD_GET(MCP3301_DATA_MASK, get_unaligned_be16(adc->rx_buf)), 12);
return 0;
case mcp3550_50:
case mcp3550_60:
case mcp3551:
case mcp3553: {
- u32 raw = be32_to_cpup((__be32 *)adc->rx_buf);
+ u32 raw = get_unaligned_be32(adc->rx_buf);
if (!(adc->spi->mode & SPI_CPOL))
raw <<= 1; /* strip Data Ready bit in SPI mode 0,0 */
+ raw = FIELD_GET(MCP3550_DATA_MASK, raw);
/*
* If the input is within -vref and vref, bit 21 is the sign.
* Up to 12% overrange or underrange are allowed, in which case
* bit 23 is the sign and bit 0 to 21 is the value.
*/
- raw >>= 8;
- if (raw & BIT(22) && raw & BIT(23))
+ if ((raw & MCP3550_OVR) && (raw & MCP3550_SGN))
return -EIO; /* cannot have overrange AND underrange */
- else if (raw & BIT(22))
- raw &= ~BIT(22); /* overrange */
- else if (raw & BIT(23) || raw & BIT(21))
+ else if (raw & MCP3550_OVR)
+ raw &= ~MCP3550_OVR; /* overrange */
+ else if ((raw & MCP3550_SGN) || (raw & BIT(21)))
raw |= GENMASK(31, 22); /* underrange or negative */
*val = (s32)raw;
--
2.43.0
Mais detalhes sobre a lista de discussão kernel