* Re: [PATCH v3 1/2] mfd: pm8xxx: add support to pm8821
From: Lee Jones @ 2016-11-21 13:48 UTC (permalink / raw)
To: Srinivas Kandagatla
Cc: bjorn.andersson-QSEj5FYQhm4dnm+yROfE0A, Rob Herring, Andy Gross,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-soc-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1479211312-1431-1-git-send-email-srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
On Tue, 15 Nov 2016, Srinivas Kandagatla wrote:
> This patch adds support to PM8821 PMIC and interrupt support.
> PM8821 is companion device that supplements primary PMIC PM8921 IC.
>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> ---
> Changes since v2:
> - removed few empty lines spotted by Bjorn
> - rephrased some debug prints suggested by Bjorn
>
> .../devicetree/bindings/mfd/qcom-pm8xxx.txt | 1 +
> drivers/mfd/qcom-pm8xxx.c | 231 ++++++++++++++++++++-
> 2 files changed, 222 insertions(+), 10 deletions(-)
Applied, thanks.
> diff --git a/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt b/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
> index 37a088f..9e5eba4 100644
> --- a/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
> +++ b/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
> @@ -10,6 +10,7 @@ voltages and other various functionality to Qualcomm SoCs.
> Value type: <string>
> Definition: must be one of:
> "qcom,pm8058"
> + "qcom,pm8821"
> "qcom,pm8921"
>
> - #address-cells:
> diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c
> index 7f9620e..f08758f 100644
> --- a/drivers/mfd/qcom-pm8xxx.c
> +++ b/drivers/mfd/qcom-pm8xxx.c
> @@ -39,6 +39,20 @@
> #define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
> #define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
>
> +#define PM8821_SSBI_REG_ADDR_IRQ_BASE 0x100
> +#define PM8821_SSBI_REG_ADDR_IRQ_MASTER0 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0x30)
> +#define PM8821_SSBI_REG_ADDR_IRQ_MASTER1 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0xb0)
> +#define PM8821_SSBI_REG(m, b, offset) \
> + ((m == 0) ? \
> + (PM8821_SSBI_REG_ADDR_IRQ_MASTER0 + b + offset) : \
> + (PM8821_SSBI_REG_ADDR_IRQ_MASTER1 + b + offset))
> +#define PM8821_SSBI_ADDR_IRQ_ROOT(m, b) PM8821_SSBI_REG(m, b, 0x0)
> +#define PM8821_SSBI_ADDR_IRQ_CLEAR(m, b) PM8821_SSBI_REG(m, b, 0x01)
> +#define PM8821_SSBI_ADDR_IRQ_MASK(m, b) PM8821_SSBI_REG(m, b, 0x08)
> +#define PM8821_SSBI_ADDR_IRQ_RT_STATUS(m, b) PM8821_SSBI_REG(m, b, 0x0f)
> +
> +#define PM8821_BLOCKS_PER_MASTER 7
> +
> #define PM_IRQF_LVL_SEL 0x01 /* level select */
> #define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
> #define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
> @@ -54,6 +68,7 @@
> #define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
>
> #define PM8XXX_NR_IRQS 256
> +#define PM8821_NR_IRQS 112
>
> struct pm_irq_chip {
> struct regmap *regmap;
> @@ -65,6 +80,12 @@ struct pm_irq_chip {
> u8 config[0];
> };
>
> +struct pm_irq_data {
> + int num_irqs;
> + const struct irq_domain_ops *irq_domain_ops;
> + void (*irq_handler)(struct irq_desc *desc);
> +};
> +
> static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
> unsigned int *ip)
> {
> @@ -182,6 +203,78 @@ static void pm8xxx_irq_handler(struct irq_desc *desc)
> chained_irq_exit(irq_chip, desc);
> }
>
> +static void pm8821_irq_block_handler(struct pm_irq_chip *chip,
> + int master, int block)
> +{
> + int pmirq, irq, i, ret;
> + unsigned int bits;
> +
> + ret = regmap_read(chip->regmap,
> + PM8821_SSBI_ADDR_IRQ_ROOT(master, block), &bits);
> + if (ret) {
> + pr_err("Reading block %d failed ret=%d", block, ret);
> + return;
> + }
> +
> + /* Convert block offset to global block number */
> + block += (master * PM8821_BLOCKS_PER_MASTER) - 1;
> +
> + /* Check IRQ bits */
> + for (i = 0; i < 8; i++) {
> + if (bits & BIT(i)) {
> + pmirq = block * 8 + i;
> + irq = irq_find_mapping(chip->irqdomain, pmirq);
> + generic_handle_irq(irq);
> + }
> + }
> +}
> +
> +static inline void pm8821_irq_master_handler(struct pm_irq_chip *chip,
> + int master, u8 master_val)
> +{
> + int block;
> +
> + for (block = 1; block < 8; block++)
> + if (master_val & BIT(block))
> + pm8821_irq_block_handler(chip, master, block);
> +}
> +
> +static void pm8821_irq_handler(struct irq_desc *desc)
> +{
> + struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
> + struct irq_chip *irq_chip = irq_desc_get_chip(desc);
> + unsigned int master;
> + int ret;
> +
> + chained_irq_enter(irq_chip, desc);
> + ret = regmap_read(chip->regmap,
> + PM8821_SSBI_REG_ADDR_IRQ_MASTER0, &master);
> + if (ret) {
> + pr_err("Failed to read master 0 ret=%d\n", ret);
> + goto done;
> + }
> +
> + /* bits 1 through 7 marks the first 7 blocks in master 0 */
> + if (master & GENMASK(7, 1))
> + pm8821_irq_master_handler(chip, 0, master);
> +
> + /* bit 0 marks if master 1 contains any bits */
> + if (!(master & BIT(0)))
> + goto done;
> +
> + ret = regmap_read(chip->regmap,
> + PM8821_SSBI_REG_ADDR_IRQ_MASTER1, &master);
> + if (ret) {
> + pr_err("Failed to read master 1 ret=%d\n", ret);
> + goto done;
> + }
> +
> + pm8821_irq_master_handler(chip, 1, master);
> +
> +done:
> + chained_irq_exit(irq_chip, desc);
> +}
> +
> static void pm8xxx_irq_mask_ack(struct irq_data *d)
> {
> struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
> @@ -299,6 +392,104 @@ static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
> .map = pm8xxx_irq_domain_map,
> };
>
> +static void pm8821_irq_mask_ack(struct irq_data *d)
> +{
> + struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
> + unsigned int pmirq = irqd_to_hwirq(d);
> + u8 block, master;
> + int irq_bit, rc;
> +
> + block = pmirq / 8;
> + master = block / PM8821_BLOCKS_PER_MASTER;
> + irq_bit = pmirq % 8;
> + block %= PM8821_BLOCKS_PER_MASTER;
> +
> + rc = regmap_update_bits(chip->regmap,
> + PM8821_SSBI_ADDR_IRQ_MASK(master, block),
> + BIT(irq_bit), BIT(irq_bit));
> + if (rc) {
> + pr_err("Failed to mask IRQ:%d rc=%d\n", pmirq, rc);
> + return;
> + }
> +
> + rc = regmap_update_bits(chip->regmap,
> + PM8821_SSBI_ADDR_IRQ_CLEAR(master, block),
> + BIT(irq_bit), BIT(irq_bit));
> + if (rc)
> + pr_err("Failed to CLEAR IRQ:%d rc=%d\n", pmirq, rc);
> +}
> +
> +static void pm8821_irq_unmask(struct irq_data *d)
> +{
> + struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
> + unsigned int pmirq = irqd_to_hwirq(d);
> + int irq_bit, rc;
> + u8 block, master;
> +
> + block = pmirq / 8;
> + master = block / PM8821_BLOCKS_PER_MASTER;
> + irq_bit = pmirq % 8;
> + block %= PM8821_BLOCKS_PER_MASTER;
> +
> + rc = regmap_update_bits(chip->regmap,
> + PM8821_SSBI_ADDR_IRQ_MASK(master, block),
> + BIT(irq_bit), ~BIT(irq_bit));
> + if (rc)
> + pr_err("Failed to read/write unmask IRQ:%d rc=%d\n", pmirq, rc);
> +
> +}
> +
> +static int pm8821_irq_get_irqchip_state(struct irq_data *d,
> + enum irqchip_irq_state which,
> + bool *state)
> +{
> + struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
> + int rc, pmirq = irqd_to_hwirq(d);
> + u8 block, irq_bit, master;
> + unsigned int bits;
> +
> + block = pmirq / 8;
> + master = block / PM8821_BLOCKS_PER_MASTER;
> + irq_bit = pmirq % 8;
> + block %= PM8821_BLOCKS_PER_MASTER;
> +
> + rc = regmap_read(chip->regmap,
> + PM8821_SSBI_ADDR_IRQ_RT_STATUS(master, block), &bits);
> + if (rc) {
> + pr_err("Reading Status of IRQ %d failed rc=%d\n", pmirq, rc);
> + return rc;
> + }
> +
> + *state = !!(bits & BIT(irq_bit));
> +
> + return rc;
> +}
> +
> +static struct irq_chip pm8821_irq_chip = {
> + .name = "pm8821",
> + .irq_mask_ack = pm8821_irq_mask_ack,
> + .irq_unmask = pm8821_irq_unmask,
> + .irq_get_irqchip_state = pm8821_irq_get_irqchip_state,
> + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
> +};
> +
> +static int pm8821_irq_domain_map(struct irq_domain *d, unsigned int irq,
> + irq_hw_number_t hwirq)
> +{
> + struct pm_irq_chip *chip = d->host_data;
> +
> + irq_set_chip_and_handler(irq, &pm8821_irq_chip, handle_level_irq);
> + irq_set_chip_data(irq, chip);
> + irq_set_noprobe(irq);
> +
> + return 0;
> +}
> +
> +static const struct irq_domain_ops pm8821_irq_domain_ops = {
> + .xlate = irq_domain_xlate_twocell,
> + .map = pm8821_irq_domain_map,
> +};
> +
> static const struct regmap_config ssbi_regmap_config = {
> .reg_bits = 16,
> .val_bits = 8,
> @@ -308,22 +499,41 @@ static const struct regmap_config ssbi_regmap_config = {
> .reg_write = ssbi_reg_write
> };
>
> +static const struct pm_irq_data pm8xxx_data = {
> + .num_irqs = PM8XXX_NR_IRQS,
> + .irq_domain_ops = &pm8xxx_irq_domain_ops,
> + .irq_handler = pm8xxx_irq_handler,
> +};
> +
> +static const struct pm_irq_data pm8821_data = {
> + .num_irqs = PM8821_NR_IRQS,
> + .irq_domain_ops = &pm8821_irq_domain_ops,
> + .irq_handler = pm8821_irq_handler,
> +};
> +
> static const struct of_device_id pm8xxx_id_table[] = {
> - { .compatible = "qcom,pm8018", },
> - { .compatible = "qcom,pm8058", },
> - { .compatible = "qcom,pm8921", },
> + { .compatible = "qcom,pm8018", .data = &pm8xxx_data},
> + { .compatible = "qcom,pm8058", .data = &pm8xxx_data},
> + { .compatible = "qcom,pm8821", .data = &pm8821_data},
> + { .compatible = "qcom,pm8921", .data = &pm8xxx_data},
> { }
> };
> MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
>
> static int pm8xxx_probe(struct platform_device *pdev)
> {
> + const struct pm_irq_data *data;
> struct regmap *regmap;
> int irq, rc;
> unsigned int val;
> u32 rev;
> struct pm_irq_chip *chip;
> - unsigned int nirqs = PM8XXX_NR_IRQS;
> +
> + data = of_device_get_match_data(&pdev->dev);
> + if (!data) {
> + dev_err(&pdev->dev, "No matching driver data found\n");
> + return -EINVAL;
> + }
>
> irq = platform_get_irq(pdev, 0);
> if (irq < 0)
> @@ -354,25 +564,26 @@ static int pm8xxx_probe(struct platform_device *pdev)
> rev |= val << BITS_PER_BYTE;
>
> chip = devm_kzalloc(&pdev->dev, sizeof(*chip) +
> - sizeof(chip->config[0]) * nirqs,
> - GFP_KERNEL);
> + sizeof(chip->config[0]) * data->num_irqs,
> + GFP_KERNEL);
> if (!chip)
> return -ENOMEM;
>
> platform_set_drvdata(pdev, chip);
> chip->regmap = regmap;
> - chip->num_irqs = nirqs;
> + chip->num_irqs = data->num_irqs;
> chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
> chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
> spin_lock_init(&chip->pm_irq_lock);
>
> - chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, nirqs,
> - &pm8xxx_irq_domain_ops,
> + chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
> + data->num_irqs,
> + data->irq_domain_ops,
> chip);
> if (!chip->irqdomain)
> return -ENODEV;
>
> - irq_set_chained_handler_and_data(irq, pm8xxx_irq_handler, chip);
> + irq_set_chained_handler_and_data(irq, data->irq_handler, chip);
> irq_set_irq_wake(irq, 1);
>
> rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v5 3/3] dt-bindings: i2c: pxa: Update the documentation for the Armada 3700
From: Romain Perier @ 2016-11-21 13:32 UTC (permalink / raw)
To: Wolfram Sang, linux-i2c-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jason Cooper,
Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
Thomas Petazzoni, Nadav Haklai, Omri Itach, Shadi Ammouri,
Yahuda Yitschak, Hanna Hawa, Neta Zur Hershkovits, Igal Liberman,
Marcin Wojtas <mw>
In-Reply-To: <20161121133247.29889-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
This commit documents the compatible string to have the compatibility for
the I2C unit found in the Armada 3700.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Changes in v5:
- Added the tag 'Acked-by', by Rob Herring
Changes in v2:
- Fixed wrong compatible string, it should be "marvell,armada-3700-i2c"
and not "marvell,armada-3700".
Documentation/devicetree/bindings/i2c/i2c-pxa.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/i2c/i2c-pxa.txt b/Documentation/devicetree/bindings/i2c/i2c-pxa.txt
index 12b78ac..d30f0b1 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-pxa.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-pxa.txt
@@ -7,6 +7,7 @@ Required properties :
compatible processor, e.g. pxa168, pxa910, mmp2, mmp3.
For the pxa2xx/pxa3xx, an additional node "mrvl,pxa-i2c" is required
as shown in the example below.
+ For the Armada 3700, the compatible should be "marvell,armada-3700-i2c".
Recommended properties :
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v5 2/3] arm64: dts: marvell: Add I2C definitions for the Armada 3700
From: Romain Perier @ 2016-11-21 13:32 UTC (permalink / raw)
To: Wolfram Sang, linux-i2c-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jason Cooper,
Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
Thomas Petazzoni, Nadav Haklai, Omri Itach, Shadi Ammouri,
Yahuda Yitschak, Hanna Hawa, Neta Zur Hershkovits, Igal Liberman,
Marcin Wojtas <mw>
In-Reply-To: <20161121133247.29889-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
The Armada 3700 has two i2c bus interface units, this commit adds the
definitions of the corresponding device nodes. It also enables the node
on the development board for this SoC.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Acked-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
arch/arm64/boot/dts/marvell/armada-3720-db.dts | 4 ++++
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 18 ++++++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/arch/arm64/boot/dts/marvell/armada-3720-db.dts b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
index 1372e9a6..16d84af 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts
+++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
@@ -62,6 +62,10 @@
};
};
+&i2c0 {
+ status = "okay";
+};
+
/* CON3 */
&sata {
status = "okay";
diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
index e9bd587..1b0fd21 100644
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -98,6 +98,24 @@
/* 32M internal register @ 0xd000_0000 */
ranges = <0x0 0x0 0xd0000000 0x2000000>;
+ i2c0: i2c@11000 {
+ compatible = "marvell,armada-3700-i2c";
+ reg = <0x11000 0x24>;
+ clocks = <&nb_periph_clk 10>;
+ interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+ mrvl,i2c-fast-mode;
+ status = "disabled";
+ };
+
+ i2c1: i2c@11080 {
+ compatible = "marvell,armada-3700-i2c";
+ reg = <0x11080 0x24>;
+ clocks = <&nb_periph_clk 9>;
+ interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+ mrvl,i2c-fast-mode;
+ status = "disabled";
+ };
+
uart0: serial@12000 {
compatible = "marvell,armada-3700-uart";
reg = <0x12000 0x400>;
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v5 1/3] i2c: pxa: Add support for the I2C units found in Armada 3700
From: Romain Perier @ 2016-11-21 13:32 UTC (permalink / raw)
To: Wolfram Sang, linux-i2c-u79uwXL29TY76Z2rM5mHXA
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jason Cooper,
Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
Thomas Petazzoni, Nadav Haklai, Omri Itach, Shadi Ammouri,
Yahuda Yitschak, Hanna Hawa, Neta Zur Hershkovits, Igal Liberman,
Marcin Wojtas <mw>
In-Reply-To: <20161121133247.29889-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
The Armada 3700 has two I2C controllers that is compliant with the I2C
Bus Specificiation 2.1, supports multi-master and different bus speed:
Standard mode (up to 100 KHz), Fast mode (up to 400 KHz),
High speed mode (up to 3.4 Mhz).
This IP block has a lot of similarity with the PXA, except some register
offsets and bitfield. This commits adds a basic support for this I2C
unit.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Tested-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
Changes in v5:
- Don't define registers for armada-3700, we can re-use the ones
for PXA3XX.
- Define registers mask when OF is not used, in probe_pdata.
Changes in v4:
- Replaced the type of hs_mask and fm_mask by u32, instead of
unsigned int, As writel() take an u32 as first argument...
Changes in v3:
- Replaced the type of hs_mask and fm_mask by unsigned int,
instead of unsigned long.
drivers/i2c/busses/Kconfig | 2 +-
drivers/i2c/busses/i2c-pxa.c | 24 ++++++++++++++++++++++--
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index d252276..2f56a26 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -763,7 +763,7 @@ config I2C_PUV3
config I2C_PXA
tristate "Intel PXA2XX I2C adapter"
- depends on ARCH_PXA || ARCH_MMP || (X86_32 && PCI && OF)
+ depends on ARCH_PXA || ARCH_MMP || ARCH_MVEBU || (X86_32 && PCI && OF)
help
If you have devices in the PXA I2C bus, say yes to this option.
This driver can also be built as a module. If so, the module
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index e28b825..374d17c 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -98,6 +98,7 @@ static const struct platform_device_id i2c_pxa_id_table[] = {
{ "pxa3xx-pwri2c", REGS_PXA3XX },
{ "ce4100-i2c", REGS_CE4100 },
{ "pxa910-i2c", REGS_PXA910 },
+ { "armada-3700-i2c", REGS_PXA3XX },
{ },
};
MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
@@ -122,7 +123,9 @@ MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
#define ICR_SADIE (1 << 13) /* slave address detected int enable */
#define ICR_UR (1 << 14) /* unit reset */
#define ICR_FM (1 << 15) /* fast mode */
+#define ICR_BUSMODE_FM (1 << 16) /* shifted fast mode for armada-3700 */
#define ICR_HS (1 << 16) /* High Speed mode */
+#define ICR_BUSMODE_HS (1 << 17) /* shifted high speed mode for armada-3700 */
#define ICR_GPIOEN (1 << 19) /* enable GPIO mode for SCL in HS */
#define ISR_RWM (1 << 0) /* read/write mode */
@@ -193,6 +196,8 @@ struct pxa_i2c {
unsigned char master_code;
unsigned long rate;
bool highmode_enter;
+ u32 fm_mask;
+ u32 hs_mask;
};
#define _IBMR(i2c) ((i2c)->reg_ibmr)
@@ -503,8 +508,8 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c)
writel(i2c->slave_addr, _ISAR(i2c));
/* set control register values */
- writel(I2C_ICR_INIT | (i2c->fast_mode ? ICR_FM : 0), _ICR(i2c));
- writel(readl(_ICR(i2c)) | (i2c->high_mode ? ICR_HS : 0), _ICR(i2c));
+ writel(I2C_ICR_INIT | (i2c->fast_mode ? i2c->fm_mask : 0), _ICR(i2c));
+ writel(readl(_ICR(i2c)) | (i2c->high_mode ? i2c->hs_mask : 0), _ICR(i2c));
#ifdef CONFIG_I2C_PXA_SLAVE
dev_info(&i2c->adap.dev, "Enabling slave mode\n");
@@ -1137,6 +1142,7 @@ static const struct of_device_id i2c_pxa_dt_ids[] = {
{ .compatible = "mrvl,pxa-i2c", .data = (void *)REGS_PXA2XX },
{ .compatible = "mrvl,pwri2c", .data = (void *)REGS_PXA3XX },
{ .compatible = "mrvl,mmp-twsi", .data = (void *)REGS_PXA910 },
+ { .compatible = "marvell,armada-3700-i2c", .data = (void *)REGS_PXA3XX },
{}
};
MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
@@ -1158,6 +1164,13 @@ static int i2c_pxa_probe_dt(struct platform_device *pdev, struct pxa_i2c *i2c,
i2c->use_pio = 1;
if (of_get_property(np, "mrvl,i2c-fast-mode", NULL))
i2c->fast_mode = 1;
+ if (of_device_is_compatible(np, "marvell,armada-3700-i2c")) {
+ i2c->fm_mask = ICR_BUSMODE_FM;
+ i2c->hs_mask = ICR_BUSMODE_HS;
+ } else {
+ i2c->fm_mask = ICR_FM;
+ i2c->hs_mask = ICR_HS;
+ }
*i2c_types = (enum pxa_i2c_types)(of_id->data);
@@ -1181,6 +1194,13 @@ static int i2c_pxa_probe_pdata(struct platform_device *pdev,
i2c->master_code = 0xe;
i2c->rate = plat->rate;
}
+ if (!strcmp(id->name, "armada-3700-i2c")) {
+ i2c->fm_mask = ICR_BUSMODE_FM;
+ i2c->hs_mask = ICR_BUSMODE_HS;
+ } else {
+ i2c->fm_mask = ICR_FM;
+ i2c->hs_mask = ICR_HS;
+ }
return 0;
}
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v5 0/3] Add basic support for the I2C units of the Armada 3700
From: Romain Perier @ 2016-11-21 13:32 UTC (permalink / raw)
To: Wolfram Sang, linux-i2c
Cc: devicetree, Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland,
Kumar Gala, linux-arm-kernel, Jason Cooper, Andrew Lunn,
Sebastian Hesselbarth, Gregory Clement, Thomas Petazzoni,
Nadav Haklai, Omri Itach, Shadi Ammouri, Yahuda Yitschak,
Hanna Hawa, Neta Zur Hershkovits, Igal Liberman,
Marcin Wojtas <mw>
This series add basic support for the I2C bus interface units present
in the Armada 3700 to the pxa-i2c driver. It also add the definitions of
the device nodes to the devicetree at the SoC level and for its official
development board: the Armada 3720 DB.
Romain Perier (3):
i2c: pxa: Add support for the I2C units found in Armada 3700
arm64: dts: marvell: Add I2C definitions for the Armada 3700
dt-bindings: i2c: pxa: Update the documentation for the Armada 3700
Documentation/devicetree/bindings/i2c/i2c-pxa.txt | 1 +
arch/arm64/boot/dts/marvell/armada-3720-db.dts | 4 ++++
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 18 +++++++++++++++++
drivers/i2c/busses/Kconfig | 2 +-
drivers/i2c/busses/i2c-pxa.c | 24 +++++++++++++++++++++--
5 files changed, 46 insertions(+), 3 deletions(-)
--
2.9.3
^ permalink raw reply
* Re: [PATCH] PM / Domains: Fix compatible for domain idle state
From: Ulf Hansson @ 2016-11-21 13:27 UTC (permalink / raw)
To: Lina Iyer
Cc: devicetree@vger.kernel.org, Lorenzo Pieralisi, Juri Lelli,
linux-pm@vger.kernel.org, Rafael J. Wysocki, Kevin Hilman,
Stephen Boyd, Sudeep Holla, Brendan Jackman,
linux-arm-msm@vger.kernel.org, Andy Gross,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <1478210075-92045-2-git-send-email-lina.iyer@linaro.org>
On 3 November 2016 at 22:54, Lina Iyer <lina.iyer@linaro.org> wrote:
> Re-using idle state definition provided by arm,idle-state for domain
> idle states creates a lot of confusion and limits further evolution of
> the domain idle definition. To keep things clear and simple, define a
> idle states for domain using a new compatible "domain-idle-state".
>
> Fix existing PM domains code to look for the newly defined compatible.
>
> Cc: <devicetree@vger.kernel.org>
> Cc: Rob Herring <robh@kernel.org>
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Kind regards
Uffe
> ---
> .../bindings/power/domain-idle-state.txt | 33 ++++++++++++++++++++++
> .../devicetree/bindings/power/power_domain.txt | 8 +++---
> drivers/base/power/domain.c | 2 +-
> 3 files changed, 38 insertions(+), 5 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/power/domain-idle-state.txt
>
> diff --git a/Documentation/devicetree/bindings/power/domain-idle-state.txt b/Documentation/devicetree/bindings/power/domain-idle-state.txt
> new file mode 100644
> index 0000000..eefc7ed
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/domain-idle-state.txt
> @@ -0,0 +1,33 @@
> +PM Domain Idle State Node:
> +
> +A domain idle state node represents the state parameters that will be used to
> +select the state when there are no active components in the domain.
> +
> +The state node has the following parameters -
> +
> +- compatible:
> + Usage: Required
> + Value type: <string>
> + Definition: Must be "domain-idle-state".
> +
> +- entry-latency-us
> + Usage: Required
> + Value type: <prop-encoded-array>
> + Definition: u32 value representing worst case latency in
> + microseconds required to enter the idle state.
> + The exit-latency-us duration may be guaranteed
> + only after entry-latency-us has passed.
> +
> +- exit-latency-us
> + Usage: Required
> + Value type: <prop-encoded-array>
> + Definition: u32 value representing worst case latency
> + in microseconds required to exit the idle state.
> +
> +- min-residency-us
> + Usage: Required
> + Value type: <prop-encoded-array>
> + Definition: u32 value representing minimum residency duration
> + in microseconds after which the idle state will yield
> + power benefits after overcoming the overhead in entering
> +i the idle state.
> diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
> index e165036..723e1ad 100644
> --- a/Documentation/devicetree/bindings/power/power_domain.txt
> +++ b/Documentation/devicetree/bindings/power/power_domain.txt
> @@ -31,7 +31,7 @@ Optional properties:
>
> - domain-idle-states : A phandle of an idle-state that shall be soaked into a
> generic domain power state. The idle state definitions are
> - compatible with arm,idle-state specified in [1].
> + compatible with domain-idle-state specified in [1].
> The domain-idle-state property reflects the idle state of this PM domain and
> not the idle states of the devices or sub-domains in the PM domain. Devices
> and sub-domains have their own idle-states independent of the parent
> @@ -85,7 +85,7 @@ Example 3:
> };
>
> DOMAIN_RET: state@0 {
> - compatible = "arm,idle-state";
> + compatible = "domain-idle-state";
> reg = <0x0>;
> entry-latency-us = <1000>;
> exit-latency-us = <2000>;
> @@ -93,7 +93,7 @@ Example 3:
> };
>
> DOMAIN_PWR_DN: state@1 {
> - compatible = "arm,idle-state";
> + compatible = "domain-idle-state";
> reg = <0x1>;
> entry-latency-us = <5000>;
> exit-latency-us = <8000>;
> @@ -118,4 +118,4 @@ The node above defines a typical PM domain consumer device, which is located
> inside a PM domain with index 0 of a power controller represented by a node
> with the label "power".
>
> -[1]. Documentation/devicetree/bindings/arm/idle-states.txt
> +[1]. Documentation/devicetree/bindings/power/domain-idle-state.txt
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index 661737c..f0bc672 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -2048,7 +2048,7 @@ int genpd_dev_pm_attach(struct device *dev)
> EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
>
> static const struct of_device_id idle_state_match[] = {
> - { .compatible = "arm,idle-state", },
> + { .compatible = "domain-idle-state", },
> { }
> };
>
> --
> 2.7.4
>
^ permalink raw reply
* Re: [PATCH 03/10] mfd: sun6i-prcm: Add codec analog controls sub-device for Allwinner A23
From: Lee Jones @ 2016-11-21 13:23 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Liam Girdwood, Mark Brown, Maxime Ripard, Rob Herring,
Mark Rutland, alsa-devel, linux-arm-kernel, linux-kernel,
devicetree, Mylene Josserand
In-Reply-To: <20161112064648.26779-4-wens@csie.org>
On Sat, 12 Nov 2016, Chen-Yu Tsai wrote:
> The PRCM block on the A23 contains a message box like interface to
> the registers for the analog path controls of the internal codec.
>
> Add a sub-device for it.
>
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> ---
> drivers/mfd/sun6i-prcm.c | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> diff --git a/drivers/mfd/sun6i-prcm.c b/drivers/mfd/sun6i-prcm.c
> index 011fcc555945..4abbf2ede944 100644
> --- a/drivers/mfd/sun6i-prcm.c
> +++ b/drivers/mfd/sun6i-prcm.c
> @@ -12,6 +12,9 @@
> #include <linux/init.h>
> #include <linux/of.h>
>
> +#define SUN8I_CODEC_ANALOG_BASE 0x1c0
> +#define SUN8I_CODEC_ANALOG_END (SUN8I_CODEC_ANALOG_BASE + 0x4 - 1)
> +
> struct prcm_data {
> int nsubdevs;
> const struct mfd_cell *subdevs;
> @@ -57,6 +60,14 @@ static const struct resource sun6i_a31_apb0_rstc_res[] = {
> },
> };
>
> +static const struct resource sun8i_codec_analog_res[] = {
> + {
> + .start = SUN8I_CODEC_ANALOG_BASE,
> + .end = SUN8I_CODEC_ANALOG_END,
> + .flags = IORESOURCE_MEM,
> + },
> +};
Use the DEVICE_RES_* macros.
> static const struct mfd_cell sun6i_a31_prcm_subdevs[] = {
> {
> .name = "sun6i-a31-ar100-clk",
> @@ -109,6 +120,12 @@ static const struct mfd_cell sun8i_a23_prcm_subdevs[] = {
> .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res),
> .resources = sun6i_a31_apb0_rstc_res,
> },
> + {
> + .name = "sun8i-codec-analog",
> + .of_compatible = "allwinner,sun8i-a23-codec-analog",
> + .num_resources = ARRAY_SIZE(sun8i_codec_analog_res),
> + .resources = sun8i_codec_analog_res,
> + },
> };
>
> static const struct prcm_data sun6i_a31_prcm_data = {
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH v3 7/7] i2c: i2c-mux-simple: new driver
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
This is a generic simple i2c mux that uses the generic multiplexer
subsystem to do the muxing.
The user can select if the mux is to be mux-locked and parent-locked
as described in Documentation/i2c/i2c-topology.
Signed-off-by: Peter Rosin <peda@axentia.se>
---
drivers/i2c/muxes/Kconfig | 13 +++
drivers/i2c/muxes/Makefile | 1 +
drivers/i2c/muxes/i2c-mux-simple.c | 177 +++++++++++++++++++++++++++++++++++++
3 files changed, 191 insertions(+)
create mode 100644 drivers/i2c/muxes/i2c-mux-simple.c
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index 96de9ce5669b..fb4f587d822a 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -73,6 +73,19 @@ config I2C_MUX_REG
This driver can also be built as a module. If so, the module
will be called i2c-mux-reg.
+config I2C_MUX_SIMPLE
+ tristate "Simple I2C multiplexer"
+ select MULTIPLEXER
+ depends on OF
+ help
+ If you say yes to this option, support will be included for a
+ simple generic I2C multiplexer. This driver provides access to
+ I2C busses connected through a MUX, which is controlled
+ by a generic MUX controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-mux-simple.
+
config I2C_DEMUX_PINCTRL
tristate "pinctrl-based I2C demultiplexer"
depends on PINCTRL && OF
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index 7c267c29b191..eea1fb9e6466 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -10,5 +10,6 @@ obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o
+obj-$(CONFIG_I2C_MUX_SIMPLE) += i2c-mux-simple.o
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/muxes/i2c-mux-simple.c b/drivers/i2c/muxes/i2c-mux-simple.c
new file mode 100644
index 000000000000..a2f1dc8a3295
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-mux-simple.c
@@ -0,0 +1,177 @@
+/*
+ * Generic simple I2C multiplexer
+ *
+ * Peter Rosin <peda@axentia.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/module.h>
+#include <linux/mux.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+struct mux {
+ struct mux_control *control;
+
+ bool do_not_deselect;
+};
+
+static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct mux *mux = i2c_mux_priv(muxc);
+ int ret;
+
+ ret = mux_control_select(mux->control, chan);
+ mux->do_not_deselect = ret < 0;
+
+ return ret;
+}
+
+static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct mux *mux = i2c_mux_priv(muxc);
+
+ if (mux->do_not_deselect)
+ return 0;
+
+ return mux_control_deselect(mux->control);
+}
+
+static struct i2c_adapter *mux_parent_adapter(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *parent_np;
+ struct i2c_adapter *parent;
+
+ parent_np = of_parse_phandle(np, "i2c-parent", 0);
+ if (!parent_np) {
+ dev_err(dev, "Cannot parse i2c-parent\n");
+ return ERR_PTR(-ENODEV);
+ }
+ parent = of_find_i2c_adapter_by_node(parent_np);
+ of_node_put(parent_np);
+ if (!parent)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return parent;
+}
+
+static const struct of_device_id i2c_mux_of_match[] = {
+ { .compatible = "i2c-mux-simple,parent-locked",
+ .data = (void *)0, },
+ { .compatible = "i2c-mux-simple,mux-locked",
+ .data = (void *)1, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_mux_of_match);
+
+static int i2c_mux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *child;
+ const struct of_device_id *match;
+ struct i2c_mux_core *muxc;
+ struct mux *mux;
+ struct i2c_adapter *parent;
+ int children;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return -ENOMEM;
+
+ mux->control = devm_mux_control_get(dev);
+ if (IS_ERR(mux->control)) {
+ if (PTR_ERR(mux->control) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get control-mux\n");
+ return PTR_ERR(mux->control);
+ }
+
+ parent = mux_parent_adapter(dev);
+ if (IS_ERR(parent)) {
+ if (PTR_ERR(parent) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get i2c-parent adapter\n");
+ return PTR_ERR(parent);
+ }
+
+ children = of_get_child_count(np);
+
+ muxc = i2c_mux_alloc(parent, dev, children, 0, 0,
+ i2c_mux_select, i2c_mux_deselect);
+ if (!muxc) {
+ ret = -ENOMEM;
+ goto err_parent;
+ }
+ muxc->priv = mux;
+
+ platform_set_drvdata(pdev, muxc);
+
+ match = of_match_device(of_match_ptr(i2c_mux_of_match), dev);
+ if (match)
+ muxc->mux_locked = !!of_device_get_match_data(dev);
+
+ for_each_child_of_node(np, child) {
+ u32 chan;
+
+ ret = of_property_read_u32(child, "reg", &chan);
+ if (ret < 0) {
+ dev_err(dev, "no reg property for node '%s'\n",
+ child->name);
+ goto err_children;
+ }
+
+ if (chan >= mux->control->states) {
+ dev_err(dev, "invalid reg %u\n", chan);
+ ret = -EINVAL;
+ goto err_children;
+ }
+
+ ret = i2c_mux_add_adapter(muxc, 0, chan, 0);
+ if (ret)
+ goto err_children;
+ }
+
+ dev_info(dev, "%d-port mux on %s adapter\n", children, parent->name);
+
+ return 0;
+
+err_children:
+ i2c_mux_del_adapters(muxc);
+err_parent:
+ i2c_put_adapter(parent);
+
+ return ret;
+}
+
+static int i2c_mux_remove(struct platform_device *pdev)
+{
+ struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
+
+ i2c_mux_del_adapters(muxc);
+ i2c_put_adapter(muxc->parent);
+
+ return 0;
+}
+
+static struct platform_driver i2c_mux_driver = {
+ .probe = i2c_mux_probe,
+ .remove = i2c_mux_remove,
+ .driver = {
+ .name = "i2c-mux-simple",
+ .of_match_table = i2c_mux_of_match,
+ },
+};
+module_platform_driver(i2c_mux_driver);
+
+MODULE_DESCRIPTION("Simple I2C multiplexer driver");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");
--
2.1.4
^ permalink raw reply related
* [PATCH v3 6/7] dt-bindings: i2c: i2c-mux-simple: document i2c-mux-simple bindings
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../devicetree/bindings/i2c/i2c-mux-simple.txt | 76 ++++++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt
diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt
new file mode 100644
index 000000000000..decfd742b297
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt
@@ -0,0 +1,76 @@
+Simple I2C Bus Mux
+
+This binding describes an I2C bus multiplexer that uses a mux controller
+from the mux subsystem to route the I2C signals.
+
+ .-----. .-----.
+ | dev | | dev |
+ .------------. '-----' '-----'
+ | SoC | | |
+ | | .--------+--------'
+ | .------. | .------+ child bus A, on MUX value set to 0
+ | | I2C |-|--| Mux |
+ | '------' | '--+---+ child bus B, on MUX value set to 1
+ | .------. | | '----------+--------+--------.
+ | | MUX- | | | | | |
+ | | Ctrl |-|-----+ .-----. .-----. .-----.
+ | '------' | | dev | | dev | | dev |
+ '------------' '-----' '-----' '-----'
+
+Required properties:
+- compatible: i2c-mux-simple,mux-locked or i2c-mux-simple,parent-locked
+- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side
+ port is connected to.
+* Standard I2C mux properties. See i2c-mux.txt in this directory.
+* I2C child bus nodes. See i2c-mux.txt in this directory. The sub-bus number
+ is also the mux-controller state described in ../misc/mux-controller.txt
+
+For each i2c child node, an I2C child bus will be created. They will
+be numbered based on their order in the device tree.
+
+Whenever an access is made to a device on a child bus, the value set
+in the relevant node's reg property will be set as the state in the
+mux controller.
+
+Example:
+ mux {
+ compatible = "mux-gpio";
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+
+ i2c-mux {
+ compatible = "i2c-mux-simple,mux-locked";
+ i2c-parent = <&i2c1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ssd1307: oled@3c {
+ compatible = "solomon,ssd1307fb-i2c";
+ reg = <0x3c>;
+ pwms = <&pwm 4 3000>;
+ reset-gpios = <&gpio2 7 1>;
+ reset-active-low;
+ };
+ };
+
+ i2c@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pca9555: pca9555@20 {
+ compatible = "nxp,pca9555";
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0x20>;
+ };
+ };
+ };
+ };
--
2.1.4
^ permalink raw reply related
* [PATCH v3 5/7] iio: multiplexer: new iio category and iio-mux driver
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
When a multiplexer changes how an iio device behaves (for example
by feeding different signals to an ADC), this driver can be used
create one virtual iio channel for each multiplexer state.
Depends on the generic multiplexer subsystem.
Cache any ext_info values from the parent iio channel, creating a private
copy of the ext_info attributes for each multiplexer state/channel.
Signed-off-by: Peter Rosin <peda@axentia.se>
---
MAINTAINERS | 1 +
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/multiplexer/Kconfig | 18 ++
drivers/iio/multiplexer/Makefile | 6 +
drivers/iio/multiplexer/iio-mux.c | 450 ++++++++++++++++++++++++++++++++++++++
6 files changed, 477 insertions(+)
create mode 100644 drivers/iio/multiplexer/Kconfig
create mode 100644 drivers/iio/multiplexer/Makefile
create mode 100644 drivers/iio/multiplexer/iio-mux.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 3fc0dbbc04ea..02d9072aae16 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6129,6 +6129,7 @@ M: Peter Rosin <peda@axentia.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
+F: drivers/iio/multiplexer/iio-mux.c
IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23@kernel.org>
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 6743b18194fb..dcb541d0d70e 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -82,6 +82,7 @@ source "drivers/iio/humidity/Kconfig"
source "drivers/iio/imu/Kconfig"
source "drivers/iio/light/Kconfig"
source "drivers/iio/magnetometer/Kconfig"
+source "drivers/iio/multiplexer/Kconfig"
source "drivers/iio/orientation/Kconfig"
if IIO_TRIGGER
source "drivers/iio/trigger/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 87e4c4369e2f..f9879c29cf6f 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -27,6 +27,7 @@ obj-y += humidity/
obj-y += imu/
obj-y += light/
obj-y += magnetometer/
+obj-y += multiplexer/
obj-y += orientation/
obj-y += potentiometer/
obj-y += pressure/
diff --git a/drivers/iio/multiplexer/Kconfig b/drivers/iio/multiplexer/Kconfig
new file mode 100644
index 000000000000..70a044510686
--- /dev/null
+++ b/drivers/iio/multiplexer/Kconfig
@@ -0,0 +1,18 @@
+#
+# Multiplexer drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Multiplexers"
+
+config IIO_MUX
+ tristate "IIO multiplexer driver"
+ select MULTIPLEXER
+ depends on OF
+ help
+ Say yes here to build support for the IIO multiplexer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iio-mux.
+
+endmenu
diff --git a/drivers/iio/multiplexer/Makefile b/drivers/iio/multiplexer/Makefile
new file mode 100644
index 000000000000..68be3c4abd07
--- /dev/null
+++ b/drivers/iio/multiplexer/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for industrial I/O multiplexer drivers
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_IIO_MUX) += iio-mux.o
diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c
new file mode 100644
index 000000000000..0b9d04914241
--- /dev/null
+++ b/drivers/iio/multiplexer/iio-mux.c
@@ -0,0 +1,450 @@
+/*
+ * IIO multiplexer driver
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/mux.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+struct mux_ext_info_cache {
+ char *data;
+ size_t size;
+};
+
+struct mux_child {
+ struct mux_ext_info_cache *ext_info_cache;
+};
+
+struct mux {
+ int cached_state;
+ struct mux_control *control;
+ struct iio_channel *parent;
+ struct iio_dev *indio_dev;
+ struct iio_chan_spec *chan;
+ struct iio_chan_spec_ext_info *ext_info;
+ struct mux_child *child;
+};
+
+static int iio_mux_select(struct mux *mux, int idx)
+{
+ struct mux_child *child = &mux->child[idx];
+ struct iio_chan_spec const *chan = &mux->chan[idx];
+ int ret;
+ int i;
+
+ ret = mux_control_select(mux->control, chan->channel);
+ if (ret < 0) {
+ mux->cached_state = -1;
+ return ret;
+ }
+
+ if (mux->cached_state == chan->channel)
+ return 0;
+
+ if (chan->ext_info) {
+ for (i = 0; chan->ext_info[i].name; ++i) {
+ const char *attr = chan->ext_info[i].name;
+ struct mux_ext_info_cache *cache;
+
+ cache = &child->ext_info_cache[i];
+
+ if (cache->size < 0)
+ continue;
+
+ ret = iio_write_channel_ext_info(mux->parent, attr,
+ cache->data,
+ cache->size);
+
+ if (ret < 0) {
+ mux_control_deselect(mux->control);
+ mux->cached_state = -1;
+ return ret;
+ }
+ }
+ }
+ mux->cached_state = chan->channel;
+
+ return 0;
+}
+
+static void iio_mux_deselect(struct mux *mux)
+{
+ mux_control_deselect(mux->control);
+}
+
+static int mux_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mux *mux = iio_priv(indio_dev);
+ int idx = chan - mux->chan;
+ int ret;
+
+ ret = iio_mux_select(mux, idx);
+ if (ret < 0)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_read_channel_raw(mux->parent, val);
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = iio_read_channel_scale(mux->parent, val, val2);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ iio_mux_deselect(mux);
+
+ return ret;
+}
+
+static int mux_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct mux *mux = iio_priv(indio_dev);
+ int idx = chan - mux->chan;
+ int ret;
+
+ ret = iio_mux_select(mux, idx);
+ if (ret < 0)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *type = IIO_VAL_INT;
+ ret = iio_read_avail_channel_raw(mux->parent, vals, length);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ iio_mux_deselect(mux);
+
+ return ret;
+}
+
+static int mux_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mux *mux = iio_priv(indio_dev);
+ int idx = chan - mux->chan;
+ int ret;
+
+ ret = iio_mux_select(mux, idx);
+ if (ret < 0)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_write_channel_raw(mux->parent, val);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ iio_mux_deselect(mux);
+
+ return ret;
+}
+
+static const struct iio_info mux_info = {
+ .read_raw = mux_read_raw,
+ .read_avail = mux_read_avail,
+ .write_raw = mux_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static ssize_t mux_read_ext_info(struct iio_dev *indio_dev, uintptr_t private,
+ struct iio_chan_spec const *chan, char *buf)
+{
+ struct mux *mux = iio_priv(indio_dev);
+ int idx = chan - mux->chan;
+ ssize_t ret;
+
+ ret = iio_mux_select(mux, idx);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_read_channel_ext_info(mux->parent,
+ mux->ext_info[private].name,
+ buf);
+
+ iio_mux_deselect(mux);
+
+ return ret;
+}
+
+static ssize_t mux_write_ext_info(struct iio_dev *indio_dev, uintptr_t private,
+ struct iio_chan_spec const *chan,
+ const char *buf, size_t len)
+{
+ struct device *dev = indio_dev->dev.parent;
+ struct mux *mux = iio_priv(indio_dev);
+ int idx = chan - mux->chan;
+ char *new;
+ ssize_t ret;
+
+ ret = iio_mux_select(mux, idx);
+ if (ret < 0)
+ return ret;
+
+ new = devm_kmemdup(dev, buf, len + 1, GFP_KERNEL);
+ if (!new) {
+ iio_mux_deselect(mux);
+ return -ENOMEM;
+ }
+
+ new[len] = 0;
+
+ ret = iio_write_channel_ext_info(mux->parent,
+ mux->ext_info[private].name,
+ buf, len);
+ if (ret < 0) {
+ iio_mux_deselect(mux);
+ devm_kfree(dev, new);
+ return ret;
+ }
+
+ devm_kfree(dev, mux->child[idx].ext_info_cache[private].data);
+ mux->child[idx].ext_info_cache[private].data = new;
+ mux->child[idx].ext_info_cache[private].size = len;
+
+ iio_mux_deselect(mux);
+
+ return ret;
+}
+
+static int mux_configure_channel(struct device *dev, struct mux *mux,
+ struct device_node *child_np, int idx)
+{
+ struct mux_child *child = &mux->child[idx];
+ struct iio_chan_spec *chan = &mux->chan[idx];
+ struct iio_chan_spec const *pchan = mux->parent->channel;
+ u32 state;
+ char *page = NULL;
+ int num_ext_info;
+ int i;
+ int ret;
+
+ chan->indexed = 1;
+ chan->output = pchan->output;
+ chan->datasheet_name = child_np->name;
+ chan->ext_info = mux->ext_info;
+
+ ret = iio_get_channel_type(mux->parent, &chan->type);
+ if (ret < 0) {
+ dev_err(dev, "failed to get parent channel type\n");
+ return ret;
+ }
+
+ if (iio_channel_has_info(pchan, IIO_CHAN_INFO_RAW))
+ chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW);
+ if (iio_channel_has_info(pchan, IIO_CHAN_INFO_SCALE))
+ chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE);
+
+ if (iio_channel_has_available(pchan, IIO_CHAN_INFO_RAW))
+ chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW);
+
+ ret = of_property_read_u32(child_np, "reg", &state);
+ if (ret < 0) {
+ dev_err(dev, "no reg property for node '%s'\n", child_np->name);
+ return ret;
+ }
+
+ if (state >= mux->control->states) {
+ dev_err(dev, "invalid reg %u\n", state);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < idx; ++i) {
+ if (mux->chan[i].channel == state) {
+ dev_err(dev, "double use of reg %u\n", state);
+ return -EINVAL;
+ }
+ }
+
+ chan->channel = state;
+
+ num_ext_info = iio_get_channel_ext_info_count(mux->parent);
+ if (num_ext_info) {
+ page = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ }
+ child->ext_info_cache = devm_kzalloc(dev,
+ sizeof(*child->ext_info_cache) *
+ num_ext_info, GFP_KERNEL);
+ for (i = 0; i < num_ext_info; ++i) {
+ child->ext_info_cache[i].size = -1;
+
+ if (!pchan->ext_info[i].write)
+ continue;
+ if (!pchan->ext_info[i].read)
+ continue;
+
+ ret = iio_read_channel_ext_info(mux->parent,
+ mux->ext_info[i].name,
+ page);
+ if (ret < 0) {
+ dev_err(dev, "failed to get ext_info '%s'\n",
+ pchan->ext_info[i].name);
+ return ret;
+ }
+ if (ret >= PAGE_SIZE) {
+ dev_err(dev, "too large ext_info '%s'\n",
+ pchan->ext_info[i].name);
+ return -EINVAL;
+ }
+
+ child->ext_info_cache[i].data = devm_kmemdup(dev, page, ret + 1,
+ GFP_KERNEL);
+ child->ext_info_cache[i].data[ret] = 0;
+ child->ext_info_cache[i].size = ret;
+ }
+
+ if (page)
+ devm_kfree(dev, page);
+
+ return 0;
+}
+
+static int mux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child_np;
+ struct iio_dev *indio_dev;
+ struct iio_channel *parent;
+ struct mux *mux;
+ int sizeof_ext_info;
+ int children;
+ int sizeof_priv;
+ int i;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ parent = devm_iio_channel_get(dev, "parent");
+ if (IS_ERR(parent)) {
+ if (PTR_ERR(parent) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get parent channel\n");
+ return PTR_ERR(parent);
+ }
+
+ sizeof_ext_info = iio_get_channel_ext_info_count(parent);
+ if (sizeof_ext_info) {
+ sizeof_ext_info += 1; /* one extra entry for the sentinel */
+ sizeof_ext_info *= sizeof(*mux->ext_info);
+ }
+
+ children = of_get_child_count(np);
+ if (children <= 0) {
+ dev_err(dev, "not even a single child\n");
+ return -EINVAL;
+ }
+
+ sizeof_priv = sizeof(*mux);
+ sizeof_priv += sizeof(*mux->child) * children;
+ sizeof_priv += sizeof(*mux->chan) * children;
+ sizeof_priv += sizeof_ext_info;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof_priv);
+ if (!indio_dev)
+ return -ENOMEM;
+
+ mux = iio_priv(indio_dev);
+ mux->child = (struct mux_child *)(mux + 1);
+ mux->chan = (struct iio_chan_spec *)(mux->child + children);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ mux->parent = parent;
+ mux->cached_state = -1;
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &mux_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mux->chan;
+ indio_dev->num_channels = children;
+ if (sizeof_ext_info) {
+ mux->ext_info = devm_kmemdup(dev,
+ parent->channel->ext_info,
+ sizeof_ext_info, GFP_KERNEL);
+ if (!mux->ext_info)
+ return -ENOMEM;
+
+ for (i = 0; mux->ext_info[i].name; ++i) {
+ if (parent->channel->ext_info[i].read)
+ mux->ext_info[i].read = mux_read_ext_info;
+ if (parent->channel->ext_info[i].write)
+ mux->ext_info[i].write = mux_write_ext_info;
+ mux->ext_info[i].private = i;
+ }
+ }
+
+ mux->control = devm_mux_control_get(dev);
+ if (IS_ERR(mux->control)) {
+ if (PTR_ERR(mux->control) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get control-mux\n");
+ return PTR_ERR(mux->control);
+ }
+
+ i = 0;
+ for_each_child_of_node(np, child_np) {
+ ret = mux_configure_channel(dev, mux, child_np, i);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret) {
+ dev_err(dev, "failed to register iio device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id mux_match[] = {
+ { .compatible = "iio-mux" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mux_match);
+
+static struct platform_driver mux_driver = {
+ .probe = mux_probe,
+ .driver = {
+ .name = "iio-mux",
+ .of_match_table = mux_match,
+ },
+};
+module_platform_driver(mux_driver);
+
+MODULE_DESCRIPTION("IIO multiplexer driver");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");
--
2.1.4
^ permalink raw reply related
* [PATCH v3 4/7] dt-bindings: iio: iio-mux: document iio-mux bindings
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../bindings/iio/multiplexer/iio-mux.txt | 47 ++++++++++++++++++++++
MAINTAINERS | 6 +++
2 files changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
diff --git a/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt b/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
new file mode 100644
index 000000000000..2807333ccc86
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
@@ -0,0 +1,47 @@
+IIO multiplexer bindings
+
+If a multiplexer is used to select which hardware signal is fed to
+e.g. an ADC channel, these bindings describe that situation.
+
+Required properties:
+- compatible : "iio-mux"
+- io-channels : Channel node of the parent channel that has multiplexed
+ input.
+- io-channel-names : Should be "parent".
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Required properties for iio-mux child nodes:
+- reg : The multiplexer state as described in ../misc/mux-controller.txt
+
+For each iio-mux child, an iio channel will be created whose number will
+match the mux controller state.
+
+Example:
+ mux {
+ compatible = "mux-gpio";
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+
+ adc {
+ compatible = "iio-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sync@0 {
+ reg = <0>;
+ };
+
+ in@1 {
+ reg = <1>;
+ };
+
+ system-regulator@2 {
+ reg = <2>;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 37974aeee750..3fc0dbbc04ea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6124,6 +6124,12 @@ L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/rc/iguanair.c
+IIO MULTIPLEXER
+M: Peter Rosin <peda@axentia.se>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
+
IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23@kernel.org>
R: Hartmut Knaack <knaack.h@gmx.de>
--
2.1.4
^ permalink raw reply related
* [PATCH v3 3/7] iio: inkern: api for manipulating ext_info of iio channels
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
Extend the inkern api with functions for reading and writing ext_info
of iio channels.
Signed-off-by: Peter Rosin <peda@axentia.se>
---
drivers/iio/inkern.c | 55 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/iio/consumer.h | 37 +++++++++++++++++++++++++++++
2 files changed, 92 insertions(+)
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index c4757e6367e7..efe418282cc3 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -746,3 +746,58 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
return ret;
}
EXPORT_SYMBOL_GPL(iio_write_channel_raw);
+
+int iio_get_channel_ext_info_count(struct iio_channel *chan)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+ unsigned int i = 0;
+
+ if (!chan->channel->ext_info)
+ return i;
+
+ for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++)
+ ++i;
+
+ return i;
+}
+EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);
+
+ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
+ const char *attr, char *buf)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ if (!chan->channel->ext_info)
+ return -EINVAL;
+
+ for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) {
+ if (strcmp(attr, ext_info->name))
+ continue;
+
+ return ext_info->read(chan->indio_dev, ext_info->private,
+ chan->channel, buf);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_ext_info);
+
+ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
+ const char *buf, size_t len)
+{
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ if (!chan->channel->ext_info)
+ return -EINVAL;
+
+ for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) {
+ if (strcmp(attr, ext_info->name))
+ continue;
+
+ return ext_info->write(chan->indio_dev, ext_info->private,
+ chan->channel, buf, len);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index 9edccfba1ffb..1e908ccb11f0 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -271,4 +271,41 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val,
int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
int *processed, unsigned int scale);
+/**
+ * iio_get_channel_ext_info_count() - get number of ext_info attributes
+ * connected to the channel.
+ * @chan: The channel being queried
+ *
+ * Returns the number of ext_info attributes
+ */
+int iio_get_channel_ext_info_count(struct iio_channel *chan);
+
+/**
+ * iio_read_channel_ext_info() - read ext_info attribute from a given channel
+ * @chan: The channel being queried.
+ * @attr: The ext_info attribute to read.
+ * @buf: Where to store the attribute value. Assumed to hold
+ * at least PAGE_SIZE bytes.
+ *
+ * Returns the number of bytes written to buf (perhaps w/o zero termination;
+ * it need not even be a string), or an error code.
+ */
+ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
+ const char *attr, char *buf);
+
+/**
+ * iio_write_channel_ext_info() - write ext_info attribute from a given channel
+ * @chan: The channel being queried.
+ * @attr: The ext_info attribute to read.
+ * @buf: The new attribute value. Strings needs to be zero-
+ * terminated, but the terminator should not be included
+ * in the below len.
+ * @len: The size of the new attribute value.
+ *
+ * Returns the number of accepted bytes, which should be the same as len.
+ * An error code can also be returned.
+ */
+ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
+ const char *buf, size_t len);
+
#endif
--
2.1.4
^ permalink raw reply related
* [PATCH v3 2/7] misc: minimal mux subsystem and gpio-based mux controller
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
Add a new minimalistic subsystem that handles multiplexer controllers.
When multiplexers are used in various places in the kernel, and the
same multiplexer controller can be used for several independent things,
there should be one place to implement support for said multiplexer
controller.
A single multiplexer controller can also be used to control several
parallel multiplexers, that are in turn used by different subsystems
in the kernel, leading to a need to coordinate multiplexer accesses.
The multiplexer subsystem handles this coordination.
This new mux controller subsystem comes with a single backend driver
that controls gpio based multiplexers.
Signed-off-by: Peter Rosin <peda@axentia.se>
---
Documentation/driver-model/devres.txt | 6 +-
MAINTAINERS | 2 +
drivers/misc/Kconfig | 23 +++
drivers/misc/Makefile | 2 +
drivers/misc/mux-core.c | 325 ++++++++++++++++++++++++++++++++++
drivers/misc/mux-gpio.c | 116 ++++++++++++
include/linux/mux.h | 55 ++++++
7 files changed, 528 insertions(+), 1 deletion(-)
create mode 100644 drivers/misc/mux-core.c
create mode 100644 drivers/misc/mux-gpio.c
create mode 100644 include/linux/mux.h
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index 167070895498..947bfcfac9ae 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -330,7 +330,11 @@ MEM
devm_kzalloc()
MFD
- devm_mfd_add_devices()
+ devm_mfd_add_devices()
+
+MUX
+ devm_mux_control_get()
+ devm_mux_control_put()
PCI
pcim_enable_device() : after success, all PCI ops become managed
diff --git a/MAINTAINERS b/MAINTAINERS
index 7d797f087822..37974aeee750 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8248,6 +8248,8 @@ MULTIPLEXER SUBSYSTEM
M: Peter Rosin <peda@axentia.se>
S: Maintained
F: Documentation/devicetree/bindings/misc/mux-*
+F: include/linux/mux.h
+F: drivers/misc/mux-*
MULTISOUND SOUND DRIVER
M: Andrew Veliath <andrewtv@usa.net>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 64971baf11fa..a3ca79e082c7 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -766,6 +766,29 @@ config PANEL_BOOT_MESSAGE
An empty message will only clear the display at driver init time. Any other
printf()-formatted message is valid with newline and escape codes.
+config MULTIPLEXER
+ tristate "Multiplexer subsystem"
+ help
+ Multiplexer controller subsystem. Multiplexers are used in a
+ variety of settings, and this subsystem abstracts their use
+ so that the rest of the kernel sees a common interface. When
+ multiple parallel multiplexers are controlled by one single
+ multiplexer controller, this subsystem also coordinates the
+ multiplexer accesses.
+
+if MULTIPLEXER
+
+config MUX_GPIO
+ tristate "GPIO-controlled MUX controller"
+ depends on OF && GPIOLIB
+ help
+ GPIO-controlled MUX controller.
+
+ To compile this driver as a module, choose M here: the module will
+ be called mux-gpio.
+
+endif
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 31983366090a..0befa2bba762 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,6 +53,8 @@ obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_PANEL) += panel.o
+obj-$(CONFIG_MULTIPLEXER) += mux-core.o
+obj-$(CONFIG_MUX_GPIO) += mux-gpio.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
diff --git a/drivers/misc/mux-core.c b/drivers/misc/mux-core.c
new file mode 100644
index 000000000000..5142caa19236
--- /dev/null
+++ b/drivers/misc/mux-core.c
@@ -0,0 +1,325 @@
+/*
+ * Multiplexer subsystem
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) "mux-core: " fmt
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mux.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+static struct bus_type mux_bus_type = {
+ .name = "mux",
+};
+
+static int __init mux_init(void)
+{
+ return bus_register(&mux_bus_type);
+}
+
+static void __exit mux_exit(void)
+{
+ bus_unregister(&mux_bus_type);
+}
+
+static DEFINE_IDA(mux_ida);
+
+static void mux_control_release(struct device *dev)
+{
+ struct mux_control *mux = to_mux_control(dev);
+
+ ida_simple_remove(&mux_ida, mux->id);
+ kfree(mux);
+}
+
+static struct device_type mux_control_type = {
+ .name = "mux-control",
+ .release = mux_control_release,
+};
+
+/**
+ * mux_control_alloc - allocate a mux-control
+ * @sizeof_priv: Size of extra memory area for private use by the caller.
+ *
+ * Returns the new mux-control.
+ */
+struct mux_control *mux_control_alloc(size_t sizeof_priv)
+{
+ struct mux_control *mux;
+
+ mux = kzalloc(sizeof(*mux) + sizeof_priv, GFP_KERNEL);
+ if (!mux)
+ return NULL;
+
+ mux->dev.bus = &mux_bus_type;
+ mux->dev.type = &mux_control_type;
+ device_initialize(&mux->dev);
+ dev_set_drvdata(&mux->dev, mux);
+
+ init_rwsem(&mux->lock);
+
+ mux->id = ida_simple_get(&mux_ida, 0, 0, GFP_KERNEL);
+ if (mux->id < 0) {
+ pr_err("mux-controlX failed to get device id\n");
+ kfree(mux);
+ return NULL;
+ }
+ dev_set_name(&mux->dev, "mux:control%d", mux->id);
+
+ mux->cached_state = -1;
+ mux->idle_state = -1;
+
+ return mux;
+}
+EXPORT_SYMBOL_GPL(mux_control_alloc);
+
+/**
+ * mux_control_register - register a mux-control, thus readying it for use
+ * @mux: The mux-control to register.
+ *
+ * Returns zero on success or a negative errno on error.
+ */
+int mux_control_register(struct mux_control *mux)
+{
+ int ret;
+
+ /* If the calling driver did not initialize of_node, do it here */
+ if (!mux->dev.of_node && mux->dev.parent)
+ mux->dev.of_node = mux->dev.parent->of_node;
+
+ ret = device_add(&mux->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = of_platform_populate(mux->dev.of_node, NULL, NULL, &mux->dev);
+ if (ret < 0)
+ device_del(&mux->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mux_control_register);
+
+/**
+ * mux_control_unregister - take the mux-control off-line, reversing the
+ * effexts of mux_control_register
+ * @mux: the mux-control to unregister.
+ */
+void mux_control_unregister(struct mux_control *mux)
+{
+ of_platform_depopulate(&mux->dev);
+ device_del(&mux->dev);
+}
+EXPORT_SYMBOL_GPL(mux_control_unregister);
+
+/**
+ * mux_control_put - put away the mux-control for good, reversing the
+ * effects of either mux_control_alloc or mux_control_get
+ * @mux: The mux-control to put away.
+ */
+void mux_control_put(struct mux_control *mux)
+{
+ if (!mux)
+ return;
+ put_device(&mux->dev);
+}
+EXPORT_SYMBOL_GPL(mux_control_put);
+
+static int mux_control_set(struct mux_control *mux, int state)
+{
+ int ret = mux->ops->set(mux, state);
+
+ mux->cached_state = ret < 0 ? -1 : state;
+
+ return ret;
+}
+
+/**
+ * mux_control_select - select the given multiplexer state
+ * @mux: The mux-control to request a change of state from.
+ * @state: The new requested state.
+ *
+ * Returns 0 if the requested state was already active, or 1 it the
+ * mux-control state was changed to the requested state. Or a negavive
+ * errno on error.
+ * Note that the difference in return value of zero or one is of
+ * questionable value; especially if the mux-control has several independent
+ * consumers, which is something the consumers should not be making
+ * assumptions about.
+ *
+ * Make sure to call mux_control_deselect when the operation is complete and
+ * the mux-control is free for others to use, but do not call
+ * mux_control_deselect if mux_control_select fails.
+ */
+int mux_control_select(struct mux_control *mux, int state)
+{
+ int ret;
+
+ if (down_read_trylock(&mux->lock)) {
+ if (mux->cached_state == state)
+ return 0;
+
+ /* Sigh, the mux needs updating... */
+ up_read(&mux->lock);
+ }
+
+ /* ...or it's just contended. */
+ down_write(&mux->lock);
+
+ if (mux->cached_state == state) {
+ /*
+ * Hmmm, someone else changed the mux to my liking.
+ * That makes me wonder how long I waited for nothing?
+ */
+ downgrade_write(&mux->lock);
+ return 0;
+ }
+
+ ret = mux_control_set(mux, state);
+ if (ret < 0) {
+ if (mux->idle_state != -1)
+ mux_control_set(mux, mux->idle_state);
+
+ up_write(&mux->lock);
+ return ret;
+ }
+
+ downgrade_write(&mux->lock);
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(mux_control_select);
+
+/**
+ * mux_control_deselect - deselect the previously selected multiplexer state
+ * @mux: The mux-control to deselect.
+ *
+ * Returns 0 on success and a negative errno on error. An error can only
+ * occur if the mux has an idle state. Note that even if an error occurs, the
+ * mux-control is unlocked for others to access.
+ */
+int mux_control_deselect(struct mux_control *mux)
+{
+ int ret = 0;
+
+ if (mux->idle_state != -1 && mux->cached_state != mux->idle_state)
+ ret = mux_control_set(mux, mux->idle_state);
+
+ up_read(&mux->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mux_control_deselect);
+
+static int of_dev_node_match(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static struct mux_control *of_find_mux_by_node(struct device_node *np)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&mux_bus_type, NULL, np, of_dev_node_match);
+
+ return dev ? to_mux_control(dev) : NULL;
+}
+
+/**
+ * mux_control_get - get the mux-control for a device
+ * @dev: The device that needs a mux-control.
+ *
+ * Returns the mux-control.
+ */
+struct mux_control *mux_control_get(struct device *dev)
+{
+ struct mux_control *mux;
+
+ if (!dev->of_node)
+ return ERR_PTR(-ENODEV);
+
+ mux = of_find_mux_by_node(dev->of_node->parent);
+ if (!mux)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return mux;
+}
+EXPORT_SYMBOL_GPL(mux_control_get);
+
+static void devm_mux_control_free(struct device *dev, void *res)
+{
+ struct mux_control *mux = *(struct mux_control **)res;
+
+ mux_control_put(mux);
+}
+
+/**
+ * devm_mux_control_get - get the mux-control for a device, with resource
+ * management
+ * @dev: The device that needs a mux-control.
+ *
+ * Returns the mux-control.
+ */
+struct mux_control *devm_mux_control_get(struct device *dev)
+{
+ struct mux_control **ptr, *mux;
+
+ ptr = devres_alloc(devm_mux_control_free, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ mux = mux_control_get(dev);
+ if (IS_ERR(mux)) {
+ devres_free(ptr);
+ return mux;
+ }
+
+ *ptr = mux;
+ devres_add(dev, ptr);
+
+ return mux;
+}
+EXPORT_SYMBOL_GPL(devm_mux_control_get);
+
+static int devm_mux_control_match(struct device *dev, void *res, void *data)
+{
+ struct mux_control **r = res;
+
+ if (!r || !*r) {
+ WARN_ON(!r || !*r);
+ return 0;
+ }
+
+ return *r == data;
+}
+
+/**
+ * devm_mux_control_put - resource-managed version mux_control_put
+ * @dev: The device that originally got the mux-control.
+ * @mux: The mux-control to put away.
+ *
+ * Note that you do not normally need to call this function.
+ */
+void devm_mux_control_put(struct device *dev, struct mux_control *mux)
+{
+ WARN_ON(devres_release(dev, devm_mux_control_free,
+ devm_mux_control_match, mux));
+}
+EXPORT_SYMBOL_GPL(devm_mux_control_put);
+
+subsys_initcall(mux_init);
+module_exit(mux_exit);
+
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se");
+MODULE_DESCRIPTION("MUX subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/mux-gpio.c b/drivers/misc/mux-gpio.c
new file mode 100644
index 000000000000..e9baca35f400
--- /dev/null
+++ b/drivers/misc/mux-gpio.c
@@ -0,0 +1,116 @@
+/*
+ * GPIO-controlled multiplexer driver
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mux.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+struct mux_gpio {
+ struct gpio_descs *gpios;
+};
+
+static int mux_gpio_set(struct mux_control *mux, int val)
+{
+ struct mux_gpio *mux_gpio = mux_control_priv(mux);
+ int i;
+
+ for (i = 0; i < mux_gpio->gpios->ndescs; i++)
+ gpiod_set_value_cansleep(mux_gpio->gpios->desc[i],
+ val & (1 << i));
+
+ return 0;
+}
+
+static const struct mux_control_ops mux_gpio_ops = {
+ .set = mux_gpio_set,
+};
+
+static const struct of_device_id mux_gpio_dt_ids[] = {
+ { .compatible = "mux-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids);
+
+static int mux_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct mux_control *mux;
+ struct mux_gpio *mux_gpio;
+ u32 idle_state;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ mux = mux_control_alloc(sizeof(*mux_gpio));
+ if (!mux)
+ return -ENOMEM;
+ mux_gpio = mux_control_priv(mux);
+ mux->dev.parent = dev;
+ mux->ops = &mux_gpio_ops;
+
+ platform_set_drvdata(pdev, mux);
+
+ mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
+ if (IS_ERR(mux_gpio->gpios)) {
+ if (PTR_ERR(mux_gpio->gpios) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get gpios\n");
+ mux_control_put(mux);
+ return PTR_ERR(mux_gpio->gpios);
+ }
+ mux->states = 1 << mux_gpio->gpios->ndescs;
+
+ ret = of_property_read_u32(np, "idle-state", &idle_state);
+ if (ret >= 0) {
+ if (idle_state >= mux->states) {
+ dev_err(dev, "invalid idle-state %u\n", idle_state);
+ return -EINVAL;
+ }
+ mux->idle_state = idle_state;
+ }
+
+ ret = mux_control_register(mux);
+ if (ret < 0) {
+ dev_err(dev, "failed to register mux_control\n");
+ mux_control_put(mux);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mux_gpio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mux_control *mux = to_mux_control(dev);
+
+ mux_control_unregister(mux);
+ mux_control_put(mux);
+ return 0;
+}
+
+static struct platform_driver mux_gpio_driver = {
+ .driver = {
+ .name = "mux-gpio",
+ .of_match_table = of_match_ptr(mux_gpio_dt_ids),
+ },
+ .probe = mux_gpio_probe,
+ .remove = mux_gpio_remove,
+};
+module_platform_driver(mux_gpio_driver);
+
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se");
+MODULE_DESCRIPTION("GPIO-controlled multiplexer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mux.h b/include/linux/mux.h
new file mode 100644
index 000000000000..2f1472192d6d
--- /dev/null
+++ b/include/linux/mux.h
@@ -0,0 +1,55 @@
+/*
+ * mux.h - definitions for the multiplexer interface
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_MUX_H
+#define _LINUX_MUX_H
+
+#include <linux/device.h>
+#include <linux/rwsem.h>
+
+struct mux_control;
+
+struct mux_control_ops {
+ int (*set)(struct mux_control *mux, int reg);
+};
+
+struct mux_control {
+ struct rw_semaphore lock; /* protects the state of the mux */
+
+ struct device dev;
+ int id;
+
+ unsigned int states;
+ int cached_state;
+ int idle_state;
+
+ const struct mux_control_ops *ops;
+};
+
+#define to_mux_control(x) container_of((x), struct mux_control, dev)
+
+static inline void *mux_control_priv(struct mux_control *mux)
+{
+ return mux + 1;
+}
+
+struct mux_control *mux_control_alloc(size_t sizeof_priv);
+int mux_control_register(struct mux_control *mux);
+void mux_control_unregister(struct mux_control *mux);
+void mux_control_put(struct mux_control *mux);
+
+int mux_control_select(struct mux_control *mux, int state);
+int mux_control_deselect(struct mux_control *mux);
+
+struct mux_control *mux_control_get(struct device *dev);
+struct mux_control *devm_mux_control_get(struct device *dev);
+void devm_mux_control_put(struct device *dev, struct mux_control *mux);
+
+#endif /* _LINUX_MUX_H */
--
2.1.4
^ permalink raw reply related
* [PATCH v3 1/7] dt-bindings: document devicetree bindings for mux-controllers and mux-gpio
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
In-Reply-To: <1479734235-18837-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../devicetree/bindings/misc/mux-controller.txt | 76 +++++++++++++++++++++
.../devicetree/bindings/misc/mux-gpio.txt | 78 ++++++++++++++++++++++
MAINTAINERS | 5 ++
3 files changed, 159 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/mux-controller.txt
create mode 100644 Documentation/devicetree/bindings/misc/mux-gpio.txt
diff --git a/Documentation/devicetree/bindings/misc/mux-controller.txt b/Documentation/devicetree/bindings/misc/mux-controller.txt
new file mode 100644
index 000000000000..6946b5428546
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/mux-controller.txt
@@ -0,0 +1,76 @@
+Common multiplexer controller bindings
+
+A mux controller will have one, or several, child devices that uses (or
+consumes) a mux controller. A mux controller can possibly control several
+parallel multiplexers. The node for a mux controller will have one child
+node for each multiplexer controller consumer.
+
+A mux controller provides a number of states to its consumers, and the
+state space is a simple zero-based enumeration. I.e. 0-1 for a 2-way
+multiplexer, 0-7 for an 8-way multiplexer, etc.
+
+Example:
+
+ /*
+ * Two consumers (one for an ADC line and one for an i2c bus) of
+ * parallel 4-way multiplexers controlled by the same two GPIO-lines.
+ */
+ mux {
+ compatible = "mux-gpio";
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+
+ adc {
+ compatible = "iio-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sync-1@0 {
+ reg = <0>;
+ };
+
+ in@1 {
+ reg = <1>;
+ };
+
+ out@2 {
+ reg = <2>;
+ };
+
+ sync-2@3 {
+ reg = <3>;
+ };
+ };
+
+ i2c-mux {
+ compatible = "i2c-mux-simple,mux-locked";
+ i2c-parent = <&i2c1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ssd1307: oled@3c {
+ /* ... */
+ };
+ };
+
+ i2c@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pca9555: pca9555@20 {
+ /* ... */
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/misc/mux-gpio.txt b/Documentation/devicetree/bindings/misc/mux-gpio.txt
new file mode 100644
index 000000000000..23b87913f4b3
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/mux-gpio.txt
@@ -0,0 +1,78 @@
+GPIO-based multiplexer controller bindings
+
+Define what GPIO pins are used to control a multiplexer. Or several
+multiplexers, if the same pins control more than one multiplexer.
+
+Required properties:
+- compatible : "mux-gpio"
+- mux-gpios : list of gpios used to control the multiplexer, least
+ significant bit first.
+* Standard mux-controller bindings as decribed in mux-controller.txt
+
+Optional properties:
+- idle-state : if present, the state the mux will have when idle.
+
+The multiplexer state is defined as the number represented by the
+multiplexer GPIO pins, where the first pin is the least significant
+bit. And active pin is a binary 1, an inactive pin is a binary 0.
+
+Example:
+ mux {
+ compatible = "mux-gpio";
+
+ mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
+ <&pioA 1 GPIO_ACTIVE_HIGH>;
+
+ adc {
+ compatible = "iio-mux";
+ io-channels = <&adc 0>;
+ io-channel-names = "parent";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sync-1@0 {
+ reg = <0>;
+ };
+
+ in@1 {
+ reg = <1>;
+ };
+
+ out@2 {
+ reg = <2>;
+ };
+
+ sync-2@3 {
+ reg = <3>;
+ };
+ };
+
+ i2c-mux {
+ compatible = "i2c-mux-simple,mux-locked";
+ i2c-parent = <&i2c1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ssd1307: oled@3c {
+ /* ... */
+ };
+ };
+
+ i2c@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pca9555: pca9555@20 {
+ /* ... */
+ };
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index ad9b965e5e44..7d797f087822 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8244,6 +8244,11 @@ S: Orphan
F: drivers/mmc/host/mmc_spi.c
F: include/linux/spi/mmc_spi.h
+MULTIPLEXER SUBSYSTEM
+M: Peter Rosin <peda@axentia.se>
+S: Maintained
+F: Documentation/devicetree/bindings/misc/mux-*
+
MULTISOUND SOUND DRIVER
M: Andrew Veliath <andrewtv@usa.net>
S: Maintained
--
2.1.4
^ permalink raw reply related
* [PATCH v3 0/7] mux controller abstraction and iio/i2c muxes
From: Peter Rosin @ 2016-11-21 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Wolfram Sang, Rob Herring, Mark Rutland,
Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Jonathan Corbet, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c, devicetree, linux-iio, linux-doc
Hi!
The code depends on the _available work in iio which can
be found in linux-next.
v2 -> v3 changes
- have the mux-controller in the parent node of any mux-controller consumer,
to hopefully satisfy complaint from Rob about dt complexity.
- improve commit message of the mux subsystem commit, making it more
general, as requested by Jonathan.
- remove priv member from struct mux_control and calculate it on the
fly. /Jonathan.
- make the function comments in mux-core.c kernel doc. /Jonathan
- add devm_mux_control_* to Documentation/driver.model/devres.txt. /Jonathan
- add common dt bindings for mux-controllers, refer to them from the
mux-gpio bindings.
- clarify how the gpio pins map to the mux state.
- separate CONFIG_ variables for the mux core and the mux gpio driver.
- improve Kconfig help texts.
- make CONFIG_MUX_GPIO depend on CONFIG_GPIOLIB.
- keep track of the number of mux states in the mux core.
- since the iio channel number is used as mux state, it was possible
to drop the state member from the mux_child struct.
- cleanup dt bindings for i2c-mux-simple, it had some of copy-paste
problems from ots origin (i2c-mux-gpio).
- select the mux control subsystem in config for the i2c-mux-simple driver.
- add entries to MAINTAINERS and my sign-off, I'm now satisfied and know
nothing in this to be ashamed of.
v1 -> v2 changes
- fixup export of mux_control_put reported by kbuild
- drop devicetree iio-ext-info property as noted by Lars-Peter,
and replace the functionality by exposing all ext_info
attributes of the parent channel for each of the muxed
channels. A cache on top of that and each muxed channel
gets its own view of the ext_info of the parent channel.
- implement idle-state for muxes
- clear out the cache on failure in order to force a mux
update on the following use
- cleanup the probe of i2c-mux-simple driver
- fix a bug in the i2c-mux-simple driver, where failure in
the selection of the mux caused a deadlock when the mux
was later unconditionally deselected.
I have a piece of hardware that is using the same 3 GPIO pins
to control four 8-way muxes. Three of them control ADC lines
to an ADS1015 chip with an iio driver, and the last one
controls the SDA line of an i2c bus. We have some deployed
code to handle this, but you do not want to see it or ever
hear about it. I'm not sure why I even mention it. Anyway,
the situation has nagged me to no end for quite some time.
So, after first getting more intimate with the i2c muxing code
and later discovering the drivers/iio/inkern.c file and
writing a couple of drivers making use of it, I came up with
what I think is an acceptable solution; add a generic mux
controller driver (and subsystem) that is shared between all
instances, and combine that with an iio mux driver and a new
generic i2c mux driver. The new i2c mux I called "simple"
since it is only hooking the i2c muxing and the new mux
controller (much like the alsa simple card driver does for ASoC).
My initial (private) version didn't add "mux" as a new bus,
but I couldn't get decent type-checking and nice devicetree
integration with that. It does however feel a bit rich to
add a new bus for something as small as mux controllers?
One thing that I would like to do, but don't see a solution
for, is to move the mux control code that is present in
various drivers in drivers/i2c/muxes to this new minimalistic
muxing subsystem, thus converting all present i2c muxes (but
perhaps not gates and arbitrators) to be i2c-mux-simple muxes.
I'm using an rwsem to lock a mux, but that isn't really a
perfect fit. Is there a better locking primitive that I don't
know about that fits better? I had a mutex at one point, but
that didn't allow any concurrent accesses at all. At least
the rwsem allows concurrent access as long as all users
agree on the mux state, but I suspect that the rwsem will
degrade to the mutex situation pretty quickly if there is
any contention.
Also, the "mux" name feels a bit ambitious, there are many muxes
in the world, and this tiny bit of code is probably not good
enough to be a nice fit for all...
Cheers,
Peter
Peter Rosin (7):
dt-bindings: document devicetree bindings for mux-controllers and
mux-gpio
misc: minimal mux subsystem and gpio-based mux controller
iio: inkern: api for manipulating ext_info of iio channels
dt-bindings: iio: iio-mux: document iio-mux bindings
iio: multiplexer: new iio category and iio-mux driver
dt-bindings: i2c: i2c-mux-simple: document i2c-mux-simple bindings
i2c: i2c-mux-simple: new driver
.../devicetree/bindings/i2c/i2c-mux-simple.txt | 76 ++++
.../bindings/iio/multiplexer/iio-mux.txt | 47 +++
.../devicetree/bindings/misc/mux-controller.txt | 76 ++++
.../devicetree/bindings/misc/mux-gpio.txt | 78 ++++
Documentation/driver-model/devres.txt | 6 +-
MAINTAINERS | 14 +
drivers/i2c/muxes/Kconfig | 13 +
drivers/i2c/muxes/Makefile | 1 +
drivers/i2c/muxes/i2c-mux-simple.c | 177 ++++++++
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/inkern.c | 55 +++
drivers/iio/multiplexer/Kconfig | 18 +
drivers/iio/multiplexer/Makefile | 6 +
drivers/iio/multiplexer/iio-mux.c | 450 +++++++++++++++++++++
drivers/misc/Kconfig | 23 ++
drivers/misc/Makefile | 2 +
drivers/misc/mux-core.c | 325 +++++++++++++++
drivers/misc/mux-gpio.c | 116 ++++++
include/linux/iio/consumer.h | 37 ++
include/linux/mux.h | 55 +++
21 files changed, 1576 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-simple.txt
create mode 100644 Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
create mode 100644 Documentation/devicetree/bindings/misc/mux-controller.txt
create mode 100644 Documentation/devicetree/bindings/misc/mux-gpio.txt
create mode 100644 drivers/i2c/muxes/i2c-mux-simple.c
create mode 100644 drivers/iio/multiplexer/Kconfig
create mode 100644 drivers/iio/multiplexer/Makefile
create mode 100644 drivers/iio/multiplexer/iio-mux.c
create mode 100644 drivers/misc/mux-core.c
create mode 100644 drivers/misc/mux-gpio.c
create mode 100644 include/linux/mux.h
--
2.1.4
^ permalink raw reply
* [PATCH] dt-bindings: mfd: Improve readability for TPS65217 interrupt sources
From: Milo Kim @ 2016-11-21 13:15 UTC (permalink / raw)
To: bcousson, Tony Lindgren
Cc: Lee Jones, linux-omap, devicetree, linux-arm-kernel, linux-kernel,
Robert Nelson, Milo Kim
AC and USB interrupts are related with external power input.
PB interrupt means push button pressed or released event.
Use better human readable definitions.
Signed-off-by: Milo Kim <woogyom.kim@gmail.com>
---
arch/arm/boot/dts/am335x-bone-common.dtsi | 4 ++--
include/dt-bindings/mfd/tps65217.h | 6 +++---
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/arm/boot/dts/am335x-bone-common.dtsi b/arch/arm/boot/dts/am335x-bone-common.dtsi
index dc561d5..1848d58 100644
--- a/arch/arm/boot/dts/am335x-bone-common.dtsi
+++ b/arch/arm/boot/dts/am335x-bone-common.dtsi
@@ -319,13 +319,13 @@
ti,pmic-shutdown-controller;
charger {
- interrupts = <TPS65217_IRQ_AC>, <TPS65217_IRQ_USB>;
+ interrupts = <TPS65217_IRQ_AC_POWER>, <TPS65217_IRQ_USB_POWER>;
interrupts-names = "AC", "USB";
status = "okay";
};
pwrbutton {
- interrupts = <TPS65217_IRQ_PB>;
+ interrupts = <TPS65217_IRQ_PUSHBUTTON>;
status = "okay";
};
diff --git a/include/dt-bindings/mfd/tps65217.h b/include/dt-bindings/mfd/tps65217.h
index cafb9e6..0293fdd 100644
--- a/include/dt-bindings/mfd/tps65217.h
+++ b/include/dt-bindings/mfd/tps65217.h
@@ -19,8 +19,8 @@
#ifndef __DT_BINDINGS_TPS65217_H__
#define __DT_BINDINGS_TPS65217_H__
-#define TPS65217_IRQ_USB 0
-#define TPS65217_IRQ_AC 1
-#define TPS65217_IRQ_PB 2
+#define TPS65217_IRQ_USB_POWER 0 /* USB power state change */
+#define TPS65217_IRQ_AC_POWER 1 /* AC power state change */
+#define TPS65217_IRQ_PUSHBUTTON 2 /* Push button state change */
#endif
--
2.9.3
^ permalink raw reply related
* Re: [PATCH v2 1/2] regulator: max77620: add support to configure MPOK
From: Lee Jones @ 2016-11-21 13:11 UTC (permalink / raw)
To: Venkat Reddy Talla
Cc: lgirdwood, broonie, robh+dt, mark.rutland, devicetree,
linux-kernel, ldewangan, svelpula
In-Reply-To: <20161121130948.GC3917@dell>
On Mon, 21 Nov 2016, Lee Jones wrote:
> On Thu, 17 Nov 2016, Venkat Reddy Talla wrote:
>
> > Adding support to configure regulator POK mapping bit
> > to control nRST_IO and GPIO1 POK function.
> > In tegra based platform which uses MAX20024 pmic, when
> > some of regulators are configured FPS_NONE(flexible power sequencer)
> > causes PMIC GPIO1 to go low which lead to various other rails turning off,
> > to avoid this MPOK bit of those regulators need to be set to 0
> > so that PMIC GPIO1 will not go low.
> >
> > Signed-off-by: Venkat Reddy Talla <vreddytalla@nvidia.com>
> >
> > ---
> > changes in v2:
> > - updated commit message for the patch
> > - address review comments
> > ---
> > drivers/regulator/max77620-regulator.c | 46 ++++++++++++++++++++++++++++++++++
> > include/linux/mfd/max77620.h | 2 ++
>
> For my own reference:
> Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
Mark if you want to take this patch, feel free.
> > 2 files changed, 48 insertions(+)
> >
> > diff --git a/drivers/regulator/max77620-regulator.c b/drivers/regulator/max77620-regulator.c
> > index a1b49a6..850b14c 100644
> > --- a/drivers/regulator/max77620-regulator.c
> > +++ b/drivers/regulator/max77620-regulator.c
> > @@ -81,6 +81,7 @@ struct max77620_regulator_pdata {
> > int suspend_fps_pd_slot;
> > int suspend_fps_pu_slot;
> > int current_mode;
> > + int power_ok;
> > int ramp_rate_setting;
> > };
> >
> > @@ -351,11 +352,48 @@ static int max77620_set_slew_rate(struct max77620_regulator *pmic, int id,
> > return 0;
> > }
> >
> > +static int max77620_config_power_ok(struct max77620_regulator *pmic, int id)
> > +{
> > + struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id];
> > + struct max77620_regulator_info *rinfo = pmic->rinfo[id];
> > + struct max77620_chip *chip = dev_get_drvdata(pmic->dev->parent);
> > + u8 val, mask;
> > + int ret;
> > +
> > + switch (chip->chip_id) {
> > + case MAX20024:
> > + if (rpdata->power_ok >= 0) {
> > + if (rinfo->type == MAX77620_REGULATOR_TYPE_SD)
> > + mask = MAX20024_SD_CFG1_MPOK_MASK;
> > + else
> > + mask = MAX20024_LDO_CFG2_MPOK_MASK;
> > +
> > + val = rpdata->power_ok ? mask : 0;
> > +
> > + ret = regmap_update_bits(pmic->rmap, rinfo->cfg_addr,
> > + mask, val);
> > + if (ret < 0) {
> > + dev_err(pmic->dev, "Reg 0x%02x update failed %d\n",
> > + rinfo->cfg_addr, ret);
> > + return ret;
> > + }
> > + }
> > + break;
> > +
> > + default:
> > + break;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > static int max77620_init_pmic(struct max77620_regulator *pmic, int id)
> > {
> > struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id];
> > int ret;
> >
> > + max77620_config_power_ok(pmic, id);
> > +
> > /* Update power mode */
> > ret = max77620_regulator_get_power_mode(pmic, id);
> > if (ret < 0)
> > @@ -595,6 +633,12 @@ static int max77620_of_parse_cb(struct device_node *np,
> > np, "maxim,suspend-fps-power-down-slot", &pval);
> > rpdata->suspend_fps_pd_slot = (!ret) ? pval : -1;
> >
> > + ret = of_property_read_u32(np, "maxim,power-ok-control", &pval);
> > + if (!ret)
> > + rpdata->power_ok = pval;
> > + else
> > + rpdata->power_ok = -1;
> > +
> > ret = of_property_read_u32(np, "maxim,ramp-rate-setting", &pval);
> > rpdata->ramp_rate_setting = (!ret) ? pval : 0;
> >
> > @@ -807,6 +851,8 @@ static int max77620_regulator_resume(struct device *dev)
> > for (id = 0; id < MAX77620_NUM_REGS; id++) {
> > reg_pdata = &pmic->reg_pdata[id];
> >
> > + max77620_config_power_ok(pmic, id);
> > +
> > max77620_regulator_set_fps_slots(pmic, id, false);
> > if (reg_pdata->active_fps_src < 0)
> > continue;
> > diff --git a/include/linux/mfd/max77620.h b/include/linux/mfd/max77620.h
> > index 3ca0af07..ad2a9a8 100644
> > --- a/include/linux/mfd/max77620.h
> > +++ b/include/linux/mfd/max77620.h
> > @@ -180,6 +180,7 @@
> > #define MAX77620_SD_CFG1_FPWM_SD_MASK BIT(2)
> > #define MAX77620_SD_CFG1_FPWM_SD_SKIP 0
> > #define MAX77620_SD_CFG1_FPWM_SD_FPWM BIT(2)
> > +#define MAX20024_SD_CFG1_MPOK_MASK BIT(1)
> > #define MAX77620_SD_CFG1_FSRADE_SD_MASK BIT(0)
> > #define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0
> > #define MAX77620_SD_CFG1_FSRADE_SD_ENABLE BIT(0)
> > @@ -187,6 +188,7 @@
> > /* LDO_CNFG2 */
> > #define MAX77620_LDO_POWER_MODE_MASK 0xC0
> > #define MAX77620_LDO_POWER_MODE_SHIFT 6
> > +#define MAX20024_LDO_CFG2_MPOK_MASK BIT(2)
> > #define MAX77620_LDO_CFG2_ADE_MASK BIT(1)
> > #define MAX77620_LDO_CFG2_ADE_DISABLE 0
> > #define MAX77620_LDO_CFG2_ADE_ENABLE BIT(1)
>
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH] PM / Domains: Fix compatible for domain idle state
From: Rafael J. Wysocki @ 2016-11-21 13:11 UTC (permalink / raw)
To: Brendan Jackman
Cc: Lina Iyer, Rafael Wysocki, Ulf Hansson, Kevin Hilman, Linux PM,
linux-arm-kernel@lists.infradead.org, Andy Gross, Stephen Boyd,
linux-arm-msm, Lorenzo Pieralisi, Sudeep Holla, Juri Lelli,
devicetree@vger.kernel.org, Rob Herring
In-Reply-To: <87zikt6mc1.fsf@arm.com>
On Mon, Nov 21, 2016 at 1:37 PM, Brendan Jackman
<brendan.jackman@arm.com> wrote:
> Hi,
>
> On Thu, Nov 03 2016 at 21:54, Lina Iyer <lina.iyer@linaro.org> wrote:
>> Re-using idle state definition provided by arm,idle-state for domain
>> idle states creates a lot of confusion and limits further evolution of
>> the domain idle definition. To keep things clear and simple, define a
>> idle states for domain using a new compatible "domain-idle-state".
>>
>> Fix existing PM domains code to look for the newly defined compatible.
>
> This looks good to me, pinging for review from others/queue for merge.
Well, I need an ACK from at least one DT bindings maintainer so that I
can queue it up.
>> Cc: <devicetree@vger.kernel.org>
>> Cc: Rob Herring <robh@kernel.org>
>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> ---
>> .../bindings/power/domain-idle-state.txt | 33 ++++++++++++++++++++++
>> .../devicetree/bindings/power/power_domain.txt | 8 +++---
>> drivers/base/power/domain.c | 2 +-
>> 3 files changed, 38 insertions(+), 5 deletions(-)
>> create mode 100644 Documentation/devicetree/bindings/power/domain-idle-state.txt
>>
>> diff --git a/Documentation/devicetree/bindings/power/domain-idle-state.txt b/Documentation/devicetree/bindings/power/domain-idle-state.txt
>> new file mode 100644
>> index 0000000..eefc7ed
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/power/domain-idle-state.txt
>> @@ -0,0 +1,33 @@
>> +PM Domain Idle State Node:
>> +
>> +A domain idle state node represents the state parameters that will be used to
>> +select the state when there are no active components in the domain.
>> +
>> +The state node has the following parameters -
>> +
>> +- compatible:
>> + Usage: Required
>> + Value type: <string>
>> + Definition: Must be "domain-idle-state".
>> +
>> +- entry-latency-us
>> + Usage: Required
>> + Value type: <prop-encoded-array>
>> + Definition: u32 value representing worst case latency in
>> + microseconds required to enter the idle state.
>> + The exit-latency-us duration may be guaranteed
>> + only after entry-latency-us has passed.
>> +
>> +- exit-latency-us
>> + Usage: Required
>> + Value type: <prop-encoded-array>
>> + Definition: u32 value representing worst case latency
>> + in microseconds required to exit the idle state.
>> +
>> +- min-residency-us
>> + Usage: Required
>> + Value type: <prop-encoded-array>
>> + Definition: u32 value representing minimum residency duration
>> + in microseconds after which the idle state will yield
>> + power benefits after overcoming the overhead in entering
>> +i the idle state.
>> diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
>> index e165036..723e1ad 100644
>> --- a/Documentation/devicetree/bindings/power/power_domain.txt
>> +++ b/Documentation/devicetree/bindings/power/power_domain.txt
>> @@ -31,7 +31,7 @@ Optional properties:
>>
>> - domain-idle-states : A phandle of an idle-state that shall be soaked into a
>> generic domain power state. The idle state definitions are
>> - compatible with arm,idle-state specified in [1].
>> + compatible with domain-idle-state specified in [1].
>> The domain-idle-state property reflects the idle state of this PM domain and
>> not the idle states of the devices or sub-domains in the PM domain. Devices
>> and sub-domains have their own idle-states independent of the parent
>> @@ -85,7 +85,7 @@ Example 3:
>> };
>>
>> DOMAIN_RET: state@0 {
>> - compatible = "arm,idle-state";
>> + compatible = "domain-idle-state";
>> reg = <0x0>;
>> entry-latency-us = <1000>;
>> exit-latency-us = <2000>;
>> @@ -93,7 +93,7 @@ Example 3:
>> };
>>
>> DOMAIN_PWR_DN: state@1 {
>> - compatible = "arm,idle-state";
>> + compatible = "domain-idle-state";
>> reg = <0x1>;
>> entry-latency-us = <5000>;
>> exit-latency-us = <8000>;
>> @@ -118,4 +118,4 @@ The node above defines a typical PM domain consumer device, which is located
>> inside a PM domain with index 0 of a power controller represented by a node
>> with the label "power".
>>
>> -[1]. Documentation/devicetree/bindings/arm/idle-states.txt
>> +[1]. Documentation/devicetree/bindings/power/domain-idle-state.txt
>> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
>> index 661737c..f0bc672 100644
>> --- a/drivers/base/power/domain.c
>> +++ b/drivers/base/power/domain.c
>> @@ -2048,7 +2048,7 @@ int genpd_dev_pm_attach(struct device *dev)
>> EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
>>
>> static const struct of_device_id idle_state_match[] = {
>> - { .compatible = "arm,idle-state", },
>> + { .compatible = "domain-idle-state", },
>> { }
>> };
> --
Thanks,
Rafael
^ permalink raw reply
* Re: [PATCH v2 2/2] dt-bindings: max77620: add documentation for MPOK property
From: Lee Jones @ 2016-11-21 13:10 UTC (permalink / raw)
To: Venkat Reddy Talla
Cc: lgirdwood, broonie, robh+dt, mark.rutland, devicetree,
linux-kernel, ldewangan, svelpula
In-Reply-To: <1479405276-26452-2-git-send-email-vreddytalla@nvidia.com>
On Thu, 17 Nov 2016, Venkat Reddy Talla wrote:
> Adding documentation for maxim,power-ok-control dts property
>
> Signed-off-by: Venkat Reddy Talla <vreddytalla@nvidia.com>
>
> ---
> Changes from V1:
> None
> ---
> Documentation/devicetree/bindings/mfd/max77620.txt | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
For my own reference:
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
> diff --git a/Documentation/devicetree/bindings/mfd/max77620.txt b/Documentation/devicetree/bindings/mfd/max77620.txt
> index 2ad44f7..9c16d51 100644
> --- a/Documentation/devicetree/bindings/mfd/max77620.txt
> +++ b/Documentation/devicetree/bindings/mfd/max77620.txt
> @@ -106,6 +106,18 @@ Here supported time periods by device in microseconds are as follows:
> MAX77620 supports 40, 80, 160, 320, 640, 1280, 2560 and 5120 microseconds.
> MAX20024 supports 20, 40, 80, 160, 320, 640, 1280 and 2540 microseconds.
>
> +-maxim,power-ok-control: configure map power ok bit
> + 1: Enables POK(Power OK) to control nRST_IO and GPIO1
> + POK function.
> + 0: Disables POK control.
> + if property missing, do not configure MPOK bit.
> + If POK mapping is enabled for GPIO1/nRST_IO then,
> + GPIO1/nRST_IO pins are HIGH only if all rails
> + that have POK control enabled are HIGH.
> + If any of the rails goes down(which are enabled for POK
> + control) then, GPIO1/nRST_IO goes LOW.
> + this property is valid for max20024 only.
> +
> For DT binding details of different sub modules like GPIO, pincontrol,
> regulator, power, please refer respective device-tree binding document
> under their respective sub-system directories.
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH v2 1/2] regulator: max77620: add support to configure MPOK
From: Lee Jones @ 2016-11-21 13:09 UTC (permalink / raw)
To: Venkat Reddy Talla
Cc: lgirdwood, broonie, robh+dt, mark.rutland, devicetree,
linux-kernel, ldewangan, svelpula
In-Reply-To: <1479405276-26452-1-git-send-email-vreddytalla@nvidia.com>
On Thu, 17 Nov 2016, Venkat Reddy Talla wrote:
> Adding support to configure regulator POK mapping bit
> to control nRST_IO and GPIO1 POK function.
> In tegra based platform which uses MAX20024 pmic, when
> some of regulators are configured FPS_NONE(flexible power sequencer)
> causes PMIC GPIO1 to go low which lead to various other rails turning off,
> to avoid this MPOK bit of those regulators need to be set to 0
> so that PMIC GPIO1 will not go low.
>
> Signed-off-by: Venkat Reddy Talla <vreddytalla@nvidia.com>
>
> ---
> changes in v2:
> - updated commit message for the patch
> - address review comments
> ---
> drivers/regulator/max77620-regulator.c | 46 ++++++++++++++++++++++++++++++++++
> include/linux/mfd/max77620.h | 2 ++
For my own reference:
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
> 2 files changed, 48 insertions(+)
>
> diff --git a/drivers/regulator/max77620-regulator.c b/drivers/regulator/max77620-regulator.c
> index a1b49a6..850b14c 100644
> --- a/drivers/regulator/max77620-regulator.c
> +++ b/drivers/regulator/max77620-regulator.c
> @@ -81,6 +81,7 @@ struct max77620_regulator_pdata {
> int suspend_fps_pd_slot;
> int suspend_fps_pu_slot;
> int current_mode;
> + int power_ok;
> int ramp_rate_setting;
> };
>
> @@ -351,11 +352,48 @@ static int max77620_set_slew_rate(struct max77620_regulator *pmic, int id,
> return 0;
> }
>
> +static int max77620_config_power_ok(struct max77620_regulator *pmic, int id)
> +{
> + struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id];
> + struct max77620_regulator_info *rinfo = pmic->rinfo[id];
> + struct max77620_chip *chip = dev_get_drvdata(pmic->dev->parent);
> + u8 val, mask;
> + int ret;
> +
> + switch (chip->chip_id) {
> + case MAX20024:
> + if (rpdata->power_ok >= 0) {
> + if (rinfo->type == MAX77620_REGULATOR_TYPE_SD)
> + mask = MAX20024_SD_CFG1_MPOK_MASK;
> + else
> + mask = MAX20024_LDO_CFG2_MPOK_MASK;
> +
> + val = rpdata->power_ok ? mask : 0;
> +
> + ret = regmap_update_bits(pmic->rmap, rinfo->cfg_addr,
> + mask, val);
> + if (ret < 0) {
> + dev_err(pmic->dev, "Reg 0x%02x update failed %d\n",
> + rinfo->cfg_addr, ret);
> + return ret;
> + }
> + }
> + break;
> +
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> static int max77620_init_pmic(struct max77620_regulator *pmic, int id)
> {
> struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id];
> int ret;
>
> + max77620_config_power_ok(pmic, id);
> +
> /* Update power mode */
> ret = max77620_regulator_get_power_mode(pmic, id);
> if (ret < 0)
> @@ -595,6 +633,12 @@ static int max77620_of_parse_cb(struct device_node *np,
> np, "maxim,suspend-fps-power-down-slot", &pval);
> rpdata->suspend_fps_pd_slot = (!ret) ? pval : -1;
>
> + ret = of_property_read_u32(np, "maxim,power-ok-control", &pval);
> + if (!ret)
> + rpdata->power_ok = pval;
> + else
> + rpdata->power_ok = -1;
> +
> ret = of_property_read_u32(np, "maxim,ramp-rate-setting", &pval);
> rpdata->ramp_rate_setting = (!ret) ? pval : 0;
>
> @@ -807,6 +851,8 @@ static int max77620_regulator_resume(struct device *dev)
> for (id = 0; id < MAX77620_NUM_REGS; id++) {
> reg_pdata = &pmic->reg_pdata[id];
>
> + max77620_config_power_ok(pmic, id);
> +
> max77620_regulator_set_fps_slots(pmic, id, false);
> if (reg_pdata->active_fps_src < 0)
> continue;
> diff --git a/include/linux/mfd/max77620.h b/include/linux/mfd/max77620.h
> index 3ca0af07..ad2a9a8 100644
> --- a/include/linux/mfd/max77620.h
> +++ b/include/linux/mfd/max77620.h
> @@ -180,6 +180,7 @@
> #define MAX77620_SD_CFG1_FPWM_SD_MASK BIT(2)
> #define MAX77620_SD_CFG1_FPWM_SD_SKIP 0
> #define MAX77620_SD_CFG1_FPWM_SD_FPWM BIT(2)
> +#define MAX20024_SD_CFG1_MPOK_MASK BIT(1)
> #define MAX77620_SD_CFG1_FSRADE_SD_MASK BIT(0)
> #define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0
> #define MAX77620_SD_CFG1_FSRADE_SD_ENABLE BIT(0)
> @@ -187,6 +188,7 @@
> /* LDO_CNFG2 */
> #define MAX77620_LDO_POWER_MODE_MASK 0xC0
> #define MAX77620_LDO_POWER_MODE_SHIFT 6
> +#define MAX20024_LDO_CFG2_MPOK_MASK BIT(2)
> #define MAX77620_LDO_CFG2_ADE_MASK BIT(1)
> #define MAX77620_LDO_CFG2_ADE_DISABLE 0
> #define MAX77620_LDO_CFG2_ADE_ENABLE BIT(1)
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH V3 2/4] mfd: pv88080: MFD core support
From: Lee Jones @ 2016-11-21 13:09 UTC (permalink / raw)
To: Eric Jeong
Cc: LINUX-KERNEL, Alexandre Courbot, DEVICETREE, LINUX-GPIO,
Liam Girdwood, Linus Walleij, Mark Brown, Mark Rutland,
Rob Herring, Support Opensource
In-Reply-To: <d5a2f377ee9ea7a7b68c9545ada0405b8134a9f4.1479429347.git.eric.jeong@diasemi.com>
On Fri, 18 Nov 2016, Eric Jeong wrote:
>
> From: Eric Jeong <eric.jeong.opensource@diasemi.com>
>
> This patch adds supports for PV88080 MFD core device.
>
> It provides communication through the I2C interface.
> It contains the following components:
> - Regulators
> - Configurable GPIOs
>
> Kconfig and Makefile are updated to reflect support for PV88080 PMIC.
>
> Signed-off-by: Eric Jeong <eric.jeong.opensource@diasemi.com>
>
> ---
> This patch applies against linux-next and next-20161117
>
> Hi,
>
> This patch adds MFD core driver for PV88080 PMIC.
> This is done as part of the existing PV88080 regulator driver by expending
> the driver for GPIO function support.
>
> Change since PATCH V2
> - Make one file insted of usging core and i2c file
> - Use devm_ function to be managed resource automatically
> - Separated mfd_cell and regmap_irq_chip declaration for clarification.
> - Updated Kconfig to use OF and assign yes to I2C
>
> Change since PATCH V1
> - Patch separated from PATCH V1
>
> Regards,
> Eric Jeong, Dialog Semiconductor Ltd.
>
>
> drivers/mfd/Kconfig | 12 ++
> drivers/mfd/Makefile | 1 +
> drivers/mfd/pv88080.c | 331 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/pv88080.h | 222 +++++++++++++++++++++++++++++
> 4 files changed, 566 insertions(+)
> create mode 100644 drivers/mfd/pv88080.c
> create mode 100644 include/linux/mfd/pv88080.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 06dc9b0..75abf2d 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -792,6 +792,18 @@ config MFD_PM8921_CORE
> Say M here if you want to include support for PM8921 chip as a module.
> This will build a module called "pm8921-core".
>
> +config MFD_PV88080
> + tristate "Powerventure Semiconductor PV88080 PMIC Support"
> + select MFD_CORE
> + select REGMAP_I2C
> + select REGMAP_IRQ
> + depends on I2C=y && OF
> + help
> + Say yes here for support for the Powerventure Semiconductor PV88080 PMIC.
> + This includes the I2C driver and core APIs.
> + Additional drivers must be enabled in order to use the functionality
> + of the device.
> +
> config MFD_QCOM_RPM
> tristate "Qualcomm Resource Power Manager (RPM)"
> depends on ARCH_QCOM && OF
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index db39377..e9e16c6 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -173,6 +173,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
> obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
> obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
> obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o
> +obj-$(CONFIG_MFD_PV88080) += pv88080.o
> obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o
> obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
> obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
> diff --git a/drivers/mfd/pv88080.c b/drivers/mfd/pv88080.c
> new file mode 100644
> index 0000000..518b44f
> --- /dev/null
> +++ b/drivers/mfd/pv88080.c
> @@ -0,0 +1,331 @@
> +/*
> + * pv88080-i2c.c - I2C access driver for PV88080
Remove the filename.
They have a habit of becoming out of date (like now).
> + * Copyright (C) 2016 Powerventure Semiconductor Ltd.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
Alphabetical.
> +#include <linux/mfd/pv88080.h>
This doesn't need to be separated from the rest.
> +#define PV88080_REG_EVENT_A_OFFSET 0
> +#define PV88080_REG_EVENT_B_OFFSET 1
> +#define PV88080_REG_EVENT_C_OFFSET 2
Spaces after 'define'.
> +static const struct resource regulators_aa_resources[] = {
> + {
> + .name = "VDD_TEMP_FAULT",
> + .start = PV88080_AA_IRQ_VDD_FLT,
> + .end = PV88080_AA_IRQ_OVER_TEMP,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static const struct resource regulators_ba_resources[] = {
> + {
> + .name = "VDD_TEMP_FAULT",
> + .start = PV88080_BA_IRQ_VDD_FLT,
> + .end = PV88080_BA_IRQ_OVER_TEMP,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
Use the DEFINE_RES_* macros.
> +static const struct mfd_cell pv88080_aa_cells[] = {
> + {
> + .name = "pv88080-regulator",
> + .num_resources = ARRAY_SIZE(regulators_aa_resources),
> + .resources = regulators_aa_resources,
> + .of_compatible = "pvs,pv88080-regulator",
> + },
> + {
> + .name = "pv88080-gpio",
> + .of_compatible = "pvs,pv88080-gpio",
> + },
> +};
> +
> +static const struct mfd_cell pv88080_ba_cells[] = {
> + {
> + .name = "pv88080-regulator",
> + .num_resources = ARRAY_SIZE(regulators_ba_resources),
> + .resources = regulators_ba_resources,
> + .of_compatible = "pvs,pv88080-regulator",
> + },
> + {
> + .name = "pv88080-gpio",
> + .of_compatible = "pvs,pv88080-gpio",
> + },
> +};
> +
> +static const struct regmap_irq pv88080_aa_irqs[] = {
> + /* PV88080 event A register for AA/AB silicon */
> + [PV88080_AA_IRQ_VDD_FLT] = {
> + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> + .mask = PV88080_M_VDD_FLT,
> + },
> + [PV88080_AA_IRQ_OVER_TEMP] = {
> + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> + .mask = PV88080_M_OVER_TEMP,
> + },
> + [PV88080_AA_IRQ_SEQ_RDY] = {
> + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> + .mask = PV88080_M_SEQ_RDY,
> + },
> + /* PV88080 event B register for AA/AB silicon */
> + [PV88080_AA_IRQ_HVBUCK_OV] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_HVBUCK_OV,
> + },
> + [PV88080_AA_IRQ_HVBUCK_UV] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_HVBUCK_UV,
> + },
> + [PV88080_AA_IRQ_HVBUCK_SCP] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_HVBUCK_SCP,
> + },
> + [PV88080_AA_IRQ_BUCK1_SCP] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_BUCK1_SCP,
> + },
> + [PV88080_AA_IRQ_BUCK2_SCP] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_BUCK2_SCP,
> + },
> + [PV88080_AA_IRQ_BUCK3_SCP] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_BUCK3_SCP,
> + },
> + /* PV88080 event C register for AA/AB silicon */
> + [PV88080_AA_IRQ_GPIO_FLAG0] = {
> + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> + .mask = PV88080_M_GPIO_FLAG0,
> + },
> + [PV88080_AA_IRQ_GPIO_FLAG1] = {
> + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> + .mask = PV88080_M_GPIO_FLAG1,
> + },
> +};
> +
> +static const struct regmap_irq pv88080_ba_irqs[] = {
> + /* PV88080 event A register for BA/BB silicon */
> + [PV88080_BA_IRQ_VDD_FLT] = {
> + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> + .mask = PV88080_M_VDD_FLT,
> + },
> + [PV88080_BA_IRQ_OVER_TEMP] = {
> + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> + .mask = PV88080_M_OVER_TEMP,
> + },
> + [PV88080_BA_IRQ_SEQ_RDY] = {
> + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> + .mask = PV88080_M_SEQ_RDY,
> + },
> + [PV88080_BA_IRQ_EXT_OT] = {
> + .reg_offset = PV88080_REG_EVENT_A_OFFSET,
> + .mask = PV88080_M_EXT_OT,
> + },
> + /* PV88080 event B register for BA/BB silicon */
> + [PV88080_BA_IRQ_HVBUCK_OV] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_HVBUCK_OV,
> + },
> + [PV88080_BA_IRQ_HVBUCK_UV] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_HVBUCK_UV,
> + },
> + [PV88080_BA_IRQ_HVBUCK_SCP] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_HVBUCK_SCP,
> + },
> + [PV88080_BA_IRQ_BUCK1_SCP] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_BUCK1_SCP,
> + },
> + [PV88080_BA_IRQ_BUCK2_SCP] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_BUCK2_SCP,
> + },
> + [PV88080_BA_IRQ_BUCK3_SCP] = {
> + .reg_offset = PV88080_REG_EVENT_B_OFFSET,
> + .mask = PV88080_M_BUCK3_SCP,
> + },
> + /* PV88080 event C register for BA/BB silicon */
> + [PV88080_BA_IRQ_GPIO_FLAG0] = {
> + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> + .mask = PV88080_M_GPIO_FLAG0,
> + },
> + [PV88080_BA_IRQ_GPIO_FLAG1] = {
> + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> + .mask = PV88080_M_GPIO_FLAG1,
> + },
> + [PV88080_BA_IRQ_BUCK1_DROP_TIMEOUT] = {
> + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> + .mask = PV88080_M_BUCK1_DROP_TIMEOUT,
> + },
> + [PV88080_BA_IRQ_BUCK2_DROP_TIMEOUT] = {
> + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> + .mask = PV88080_M_BUCK2_DROP_TIMEOUT,
> + },
> + [PB88080_BA_IRQ_BUCK3_DROP_TIMEOUT] = {
> + .reg_offset = PV88080_REG_EVENT_C_OFFSET,
> + .mask = PV88080_M_BUCk3_DROP_TIMEOUT,
> + },
> +};
> +
> +static const struct regmap_irq_chip pv88080_aa_irq_chip = {
> + .name = "pv88080-irq",
> + .irqs = pv88080_aa_irqs,
> + .num_irqs = ARRAY_SIZE(pv88080_aa_irqs),
> + .num_regs = 3,
> + .status_base = PV88080_REG_EVENT_A,
> + .mask_base = PV88080_REG_MASK_A,
> + .ack_base = PV88080_REG_EVENT_A,
> + .init_ack_masked = true,
> +};
> +
> +static const struct regmap_irq_chip pv88080_ba_irq_chip = {
> + .name = "pv88080-irq",
> + .irqs = pv88080_ba_irqs,
> + .num_irqs = ARRAY_SIZE(pv88080_ba_irqs),
> + .num_regs = 3,
> + .status_base = PV88080_REG_EVENT_A,
> + .mask_base = PV88080_REG_MASK_A,
> + .ack_base = PV88080_REG_EVENT_A,
> + .init_ack_masked = true,
> +};
> +
> +static const struct regmap_config pv88080_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> +};
> +
> +static const struct of_device_id pv88080_of_match_table[] = {
> + { .compatible = "pvs,pv88080", .data = (void *)TYPE_PV88080_AA },
> + { .compatible = "pvs,pv88080-aa", .data = (void *)TYPE_PV88080_AA },
> + { .compatible = "pvs,pv88080-ba", .data = (void *)TYPE_PV88080_BA },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, pv88080_of_match_table);
> +
> +static int pv88080_probe(struct i2c_client *client,
> + const struct i2c_device_id *ids)
> +{
> + struct pv88080 *chip;
> + const struct of_device_id *match;
> + const struct regmap_irq_chip *pv88080_irq_chips;
> + const struct mfd_cell *pv88080_mfd_cells;
> + int ret, n_devs;
> +
> + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
> + if (!chip)
> + return -ENOMEM;
> +
> + if (client->dev.of_node) {
> + match = of_match_node(pv88080_of_match_table,
> + client->dev.of_node);
> + if (!match) {
> + dev_err(&client->dev, "Failed to get of_match_node\n");
> + return -EINVAL;
-ENODEV
> + }
> + chip->type = (unsigned long)match->data;
> + } else {
> + chip->type = ids->driver_data;
> + }
> +
> + i2c_set_clientdata(client, chip);
> +
> + chip->irq = client->irq;
> + chip->dev = &client->dev;
> +
> + chip->regmap = devm_regmap_init_i2c(client, &pv88080_regmap_config);
> + if (IS_ERR(chip->regmap)) {
> + dev_err(chip->dev, "Failed to initialize register map\n");
> + return PTR_ERR(chip->regmap);
> + }
> +
> + ret = regmap_write(chip->regmap, PV88080_REG_MASK_A, 0xFF);
> + if (ret < 0) {
> + dev_err(chip->dev, "Failed to mask A reg: %d\n", ret);
> + return ret;
> + }
> + ret = regmap_write(chip->regmap, PV88080_REG_MASK_B, 0xFF);
> + if (ret < 0) {
> + dev_err(chip->dev, "Failed to mask B reg: %d\n", ret);
> + return ret;
> + }
> + ret = regmap_write(chip->regmap, PV88080_REG_MASK_C, 0xFF);
> + if (ret < 0) {
> + dev_err(chip->dev, "Failed to mask C reg: %d\n", ret);
> + return ret;
> + }
What do these calls do?
> + switch (chip->type) {
> + case TYPE_PV88080_AA:
> + pv88080_irq_chips = &pv88080_aa_irq_chip;
> + pv88080_mfd_cells = pv88080_aa_cells;
> + n_devs = ARRAY_SIZE(pv88080_aa_cells);
> + break;
> + case TYPE_PV88080_BA:
> + pv88080_irq_chips = &pv88080_ba_irq_chip;
> + pv88080_mfd_cells = pv88080_ba_cells;
> + n_devs = ARRAY_SIZE(pv88080_ba_cells);
> + break;
> + }
> +
> + ret = devm_regmap_add_irq_chip(chip->dev, chip->regmap,
> + chip->irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + 0, pv88080_irq_chips, &chip->irq_data);
> + if (ret) {
> + dev_err(chip->dev, "Failed to add IRQ %d: %d\n",
> + chip->irq, ret);
> + return ret;
> + }
> +
> + ret = devm_mfd_add_devices(chip->dev, PLATFORM_DEVID_NONE,
> + pv88080_mfd_cells, n_devs,
> + NULL, 0, NULL);
> + if (ret) {
> + dev_err(chip->dev, "Failed to add MFD devices\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id pv88080_id_table[] = {
> + { "pv88080", TYPE_PV88080_AA },
> + { "pv88080-aa", TYPE_PV88080_AA },
> + { "pv88080-ba", TYPE_PV88080_BA },
> + { },
> +};
> +MODULE_DEVICE_TABLE(i2c, pv88080_id_table);
> +
> +static struct i2c_driver pv88080_driver = {
> + .driver = {
> + .name = "pv88080",
> + .of_match_table = of_match_ptr(pv88080_of_match_table),
> + },
> + .probe = pv88080_probe,
> + .id_table = pv88080_id_table,
> +};
> +module_i2c_driver(pv88080_driver);
> +
> +MODULE_AUTHOR("Eric Jeong <eric.jeong.opensource@diasemi.com>");
> +MODULE_DESCRIPTION("MFD Driver for Powerventure PV88080");
> +MODULE_LICENSE("GPL");
> +
> diff --git a/include/linux/mfd/pv88080.h b/include/linux/mfd/pv88080.h
> new file mode 100644
> index 0000000..76d6656
> --- /dev/null
> +++ b/include/linux/mfd/pv88080.h
> @@ -0,0 +1,222 @@
> +/*
> + * pv88080.h - Declarations for PV88080.
Remove filename.
> + * Copyright (C) 2016 Powerventure Semiconductor Ltd.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __PV88080_H__
> +#define __PV88080_H__
> +
> +#include <linux/regulator/machine.h>
> +#include <linux/device.h>
> +#include <linux/regmap.h>
> +
> +/* System Control and Event Registers */
> +#define PV88080_REG_STATUS_A 0x01
> +#define PV88080_REG_EVENT_A 0x04
> +#define PV88080_REG_MASK_A 0x09
> +#define PV88080_REG_MASK_B 0x0A
> +#define PV88080_REG_MASK_C 0x0B
> +
> +/* GPIO Registers - rev. AA */
> +#define PV88080AA_REG_GPIO_INPUT 0x18
> +#define PV88080AA_REG_GPIO_OUTPUT 0x19
> +#define PV88080AA_REG_GPIO_GPIO0 0x1A
> +
> +/* Regulator Registers - rev. AA */
> +#define PV88080AA_REG_HVBUCK_CONF1 0x2D
> +#define PV88080AA_REG_HVBUCK_CONF2 0x2E
> +#define PV88080AA_REG_BUCK1_CONF0 0x27
> +#define PV88080AA_REG_BUCK1_CONF1 0x28
> +#define PV88080AA_REG_BUCK1_CONF2 0x59
> +#define PV88080AA_REG_BUCK1_CONF5 0x5C
> +#define PV88080AA_REG_BUCK2_CONF0 0x29
> +#define PV88080AA_REG_BUCK2_CONF1 0x2A
> +#define PV88080AA_REG_BUCK2_CONF2 0x61
> +#define PV88080AA_REG_BUCK2_CONF5 0x64
> +#define PV88080AA_REG_BUCK3_CONF0 0x2B
> +#define PV88080AA_REG_BUCK3_CONF1 0x2C
> +#define PV88080AA_REG_BUCK3_CONF2 0x69
> +#define PV88080AA_REG_BUCK3_CONF5 0x6C
> +
> +/* GPIO Registers - rev. BA */
> +#define PV88080BA_REG_GPIO_INPUT 0x17
> +#define PV88080BA_REG_GPIO_OUTPUT 0x18
> +#define PV88080BA_REG_GPIO_GPIO0 0x19
> +
> +/* Regulator Registers - rev. BA */
> +#define PV88080BA_REG_HVBUCK_CONF1 0x33
> +#define PV88080BA_REG_HVBUCK_CONF2 0x34
> +#define PV88080BA_REG_BUCK1_CONF0 0x2A
> +#define PV88080BA_REG_BUCK1_CONF1 0x2C
> +#define PV88080BA_REG_BUCK1_CONF2 0x5A
> +#define PV88080BA_REG_BUCK1_CONF5 0x5D
> +#define PV88080BA_REG_BUCK2_CONF0 0x2D
> +#define PV88080BA_REG_BUCK2_CONF1 0x2F
> +#define PV88080BA_REG_BUCK2_CONF2 0x63
> +#define PV88080BA_REG_BUCK2_CONF5 0x66
> +#define PV88080BA_REG_BUCK3_CONF0 0x30
> +#define PV88080BA_REG_BUCK3_CONF1 0x32
> +#define PV88080BA_REG_BUCK3_CONF2 0x6C
> +#define PV88080BA_REG_BUCK3_CONF5 0x6F
> +
> +/* PV88080_REG_EVENT_A (addr=0x04) */
> +#define PV88080_E_VDD_FLT 0x01
> +#define PV88080_E_OVER_TEMP 0x02
> +#define PV88080_E_SEQ_RDY 0x04
> +#define PV88080_E_EXT_OT 0x08
> +
> +/* PV88080_REG_MASK_A (addr=0x09) */
> +#define PV88080_M_VDD_FLT 0x01
> +#define PV88080_M_OVER_TEMP 0x02
> +#define PV88080_M_SEQ_RDY 0x04
> +#define PV88080_M_EXT_OT 0x08
> +
> +/* PV88080_REG_EVENT_B (addr=0x05) */
> +#define PV88080_E_HVBUCK_OV 0x01
> +#define PV88080_E_HVBUCK_UV 0x02
> +#define PV88080_E_HVBUCK_SCP 0x04
> +#define PV88080_E_BUCK1_SCP 0x08
> +#define PV88080_E_BUCK2_SCP 0x10
> +#define PV88080_E_BUCK3_SCP 0x20
> +
> +/* PV88080_REG_MASK_B (addr=0x0A) */
> +#define PV88080_M_HVBUCK_OV 0x01
> +#define PV88080_M_HVBUCK_UV 0x02
> +#define PV88080_M_HVBUCK_SCP 0x04
> +#define PV88080_M_BUCK1_SCP 0x08
> +#define PV88080_M_BUCK2_SCP 0x10
> +#define PV88080_M_BUCK3_SCP 0x20
> +
> +/* PV88080_REG_EVENT_C (addr=0x06) */
> +#define PV88080_E_GPIO_FLAG0 0x01
> +#define PV88080_E_GPIO_FLAG1 0x02
> +#define PV88080_E_BUCK1_DROP_TIMEOUT 0x08
> +#define PV88080_E_BUCK2_DROP_TIMEOUT 0x10
> +#define PV88080_E_BUCk3_DROP_TIMEOUT 0x20
> +
> +/* PV88080_REG_MASK_C (addr=0x0B) */
> +#define PV88080_M_GPIO_FLAG0 0x01
> +#define PV88080_M_GPIO_FLAG1 0x02
> +#define PV88080_M_BUCK1_DROP_TIMEOUT 0x08
> +#define PV88080_M_BUCK2_DROP_TIMEOUT 0x10
> +#define PV88080_M_BUCk3_DROP_TIMEOUT 0x20
> +
> +/* PV88080xx_REG_GPIO_GPIO0 (addr=0x1A|0x19) */
> +#define PV88080_GPIO_DIRECTION_MASK 0x01
> +#define PV88080_GPIO_SINGLE_ENDED_MASK 0x02
> +
> +/* PV88080_REG_BUCK1_CONF0 (addr=0x27|0x2A) */
> +#define PV88080_BUCK1_EN 0x80
> +#define PV88080_VBUCK1_MASK 0x7F
> +
> +/* PV88080_REG_BUCK2_CONF0 (addr=0x29|0x2D) */
> +#define PV88080_BUCK2_EN 0x80
> +#define PV88080_VBUCK2_MASK 0x7F
> +
> +/* PV88080_REG_BUCK3_CONF0 (addr=0x2B|0x30) */
> +#define PV88080_BUCK3_EN 0x80
> +#define PV88080_VBUCK3_MASK 0x7F
> +
> +/* PV88080_REG_BUCK1_CONF1 (addr=0x28|0x2C) */
> +#define PV88080_BUCK1_ILIM_SHIFT 2
> +#define PV88080_BUCK1_ILIM_MASK 0x0C
> +#define PV88080_BUCK1_MODE_MASK 0x03
> +
> +/* PV88080_REG_BUCK2_CONF1 (addr=0x2A|0x2F) */
> +#define PV88080_BUCK2_ILIM_SHIFT 2
> +#define PV88080_BUCK2_ILIM_MASK 0x0C
> +#define PV88080_BUCK2_MODE_MASK 0x03
> +
> +/* PV88080_REG_BUCK3_CONF1 (addr=0x2C|0x32) */
> +#define PV88080_BUCK3_ILIM_SHIFT 2
> +#define PV88080_BUCK3_ILIM_MASK 0x0C
> +#define PV88080_BUCK3_MODE_MASK 0x03
> +
> +#define PV88080_BUCK_MODE_SLEEP 0x00
> +#define PV88080_BUCK_MODE_AUTO 0x01
> +#define PV88080_BUCK_MODE_SYNC 0x02
> +
> +/* PV88080_REG_HVBUCK_CONF1 (addr=0x2D|0x33) */
> +#define PV88080_VHVBUCK_MASK 0xFF
> +
> +/* PV88080_REG_HVBUCK_CONF1 (addr=0x2E|0x34) */
> +#define PV88080_HVBUCK_EN 0x01
> +
> +/* PV88080_REG_BUCK2_CONF2 (addr=0x61|0x63) */
> +/* PV88080_REG_BUCK3_CONF2 (addr=0x69|0x6C) */
> +#define PV88080_BUCK_VDAC_RANGE_SHIFT 7
> +#define PV88080_BUCK_VDAC_RANGE_MASK 0x01
> +
> +#define PV88080_BUCK_VDAC_RANGE_1 0x00
> +#define PV88080_BUCK_VDAC_RANGE_2 0x01
> +
> +/* PV88080_REG_BUCK2_CONF5 (addr=0x64|0x66) */
> +/* PV88080_REG_BUCK3_CONF5 (addr=0x6C|0x6F) */
> +#define PV88080_BUCK_VRANGE_GAIN_SHIFT 0
> +#define PV88080_BUCK_VRANGE_GAIN_MASK 0x01
> +
> +#define PV88080_BUCK_VRANGE_GAIN_1 0x00
> +#define PV88080_BUCK_VRANGE_GAIN_2 0x01
> +
> +#define PV88080_MAX_REGULATORS 4
> +
> +enum pv88080_types {
> + TYPE_PV88080_AA,
> + TYPE_PV88080_BA,
> +};
> +
> +/* Interrupts */
> +enum pv88080_aa_irqs {
> + PV88080_AA_IRQ_VDD_FLT,
> + PV88080_AA_IRQ_OVER_TEMP,
> + PV88080_AA_IRQ_SEQ_RDY,
> + PV88080_AA_IRQ_HVBUCK_OV,
> + PV88080_AA_IRQ_HVBUCK_UV,
> + PV88080_AA_IRQ_HVBUCK_SCP,
> + PV88080_AA_IRQ_BUCK1_SCP,
> + PV88080_AA_IRQ_BUCK2_SCP,
> + PV88080_AA_IRQ_BUCK3_SCP,
> + PV88080_AA_IRQ_GPIO_FLAG0,
> + PV88080_AA_IRQ_GPIO_FLAG1,
> +};
> +
> +enum pv88080_ba_irqs {
> + PV88080_BA_IRQ_VDD_FLT,
> + PV88080_BA_IRQ_OVER_TEMP,
> + PV88080_BA_IRQ_SEQ_RDY,
> + PV88080_BA_IRQ_EXT_OT,
> + PV88080_BA_IRQ_HVBUCK_OV,
> + PV88080_BA_IRQ_HVBUCK_UV,
> + PV88080_BA_IRQ_HVBUCK_SCP,
> + PV88080_BA_IRQ_BUCK1_SCP,
> + PV88080_BA_IRQ_BUCK2_SCP,
> + PV88080_BA_IRQ_BUCK3_SCP,
> + PV88080_BA_IRQ_GPIO_FLAG0,
> + PV88080_BA_IRQ_GPIO_FLAG1,
> + PV88080_BA_IRQ_BUCK1_DROP_TIMEOUT,
> + PV88080_BA_IRQ_BUCK2_DROP_TIMEOUT,
> + PB88080_BA_IRQ_BUCK3_DROP_TIMEOUT,
> +};
> +
> +struct pv88080 {
> + struct device *dev;
> + struct regmap *regmap;
> + unsigned long type;
Does this really need to be in here?
> + /* IRQ Data */
> + int irq;
> + struct regmap_irq_chip_data *irq_data;
> +};
> +
> +#endif /* __PV88080_H__ */
> +
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCH v2 5/8] dt-bindings: mfd: Provide human readable defines for TPS65217 interrupts
From: Milo Kim @ 2016-11-21 13:05 UTC (permalink / raw)
To: Tony Lindgren, Lee Jones
Cc: bcousson, linux-omap, devicetree, linux-arm-kernel, linux-kernel,
Robert Nelson
In-Reply-To: <20161118151051.GV4082@atomide.com>
On 11/19/2016 12:10 AM, Tony Lindgren wrote:
>>> +#define TPS65217_IRQ_USB 0
>>> > > +#define TPS65217_IRQ_AC 1
>>> > > +#define TPS65217_IRQ_PB 2
>> >
>> > What are "AC" and "PB". Seeing as these are meant to be "human
>> > readable", let's make them more human friendly.
> Good idea. Milo, please do an incremental single follow-up patch as
> I already have applied this and the dts changes using it.
Thanks for catching this. Let me send a patch soon.
Best regards,
Milo
^ permalink raw reply
* Re: [RFC PATCH v2 2/7] misc: minimal mux subsystem and gpio-based mux controller
From: Peter Rosin @ 2016-11-21 13:03 UTC (permalink / raw)
To: Jonathan Cameron, linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald-Stadler, Arnd Bergmann,
Greg Kroah-Hartman, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <7fad4fd7-fffe-5214-b25f-dca923f8c466-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On 2016-11-19 16:34, Jonathan Cameron wrote:
> On 17/11/16 21:48, Peter Rosin wrote:
>> When both the iio subsystem and the i2c subsystem wants to update
>> the same mux, there needs to be some coordination. Invent a new
>> minimal "mux" subsystem that handles this.
> I'd probably put something more general in the description. Lots of things
> may need the same infrastructure. This is just an example.
I'll make it more general for the next round.
> Few bits inline.
>
> Also, I suspect you will fairly rapidly have a need for a strobe signal
> as well. A lot of mux chips that are more than 2 way seem to have them to
> allow multiple chips to be synchronized.
I think that can be easily added later, when/if it's needed.
>>
>> Add a single backend driver for this new subsystem that can
>> control gpio based multiplexers.
>> ---
>> drivers/misc/Kconfig | 6 +
>> drivers/misc/Makefile | 2 +
>> drivers/misc/mux-core.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++++
>> drivers/misc/mux-gpio.c | 115 +++++++++++++++++++
>> include/linux/mux.h | 53 +++++++++
>> 5 files changed, 475 insertions(+)
>> create mode 100644 drivers/misc/mux-core.c
>> create mode 100644 drivers/misc/mux-gpio.c
>> create mode 100644 include/linux/mux.h
>>
>> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
>> index 64971baf11fa..9e119bb78d82 100644
>> --- a/drivers/misc/Kconfig
>> +++ b/drivers/misc/Kconfig
>> @@ -766,6 +766,12 @@ config PANEL_BOOT_MESSAGE
>> An empty message will only clear the display at driver init time. Any other
>> printf()-formatted message is valid with newline and escape codes.
>>
>> +config MUX_GPIO
>> + tristate "GPIO-controlled MUX controller"
>> + depends on OF
>> + help
>> + GPIO-controlled MUX controller
>> +
>> source "drivers/misc/c2port/Kconfig"
>> source "drivers/misc/eeprom/Kconfig"
>> source "drivers/misc/cb710/Kconfig"
>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>> index 31983366090a..92b547bcbac1 100644
>> --- a/drivers/misc/Makefile
>> +++ b/drivers/misc/Makefile
>> @@ -53,6 +53,8 @@ obj-$(CONFIG_ECHO) += echo/
>> obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
>> obj-$(CONFIG_CXL_BASE) += cxl/
>> obj-$(CONFIG_PANEL) += panel.o
>> +obj-$(CONFIG_MUX_GPIO) += mux-core.o
>> +obj-$(CONFIG_MUX_GPIO) += mux-gpio.o
>>
>> lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
>> lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
>> diff --git a/drivers/misc/mux-core.c b/drivers/misc/mux-core.c
>> new file mode 100644
>> index 000000000000..7a8bf003a92c
>> --- /dev/null
>> +++ b/drivers/misc/mux-core.c
>> @@ -0,0 +1,299 @@
>> +/*
>> + * Multiplexer subsystem
>> + *
>> + * Copyright (C) 2016 Axentia Technologies AB
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#define pr_fmt(fmt) "mux-core: " fmt
>> +
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/idr.h>
>> +#include <linux/module.h>
>> +#include <linux/mux.h>
>> +#include <linux/of.h>
>> +#include <linux/slab.h>
>> +
>> +static struct bus_type mux_bus_type = {
>> + .name = "mux",
>> +};
>> +
>> +static int __init mux_init(void)
>> +{
>> + return bus_register(&mux_bus_type);
>> +}
>> +
>> +static void __exit mux_exit(void)
>> +{
>> + bus_unregister(&mux_bus_type);
>> +}
>> +
>> +static DEFINE_IDA(mux_ida);
>> +
>> +static void mux_control_release(struct device *dev)
>> +{
>> + struct mux_control *mux = to_mux_control(dev);
>> +
>> + ida_simple_remove(&mux_ida, mux->id);
>> + kfree(mux);
>> +}
>> +
>> +static struct device_type mux_control_type = {
>> + .name = "mux-control",
>> + .release = mux_control_release,
>> +};
>> +
>> +/*
>> + * Allocate a mux-control, plus an extra memory area for private use
>> + * by the caller.
>> + */
>> +struct mux_control *mux_control_alloc(size_t sizeof_priv)
>> +{
>> + struct mux_control *mux;
>> +
> Worth planning ahead for spi controlled muxes and others that need their
> structures to be carefully aligned to avoid dma cacheline fun?
> Easy enough to add later I guess.
Right, I'll leave it for later when/if it's needed.
>> + mux = kzalloc(sizeof(*mux) + sizeof_priv, GFP_KERNEL);
>> + if (!mux)
>> + return NULL;
>> +
>> + mux->dev.bus = &mux_bus_type;
>> + mux->dev.type = &mux_control_type;
>> + device_initialize(&mux->dev);
>> + dev_set_drvdata(&mux->dev, mux);
>> +
>> + init_rwsem(&mux->lock);
>> + mux->priv = mux + 1;
> Needed? Or just do it with a bit of pointer math where the access is needed?
Good suggestion, I'll add an inline function and drop the priv member.
>> +
>> + mux->id = ida_simple_get(&mux_ida, 0, 0, GFP_KERNEL);
>> + if (mux->id < 0) {
>> + pr_err("mux-controlX failed to get device id\n");
>> + kfree(mux);
>> + return NULL;
>> + }
>> + dev_set_name(&mux->dev, "mux:control%d", mux->id);
>> +
>> + mux->cached_state = -1;
>> + mux->idle_state = -1;
>> +
>> + return mux;
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_alloc);
>> +
>> +/*
>> + * Register the mux-control, thus readying it for use.
> Either single line comment style - or perhaps kernel doc the lot...
Kernel doc was the plan from the start, coming up...
>> + */
>> +int mux_control_register(struct mux_control *mux)
>> +{
>> + /* If the calling driver did not initialize of_node, do it here */
>> + if (!mux->dev.of_node && mux->dev.parent)
>> + mux->dev.of_node = mux->dev.parent->of_node;
>> +
>> + return device_add(&mux->dev);
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_register);
>> +
>> +/*
>> + * Take the mux-control off-line.
>> + */
>> +void mux_control_unregister(struct mux_control *mux)
>> +{
>> + device_del(&mux->dev);
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_unregister);
>> +
>> +/*
>> + * Put away the mux-control for good.
>> + */
>> +void mux_control_put(struct mux_control *mux)
>> +{
>> + if (!mux)
>> + return;
>> + put_device(&mux->dev);
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_put);
>> +
>> +static int mux_control_set(struct mux_control *mux, int state)
>> +{
>> + int ret = mux->ops->set(mux, state);
>> +
>> + mux->cached_state = ret < 0 ? -1 : state;
>> +
>> + return ret;
>> +}
>> +
>> +/*
>> + * Select the given multiplexer channel. Call mux_control_deselect()
>> + * when the operation is complete on the multiplexer channel, and the
>> + * multiplexer is free for others to use.
>> + */
>> +int mux_control_select(struct mux_control *mux, int state)
>> +{
>> + int ret;
>> +
>> + if (down_read_trylock(&mux->lock)) {
>> + if (mux->cached_state == state)
>> + return 0;
>> +
>> + /* Sigh, the mux needs updating... */
>> + up_read(&mux->lock);
>> + }
>> +
>> + /* ...or it's just contended. */
>> + down_write(&mux->lock);
>> +
>> + if (mux->cached_state == state) {
>> + /*
>> + * Hmmm, someone else changed the mux to my liking.
>> + * That makes me wonder how long I waited for nothing...
>> + */
>> + downgrade_write(&mux->lock);
>> + return 0;
>> + }
>> +
>> + ret = mux_control_set(mux, state);
>> + if (ret < 0) {
>> + if (mux->idle_state != -1)
>> + mux_control_set(mux, mux->idle_state);
>> +
>> + up_write(&mux->lock);
>> + return ret;
>> + }
>> +
>> + downgrade_write(&mux->lock);
>> +
>> + return 1;
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_select);
>> +
>> +/*
>> + * Deselect the previously selected multiplexer channel.
>> + */
>> +int mux_control_deselect(struct mux_control *mux)
>> +{
>> + int ret = 0;
>> +
>> + if (mux->idle_state != -1 && mux->cached_state != mux->idle_state)
>> + ret = mux_control_set(mux, mux->idle_state);
>> +
>> + up_read(&mux->lock);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_deselect);
>> +
>> +static int of_dev_node_match(struct device *dev, void *data)
>> +{
>> + return dev->of_node == data;
>> +}
>> +
>> +static struct mux_control *of_find_mux_by_node(struct device_node *np)
>> +{
>> + struct device *dev;
>> +
>> + dev = bus_find_device(&mux_bus_type, NULL, np, of_dev_node_match);
>> +
>> + return dev ? to_mux_control(dev) : NULL;
>> +}
>> +
>> +static struct mux_control *of_mux_control_get(struct device_node *np, int index)
>> +{
>> + struct device_node *mux_np;
>> + struct mux_control *mux;
>> +
>> + mux_np = of_parse_phandle(np, "control-muxes", index);
>> + if (!mux_np)
>> + return NULL;
>> +
>> + mux = of_find_mux_by_node(mux_np);
>> + of_node_put(mux_np);
>> +
>> + return mux;
>> +}
>> +
>> +/*
>> + * Get a named mux.
>> + */
>> +struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
>> +{
>> + struct device_node *np = dev->of_node;
>> + struct mux_control *mux;
>> + int index;
>> +
>> + index = of_property_match_string(np, "control-mux-names", mux_name);
>> + if (index < 0) {
>> + dev_err(dev, "failed to get control-mux %s:%s(%i)\n",
>> + np->full_name, mux_name ?: "", index);
>> + return ERR_PTR(index);
>> + }
>> +
>> + mux = of_mux_control_get(np, index);
>> + if (!mux)
>> + return ERR_PTR(-EPROBE_DEFER);
>> +
>> + return mux;
>> +}
>> +EXPORT_SYMBOL_GPL(mux_control_get);
>> +
>> +static void devm_mux_control_free(struct device *dev, void *res)
>> +{
>> + struct mux_control *mux = *(struct mux_control **)res;
>> +
>> + mux_control_put(mux);
>> +}
>> +
>> +/*
>> + * Get a named mux, with resource management.
>> + */
> Guess it might be elsewhere in patch set but remember to add this to
> the global list of devm interfaces (in Documentation somewhere.. IIRC)
Right, now that you mention it, I remember having seen that document
at some point. I'll look it up.
Thanks for all comments!
Cheers,
Peter
>> +struct mux_control *devm_mux_control_get(struct device *dev,
>> + const char *mux_name)
>> +{
>> + struct mux_control **ptr, *mux;
>> +
>> + ptr = devres_alloc(devm_mux_control_free, sizeof(*ptr), GFP_KERNEL);
>> + if (!ptr)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + mux = mux_control_get(dev, mux_name);
>> + if (IS_ERR(mux)) {
>> + devres_free(ptr);
>> + return mux;
>> + }
>> +
>> + *ptr = mux;
>> + devres_add(dev, ptr);
>> +
>> + return mux;
>> +}
>> +EXPORT_SYMBOL_GPL(devm_mux_control_get);
>> +
>> +static int devm_mux_control_match(struct device *dev, void *res, void *data)
>> +{
>> + struct mux_control **r = res;
>> +
>> + if (!r || !*r) {
>> + WARN_ON(!r || !*r);
>> + return 0;
>> + }
>> +
>> + return *r == data;
>> +}
>> +
>> +/*
>> + * Resource-managed version mux_control_put.
>> + */
>> +void devm_mux_control_put(struct device *dev, struct mux_control *mux)
>> +{
>> + WARN_ON(devres_release(dev, devm_mux_control_free,
>> + devm_mux_control_match, mux));
>> +}
>> +EXPORT_SYMBOL_GPL(devm_mux_control_put);
>> +
>> +subsys_initcall(mux_init);
>> +module_exit(mux_exit);
>> +
>> +MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org");
>> +MODULE_DESCRIPTION("MUX subsystem");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/misc/mux-gpio.c b/drivers/misc/mux-gpio.c
>> new file mode 100644
>> index 000000000000..2ddd7fb24078
>> --- /dev/null
>> +++ b/drivers/misc/mux-gpio.c
>> @@ -0,0 +1,115 @@
>> +/*
>> + * GPIO-controlled multiplexer driver
>> + *
>> + * Copyright (C) 2016 Axentia Technologies AB
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/module.h>
>> +#include <linux/mux.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +
>> +struct mux_gpio {
>> + struct gpio_descs *gpios;
>> +};
>> +
>> +static int mux_gpio_set(struct mux_control *mux, int val)
>> +{
>> + struct mux_gpio *mux_gpio = mux->priv;
>> + int i;
>> +
>> + for (i = 0; i < mux_gpio->gpios->ndescs; i++)
>> + gpiod_set_value_cansleep(mux_gpio->gpios->desc[i],
>> + val & (1 << i));
>> +
>> + return 0;
>> +}
>> +
>> +static const struct mux_control_ops mux_gpio_ops = {
>> + .set = mux_gpio_set,
>> +};
>> +
>> +static const struct of_device_id mux_gpio_dt_ids[] = {
>> + { .compatible = "mux-gpio", },
>> + { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, mux_gpio_dt_ids);
>> +
>> +static int mux_gpio_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct device_node *np = pdev->dev.of_node;
>> + struct mux_control *mux;
>> + struct mux_gpio *mux_gpio;
>> + u32 idle_state;
>> + int ret;
>> +
>> + if (!np)
>> + return -ENODEV;
>> +
>> + mux = mux_control_alloc(sizeof(*mux_gpio));
>> + if (!mux)
>> + return -ENOMEM;
>> + mux_gpio = mux->priv;
>> + mux->dev.parent = dev;
>> + mux->ops = &mux_gpio_ops;
>> +
>> + platform_set_drvdata(pdev, mux);
>> +
>> + mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
>> + if (IS_ERR(mux_gpio->gpios)) {
>> + if (PTR_ERR(mux_gpio->gpios) != -EPROBE_DEFER)
>> + dev_err(dev, "failed to get gpios\n");
>> + mux_control_put(mux);
>> + return PTR_ERR(mux_gpio->gpios);
>> + }
>> +
>> + ret = of_property_read_u32(np, "idle-state", &idle_state);
>> + if (ret >= 0) {
>> + if (idle_state >= (1 << mux_gpio->gpios->ndescs)) {
>> + dev_err(dev, "invalid idle-state %u\n", idle_state);
>> + return -EINVAL;
>> + }
>> + mux->idle_state = idle_state;
>> + }
>> +
>> + ret = mux_control_register(mux);
>> + if (ret < 0) {
>> + dev_err(dev, "failed to register mux_control\n");
>> + mux_control_put(mux);
>> + return ret;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int mux_gpio_remove(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct mux_control *mux = to_mux_control(dev);
>> +
>> + mux_control_unregister(mux);
>> + mux_control_put(mux);
>> + return 0;
>> +}
>> +
>> +static struct platform_driver mux_gpio_driver = {
>> + .driver = {
>> + .name = "mux-gpio",
>> + .of_match_table = of_match_ptr(mux_gpio_dt_ids),
>> + },
>> + .probe = mux_gpio_probe,
>> + .remove = mux_gpio_remove,
>> +};
>> +module_platform_driver(mux_gpio_driver);
>> +
>> +MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org");
>> +MODULE_DESCRIPTION("GPIO-controlled multiplexer driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/mux.h b/include/linux/mux.h
>> new file mode 100644
>> index 000000000000..5b21b8184056
>> --- /dev/null
>> +++ b/include/linux/mux.h
>> @@ -0,0 +1,53 @@
>> +/*
>> + * mux.h - definitions for the multiplexer interface
>> + *
>> + * Copyright (C) 2016 Axentia Technologies AB
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef _LINUX_MUX_H
>> +#define _LINUX_MUX_H
>> +
>> +#include <linux/device.h>
>> +#include <linux/rwsem.h>
>> +
>> +struct mux_control;
>> +
>> +struct mux_control_ops {
>> + int (*set)(struct mux_control *mux, int reg);
>> +};
>> +
>> +struct mux_control {
>> + struct rw_semaphore lock; /* protects the state of the mux */
>> +
>> + struct device dev;
>> + int id;
>> +
>> + int cached_state;
>> + int idle_state;
>> +
>> + const struct mux_control_ops *ops;
>> +
>> + void *priv;
>> +};
>> +
>> +#define to_mux_control(x) container_of((x), struct mux_control, dev)
>> +
>> +struct mux_control *mux_control_alloc(size_t sizeof_priv);
>> +int mux_control_register(struct mux_control *mux);
>> +void mux_control_unregister(struct mux_control *mux);
>> +void mux_control_put(struct mux_control *mux);
>> +
>> +int mux_control_select(struct mux_control *mux, int state);
>> +int mux_control_deselect(struct mux_control *mux);
>> +
>> +struct mux_control *mux_control_get(struct device *dev,
>> + const char *mux_name);
>> +struct mux_control *devm_mux_control_get(struct device *dev,
>> + const char *mux_name);
>> +void devm_mux_control_put(struct device *dev, struct mux_control *mux);
>> +
>> +#endif /* _LINUX_MUX_H */
>>
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH V5 1/3] ARM64 LPC: Indirect ISA port IO introduced
From: John Garry @ 2016-11-21 12:58 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Mark Rutland, zhichang.yuan
Cc: catalin.marinas, will.deacon, robh+dt, bhelgaas, olof, arnd,
linux-arm-kernel, lorenzo.pieralisi, linux-kernel, linuxarm,
devicetree, linux-pci, linux-serial, minyard, liviu.dudau,
zourongrong, gabriele.paoloni, zhichang.yuan02, kantyzc, xuwei5,
marc.zyngier
In-Reply-To: <1478647002.7430.69.camel@kernel.crashing.org>
On 08/11/2016 23:16, Benjamin Herrenschmidt wrote:
> On Tue, 2016-11-08 at 12:03 +0000, Mark Rutland wrote:
>> On Tue, Nov 08, 2016 at 11:47:07AM +0800, zhichang.yuan wrote:
>>>
>>> For arm64, there is no I/O space as other architectural platforms, such as
>>> X86. Most I/O accesses are achieved based on MMIO. But for some arm64 SoCs,
>>> such as Hip06, when accessing some legacy ISA devices connected to LPC, those
>>> known port addresses are used to control the corresponding target devices, for
>>> example, 0x2f8 is for UART, 0xe4 is for ipmi-bt. It is different from the
>>> normal MMIO mode in using.
>>
>> This has nothing to do with arm64. Hardware with this kind of indirect
>> bus access could be integrated with a variety of CPU architectures. It
>> simply hasn't been, yet.
>
> On some ppc's we also use similar indirect access methods for IOs. We
> have a generic infrastructure for re-routing some memory or IO regions
> to hooks.
>
> On POWER8, our PCIe doesn't do IO at all, but we have an LPC bus behind
> firmware calls ;-) We use that infrastructure to plumb in the LPC bus.
>
Hi,
I would like to mention another topic on supporting LPC, and this is
regard to eSPI support.
eSPI is seen as the successor for LPC, and some BMCs already support it.
I had a chat with Arnd on this, and the idea to model LPC as a SPI bus
adpater (and also eSPI).
However it seems to me that most platforms will/should support eSPI as a
transparent bridge, same as LPC on x86. So I don't think that this is
much point in modelling LPC/eSPI as a bus.
So we shall continue with indriect-IO support...
Thanks,
John
^ permalink raw reply
* Re: [PATCH V2 2/2] pinctrl: tegra: Add driver to configure voltage and power of io pads
From: Laxman Dewangan @ 2016-11-21 12:49 UTC (permalink / raw)
To: Jon Hunter, linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
swarren-3lzwWm7+Weoh9ZMKESR00Q,
thierry.reding-Re5JQEeQqe8AvxtiuMwx3w
Cc: gnurou-Re5JQEeQqe8AvxtiuMwx3w,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-tegra-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <89eaabf1-c830-3ae9-5f34-a7f6d79e0816-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
On Monday 21 November 2016 04:38 PM, Jon Hunter wrote:
>>
>> I had a discussion with the ASIC on this and as per them
>> 1.8 V nominal is (1.62V, 1.98V)
>> 3.3 V nominal is (2.97V,3.63V)
>>
>> I am working with them to update the TRM document but we can assume that
>> this information will be there in TRM.
> My feeling is that if all use-cases today are using either 1.8V or 3.3V,
> then may be we should not worry about this and only support either 1.8V
> or 3.3V. I would be more in favour of supporting other voltages if there
> is a real need.
Sometimes, the regulator will not return exact 1.8V or 3.3V due to the
PMIC rail resolution. On such cases, it returns nearest voltage to 1.8V
or 3.3V.
That's why the PMIC resolution is considered through IO pad voltage
tolerances.
>
>>>> + const struct pinctrl_pin_desc *pins_desc;
>>>> + int num_pins_desc;
>>>> +};
>>>> +
>>>> +struct tegra_io_pads_regulator_info {
>>>> + struct device *dev;
>>>> + const struct tegra_io_pads_cfg_info *pads_cfg;
>>>> + struct regulator *regulator;
>>>> + struct notifier_block regulator_nb;
>>>> +};
>>> Is this struct necessary? Seems to be a lot of duplicated information
>>> from the other structs. Why not add the regulator and regulator_nb to
>>> the main struct? OK, not all io_pads have a regulator but you are only
>>> saving one pointer.
>> Yes, some of IO pads support multi-voltage.
> Yes, but I am saying why not put this information in the main struct and
> not bother having yet another struct where half of the information is
> duplicated.
For regulator notifier callback, we will need the driver data. If I keep
this in the main structure then I will not able to get proper structure
until I make that as global.
The notifier registration is
ret = devm_regulator_register_notifier(regulator,
&rinfo->regulator_nb);
and from the pointer of rinfo->regulator_nb, I will get the rinfo as
rinfo = container_of(nb, struct tegra_io_pads_regulator_info,
regulator_nb);
if I use this in main structure then I will not be able to get the
driver data.
>
>>> + if ((vdata->old_uV > TEGRA_IO_PAD_1800000UV_UPPER_LIMIT) &&
>>> + (vdata->min_uV <= TEGRA_IO_PAD_1800000UV_UPPER_LIMIT))
>>> + break;
>>> The data-sheet for Tegra210 only lists 1.8V or 3.3V as supported
>>> options. Do we need to support a range? Or does the h/w support a range
>>> of voltages? I am just wondering why we cannot check explicitly for 1.8V
>>> or 3.3V and treat anything else as an error.
>> Two voltage level, not range.
> Ok, then I think it would be much simpler if we just support the
> voltages we are using today.
Regulator resolution is only reason here to use tolerance.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox