Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH v2 5/7] mfd: pm8921: Use ssbi regmap

3 views
Skip to first unread message

Stephen Boyd

unread,
Dec 23, 2013, 3:50:03 PM12/23/13
to
Use a regmap so that the pm8xxx read/write APIs can be removed
once all consumer drivers are converted.

Cc: Mark Brown <bro...@kernel.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/Kconfig | 1 +
drivers/mfd/pm8921-core.c | 72 +++++++++++++++++++++++++++--------------------
2 files changed, 42 insertions(+), 31 deletions(-)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 35007ed..e4a3ae2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -455,6 +455,7 @@ config MFD_PM8921_CORE
depends on (ARCH_MSM || HEXAGON)
select MFD_CORE
select MFD_PM8XXX
+ select REGMAP
help
If you say yes to this option, support will be included for the
built-in PM8921 PMIC chip.
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 5d1d509..d13cb6e 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/ssbi.h>
+#include <linux/regmap.h>
#include <linux/of_platform.h>
#include <linux/mfd/core.h>
#include <linux/mfd/pm8xxx/core.h>
@@ -55,6 +56,7 @@

struct pm_irq_chip {
struct device *dev;
+ struct regmap *regmap;
spinlock_t pm_irq_lock;
struct irq_domain *domain;
unsigned int num_irqs;
@@ -68,29 +70,19 @@ struct pm8921 {
struct pm_irq_chip *irq_chip;
};

-static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
-{
- return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
-}
-
-static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
-{
- return pm8xxx_readb(chip->dev,
- SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
-}
-
-static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
+static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
+ unsigned int *ip)
{
int rc;

spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
if (rc) {
pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
goto bail;
}

- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
if (rc)
pr_err("Failed Reading Status rc=%d\n", rc);
bail:
@@ -98,19 +90,20 @@ bail:
return rc;
}

-static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
+static int
+pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp)
{
int rc;

spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
if (rc) {
pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
goto bail;
}

cp |= PM_IRQF_WRITE;
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_CONFIG, cp);
if (rc)
pr_err("Failed Configuring IRQ rc=%d\n", rc);
bail:
@@ -121,7 +114,7 @@ bail:
static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
{
int pmirq, irq, i, ret = 0;
- u8 bits;
+ unsigned int bits;

ret = pm8xxx_read_block_irq(chip, block, &bits);
if (ret) {
@@ -146,10 +139,11 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)

static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
{
- u8 blockbits;
+ unsigned int blockbits;
int block_number, i, ret = 0;

- ret = pm8xxx_read_master_irq(chip, master, &blockbits);
+ ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_M_STATUS1 + master,
+ &blockbits);
if (ret) {
pr_err("Failed to read master %d ret=%d\n", master, ret);
return ret;
@@ -171,12 +165,12 @@ static void pm8xxx_irq_handler(unsigned int irq, 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);
- u8 root;
+ unsigned int root;
int i, ret, masters = 0;

chained_irq_enter(irq_chip, desc);

- ret = pm8xxx_read_root_irq(chip, &root);
+ ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_ROOT, &root);
if (ret) {
pr_err("Can't read root status ret=%d\n", ret);
return;
@@ -281,7 +275,7 @@ static struct irq_chip pm8xxx_irq_chip = {
static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
{
int pmirq, rc;
- u8 block, bits, bit;
+ unsigned int block, bits, bit;
unsigned long flags;
struct irq_data *irq_data = irq_get_irq_data(irq);

@@ -292,14 +286,14 @@ static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)

spin_lock_irqsave(&chip->pm_irq_lock, flags);

- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
if (rc) {
pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
irq, pmirq, block, rc);
goto bail_out;
}

- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
if (rc) {
pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
irq, pmirq, block, rc);
@@ -337,7 +331,8 @@ static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
.map = pm8xxx_irq_domain_map,
};

-static int pm8xxx_irq_init(struct platform_device *pdev, unsigned int irq)
+static int pm8xxx_irq_init(struct platform_device *pdev, struct regmap *regmap,
+ unsigned int irq)
{
struct pm_irq_chip *chip;
unsigned int nirqs = 256;
@@ -347,7 +342,7 @@ static int pm8xxx_irq_init(struct platform_device *pdev, unsigned int irq)
if (!chip)
return -ENOMEM;

- chip->dev = &pdev->dev;
+ chip->regmap = regmap;
chip->num_irqs = nirqs;
chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
@@ -416,11 +411,21 @@ static struct pm8xxx_drvdata pm8921_drvdata = {
.pmic_read_irq_stat = pm8921_read_irq_stat,
};

+static const struct regmap_config ssbi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0x3ff,
+ .fast_io = true,
+ .reg_read = ssbi_reg_read,
+ .reg_write = ssbi_reg_write
+};
+
static int pm8921_probe(struct platform_device *pdev)
{
struct pm8921 *pmic;
+ struct regmap *regmap;
int rc;
- u8 val;
+ unsigned int val;
unsigned int irq;
u32 rev;

@@ -435,8 +440,13 @@ static int pm8921_probe(struct platform_device *pdev)
return -ENOMEM;
}

+ regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent,
+ &ssbi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
/* Read PMIC chip revision */
- rc = ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+ rc = regmap_read(regmap, REG_HWREV, &val);
if (rc) {
pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
return rc;
@@ -445,7 +455,7 @@ static int pm8921_probe(struct platform_device *pdev)
rev = val;

/* Read PMIC chip revision 2 */
- rc = ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+ rc = regmap_read(regmap, REG_HWREV_2, &val);
if (rc) {
pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
REG_HWREV_2, rc);
@@ -458,7 +468,7 @@ static int pm8921_probe(struct platform_device *pdev)
pm8921_drvdata.pm_chip_data = pmic;
platform_set_drvdata(pdev, &pm8921_drvdata);

- rc = pm8xxx_irq_init(pdev, irq);
+ rc = pm8xxx_irq_init(pdev, regmap, irq);
if (rc)
return rc;

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Stephen Boyd

unread,
Dec 23, 2013, 3:50:02 PM12/23/13
to
Add read and write helper functions that the pm8921-core driver
can use to read and write ssbi regsiters via a "no-bus" regmap.

Cc: Mark Brown <bro...@kernel.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/ssbi.c | 13 +++++++++++++
include/linux/ssbi.h | 3 +++
2 files changed, 16 insertions(+)

diff --git a/drivers/mfd/ssbi.c b/drivers/mfd/ssbi.c
index b78942e..c5d270f 100644
--- a/drivers/mfd/ssbi.c
+++ b/drivers/mfd/ssbi.c
@@ -269,6 +269,19 @@ int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len)
}
EXPORT_SYMBOL_GPL(ssbi_write);

+int ssbi_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ *val = 0;
+ return ssbi_read(context, reg, (u8 *)val, 1);
+}
+EXPORT_SYMBOL_GPL(ssbi_reg_read);
+
+int ssbi_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ return ssbi_write(context, reg, (u8 *)&val, 1);
+}
+EXPORT_SYMBOL_GPL(ssbi_reg_write);
+
static int ssbi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
diff --git a/include/linux/ssbi.h b/include/linux/ssbi.h
index bcbb642..b862f3d 100644
--- a/include/linux/ssbi.h
+++ b/include/linux/ssbi.h
@@ -20,4 +20,7 @@
int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len);
int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len);

+int ssbi_reg_read(void *context, unsigned int reg, unsigned int *val);
+int ssbi_reg_write(void *context, unsigned int reg, unsigned int val);
+
#endif

Stephen Boyd

unread,
Dec 23, 2013, 3:50:03 PM12/23/13
to
These patches lay the groundwork for converting the pm8921 sub-devices
to devicetree as well as simplify the API by migrating the core code
to use the regmap API instead of the custom pm8xxx read/write wrapper.

Changes since v1:
* First 3 cleanup patches dropped because they're applied upstream
* New regmap read/write helpers
* New patch for DT match table
* New binding document

Stephen Boyd (7):
mfd: Move pm8xxx-irq.c contents into only driver that uses it
mfd: pm8921: Update for genirq changes
mfd: pm8921: Migrate to irqdomains
mfd: ssbi: Add regmap read/write helpers
mfd: pm8921: Use ssbi regmap
mfd: pm8921: Add DT match table
devicetree: bindings: Document PM8921/8058 PMICs

.../devicetree/bindings/mfd/qcom,pm8xxx.txt | 63 +++
drivers/mfd/Kconfig | 12 +-
drivers/mfd/pm8921-core.c | 424 ++++++++++++++++++---
drivers/mfd/pm8xxx-irq.c | 371 ------------------
drivers/mfd/ssbi.c | 13 +
include/linux/mfd/pm8xxx/irq.h | 59 ---
include/linux/mfd/pm8xxx/pm8921.h | 30 --
include/linux/ssbi.h | 3 +
8 files changed, 449 insertions(+), 526 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
delete mode 100644 drivers/mfd/pm8xxx-irq.c
delete mode 100644 include/linux/mfd/pm8xxx/irq.h
delete mode 100644 include/linux/mfd/pm8xxx/pm8921.h

Stephen Boyd

unread,
Dec 23, 2013, 3:50:03 PM12/23/13
to
Convert this driver to use irqdomains so that the PMIC's child
devices can be converted to devicetree.

Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/pm8921-core.c | 184 ++++++++++++++------------------------
include/linux/mfd/pm8xxx/irq.h | 36 --------
include/linux/mfd/pm8xxx/pm8921.h | 30 -------
3 files changed, 65 insertions(+), 185 deletions(-)
delete mode 100644 include/linux/mfd/pm8xxx/irq.h
delete mode 100644 include/linux/mfd/pm8xxx/pm8921.h

diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 7964932..5d1d509 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -17,15 +17,15 @@
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/ssbi.h>
+#include <linux/of_platform.h>
#include <linux/mfd/core.h>
-#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/irq.h>

#define SSBI_REG_ADDR_IRQ_BASE 0x1BB

@@ -56,8 +56,7 @@
struct pm_irq_chip {
struct device *dev;
spinlock_t pm_irq_lock;
- unsigned int devirq;
- unsigned int irq_base;
+ struct irq_domain *domain;
unsigned int num_irqs;
unsigned int num_blocks;
unsigned int num_masters;
@@ -138,7 +137,7 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
for (i = 0; i < 8; i++) {
if (bits & (1 << i)) {
pmirq = block * 8 + i;
- irq = pmirq + chip->irq_base;
+ irq = irq_find_mapping(chip->domain, pmirq);
generic_handle_irq(irq);
}
}
@@ -197,12 +196,11 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
static void pm8xxx_irq_mask_ack(struct irq_data *d)
{
struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
u8 block, config;

block = pmirq / 8;
- master = block / 8;
irq_bit = pmirq % 8;

config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
@@ -212,12 +210,11 @@ static void pm8xxx_irq_mask_ack(struct irq_data *d)
static void pm8xxx_irq_unmask(struct irq_data *d)
{
struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
u8 block, config;

block = pmirq / 8;
- master = block / 8;
irq_bit = pmirq % 8;

config = chip->config[pmirq];
@@ -227,12 +224,11 @@ static void pm8xxx_irq_unmask(struct irq_data *d)
static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
u8 block, config;

block = pmirq / 8;
- master = block / 8;
irq_bit = pmirq % 8;

chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
@@ -287,12 +283,9 @@ static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
int pmirq, rc;
u8 block, bits, bit;
unsigned long flags;
+ struct irq_data *irq_data = irq_get_irq_data(irq);

- if (chip == NULL || irq < chip->irq_base ||
- irq >= chip->irq_base + chip->num_irqs)
- return -EINVAL;
-
- pmirq = irq - chip->irq_base;
+ pmirq = irq_data->hwirq;

block = pmirq / 8;
bit = pmirq % 8;
@@ -321,64 +314,55 @@ bail_out:
return rc;
}

-static struct pm_irq_chip *pm8xxx_irq_init(struct device *dev,
- const struct pm8xxx_irq_platform_data *pdata)
+static struct lock_class_key pm8xxx_irq_lock_class;
+
+static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
{
- struct pm_irq_chip *chip;
- int devirq, rc;
- unsigned int pmirq;
+ struct pm_irq_chip *chip = d->host_data;

- if (!pdata) {
- pr_err("No platform data\n");
- return ERR_PTR(-EINVAL);
- }
+ irq_set_lockdep_class(irq, &pm8xxx_irq_lock_class);
+ irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, chip);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ return 0;
+}

- devirq = pdata->devirq;
- if (devirq < 0) {
- pr_err("missing devirq\n");
- rc = devirq;
- return ERR_PTR(-EINVAL);
- }
+static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
+ .xlate = irq_domain_xlate_twocell,
+ .map = pm8xxx_irq_domain_map,
+};

- chip = kzalloc(sizeof(struct pm_irq_chip)
- + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
- if (!chip) {
- pr_err("Cannot alloc pm_irq_chip struct\n");
- return ERR_PTR(-EINVAL);
- }
+static int pm8xxx_irq_init(struct platform_device *pdev, unsigned int irq)
+{
+ struct pm_irq_chip *chip;
+ unsigned int nirqs = 256;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip) + sizeof(u8) * nirqs,
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;

- chip->dev = dev;
- chip->devirq = devirq;
- chip->irq_base = pdata->irq_base;
- chip->num_irqs = pdata->irq_cdata.nirqs;
+ chip->dev = &pdev->dev;
+ chip->num_irqs = nirqs;
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);

- for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
- irq_set_chip_and_handler(chip->irq_base + pmirq,
- &pm8xxx_irq_chip,
- handle_level_irq);
- irq_set_chip_data(chip->irq_base + pmirq, chip);
-#ifdef CONFIG_ARM
- set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
-#else
- irq_set_noprobe(chip->irq_base + pmirq);
-#endif
- }
+ chip->domain = irq_domain_add_linear(pdev->dev.of_node, nirqs,
+ &pm8xxx_irq_domain_ops,
+ chip);
+ if (!chip->domain)
+ return -ENODEV;

- irq_set_irq_type(devirq, pdata->irq_trigger_flag);
- irq_set_handler_data(devirq, chip);
- irq_set_chained_handler(devirq, pm8xxx_irq_handler);
- irq_set_irq_wake(devirq, 1);
+ irq_set_handler_data(irq, chip);
+ irq_set_chained_handler(irq, pm8xxx_irq_handler);
+ irq_set_irq_wake(irq, 1);

- return chip;
-}
-
-static int pm8xxx_irq_exit(struct pm_irq_chip *chip)
-{
- irq_set_chained_handler(chip->devirq, NULL);
- kfree(chip);
return 0;
}

@@ -432,42 +416,18 @@ static struct pm8xxx_drvdata pm8921_drvdata = {
.pmic_read_irq_stat = pm8921_read_irq_stat,
};

-static int pm8921_add_subdevices(const struct pm8921_platform_data
- *pdata,
- struct pm8921 *pmic,
- u32 rev)
-{
- int ret = 0, irq_base = 0;
- struct pm_irq_chip *irq_chip;
-
- if (pdata->irq_pdata) {
- pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
- pdata->irq_pdata->irq_cdata.rev = rev;
- irq_base = pdata->irq_pdata->irq_base;
- irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
-
- if (IS_ERR(irq_chip)) {
- pr_err("Failed to init interrupts ret=%ld\n",
- PTR_ERR(irq_chip));
- return PTR_ERR(irq_chip);
- }
- pmic->irq_chip = irq_chip;
- }
- return ret;
-}
-
static int pm8921_probe(struct platform_device *pdev)
{
- const struct pm8921_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct pm8921 *pmic;
int rc;
u8 val;
+ unsigned int irq;
u32 rev;

- if (!pdata) {
- pr_err("missing platform data\n");
- return -EINVAL;
- }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;

pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8921), GFP_KERNEL);
if (!pmic) {
@@ -498,37 +458,23 @@ static int pm8921_probe(struct platform_device *pdev)
pm8921_drvdata.pm_chip_data = pmic;
platform_set_drvdata(pdev, &pm8921_drvdata);

- rc = pm8921_add_subdevices(pdata, pmic, rev);
- if (rc) {
- pr_err("Cannot add subdevices rc=%d\n", rc);
- goto err;
- }
+ rc = pm8xxx_irq_init(pdev, irq);
+ if (rc)
+ return rc;

- /* gpio might not work if no irq device is found */
- WARN_ON(pmic->irq_chip == NULL);
+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+}

+static int pm8921_remove_child(struct device *dev, void *unused)
+{
+ platform_device_unregister(to_platform_device(dev));
return 0;
-
-err:
- mfd_remove_devices(pmic->dev);
- return rc;
}

static int pm8921_remove(struct platform_device *pdev)
{
- struct pm8xxx_drvdata *drvdata;
- struct pm8921 *pmic = NULL;
-
- drvdata = platform_get_drvdata(pdev);
- if (drvdata)
- pmic = drvdata->pm_chip_data;
- if (pmic)
- mfd_remove_devices(pmic->dev);
- if (pmic->irq_chip) {
- pm8xxx_irq_exit(pmic->irq_chip);
- pmic->irq_chip = NULL;
- }
-
+ device_for_each_child(&pdev->dev, NULL, pm8921_remove_child);
+ irq_set_chained_handler(platform_get_irq(pdev, 0), NULL);
return 0;
}

diff --git a/include/linux/mfd/pm8xxx/irq.h b/include/linux/mfd/pm8xxx/irq.h
deleted file mode 100644
index 1a11b02..0000000
--- a/include/linux/mfd/pm8xxx/irq.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
-/*
- * Qualcomm PMIC irq 8xxx driver header file
- *
- */
-
-#ifndef __MFD_PM8XXX_IRQ_H
-#define __MFD_PM8XXX_IRQ_H
-
-#include <linux/errno.h>
-#include <linux/err.h>
-
-struct pm8xxx_irq_core_data {
- u32 rev;
- int nirqs;
-};
-
-struct pm8xxx_irq_platform_data {
- int irq_base;
- struct pm8xxx_irq_core_data irq_cdata;
- int devirq;
- int irq_trigger_flag;
-};
-
-#endif /* __MFD_PM8XXX_IRQ_H */
diff --git a/include/linux/mfd/pm8xxx/pm8921.h b/include/linux/mfd/pm8xxx/pm8921.h
deleted file mode 100644
index 00fa3de..0000000
--- a/include/linux/mfd/pm8xxx/pm8921.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
-/*
- * Qualcomm PMIC 8921 driver header file
- *
- */
-
-#ifndef __MFD_PM8921_H
-#define __MFD_PM8921_H
-
-#include <linux/mfd/pm8xxx/irq.h>
-
-#define PM8921_NR_IRQS 256
-
-struct pm8921_platform_data {
- int irq_base;
- struct pm8xxx_irq_platform_data *irq_pdata;
-};
-
-#endif

Stephen Boyd

unread,
Dec 23, 2013, 3:50:04 PM12/23/13
to
Since this code has been marked broken for some time a few genirq
tree wide changes weren't made. set_irq_wake() was renamed to
irq_set_irq_wake() in commit a0cd9ca2b (genirq: Namespace
cleanup, 2011-02-10) and commit 10a8c383 (irq: introduce entry
and exit functions for chained handlers) introduced the chained
irq functions but this driver wasn't updated to use them. Fix
these problems and remove the BROKEN marking on this driver.

Acked-by: Lee Jones <lee....@linaro.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/Kconfig | 1 -
drivers/mfd/pm8921-core.c | 7 +++++--
2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 9ee4ce6..35007ed 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -453,7 +453,6 @@ config MFD_PM8XXX
config MFD_PM8921_CORE
tristate "Qualcomm PM8921 PMIC chip"
depends on (ARCH_MSM || HEXAGON)
- depends on BROKEN
select MFD_CORE
select MFD_PM8XXX
help
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 669f8f2..7964932 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -15,6 +15,7 @@

#include <linux/kernel.h>
#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -174,6 +175,8 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
u8 root;
int i, ret, masters = 0;

+ chained_irq_enter(irq_chip, desc);
+
ret = pm8xxx_read_root_irq(chip, &root);
if (ret) {
pr_err("Can't read root status ret=%d\n", ret);
@@ -188,7 +191,7 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
if (masters & (1 << i))
pm8xxx_irq_master_handler(chip, i);

- irq_chip->irq_ack(&desc->irq_data);
+ chained_irq_exit(irq_chip, desc);
}

static void pm8xxx_irq_mask_ack(struct irq_data *d)
@@ -367,7 +370,7 @@ static struct pm_irq_chip *pm8xxx_irq_init(struct device *dev,
irq_set_irq_type(devirq, pdata->irq_trigger_flag);
irq_set_handler_data(devirq, chip);
irq_set_chained_handler(devirq, pm8xxx_irq_handler);
- set_irq_wake(devirq, 1);
+ irq_set_irq_wake(devirq, 1);

return chip;

Stephen Boyd

unread,
Dec 23, 2013, 3:50:04 PM12/23/13
to
PM8921 and PM8058 are PMICs found paired with MSM8960 and MSM8660
devices respectively. They contain subdevices such as keypads,
RTC, regulators, clocks, etc.

Cc: <devic...@vger.kernel.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
.../devicetree/bindings/mfd/qcom,pm8xxx.txt | 63 ++++++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt

diff --git a/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt b/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
new file mode 100644
index 0000000..e3fe625
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
@@ -0,0 +1,63 @@
+Qualcomm PM8xxx PMIC multi-function devices
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,pm8058"
+ "qcom,pm8921"
+
+- #address-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 1
+
+- #size-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 0
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: specifies the interrupt that indicates a subdevice
+ has generated an interrupt (summary interrupt). The
+ format of the specifier is defined by the binding document
+ describing the node's interrupt parent.
+
+- #interrupt-cells:
+ Usage: required
+ Value type : <u32>
+ Definition: must be 2. Specifies the number of cells needed to encode
+ an interrupt source. The 1st cell contains the interrupt
+ number. The 2nd cell is the trigger type and level flags
+ encoded as follows:
+
+ 1 = low-to-high edge triggered
+ 2 = high-to-low edge triggered
+ 4 = active high level-sensitive
+ 8 = active low level-sensitive
+
+- interrupt-controller:
+ Usage: required
+ Value type: <empty>
+ Definition: identifies this node as an interrupt controller
+
+EXAMPLE
+
+ pmicintc: pmic@0 {
+ compatible = "qcom,pm8921";
+ interrupts = <104 8>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pwrkey {
+ compatible = "qcom,pm8921-pwrkey";
+ interrupt-parent = <&pmicintc>;
+ interrupts = <50 1>, <51 1>;
+ };
+ };

Stephen Boyd

unread,
Dec 23, 2013, 3:50:02 PM12/23/13
to
The pm8xxx-irq.c code is practically mandatory given that the
pm8921-core driver will WARN about it missing and the Kconfig
marks it as default y when a PM8xxx chips is enabled. The only
reason the file was split out was because we planned to support
other pm8xxx chips with different pm8xxx-core.c files. Now that
we have DT on ARM this isn't necessary because we should be able
to support all the ssbi based PM8xxx chips in one driver and one
file with no data bloat. Let's move this code into the only
driver that uses it right now (pm8921) so that it's always compiled when
needed. In the future we can rename pm8921-core.c to something
more generic.

Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/Kconfig | 10 --
drivers/mfd/pm8921-core.c | 348 ++++++++++++++++++++++++++++++++++++++
drivers/mfd/pm8xxx-irq.c | 371 -----------------------------------------
include/linux/mfd/pm8xxx/irq.h | 23 ---
4 files changed, 348 insertions(+), 404 deletions(-)
delete mode 100644 drivers/mfd/pm8xxx-irq.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 914c3d1..9ee4ce6 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -466,16 +466,6 @@ 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_PM8XXX_IRQ
- bool "Qualcomm PM8xxx IRQ features"
- depends on MFD_PM8XXX
- default y if MFD_PM8XXX
- help
- This is the IRQ driver for Qualcomm PM 8xxx PMIC chips.
-
- This is required to use certain other PM 8xxx features, such as GPIO
- and MPP.
-
config MFD_RDC321X
tristate "RDC R-321x southbridge"
select MFD_CORE
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index a6841f7..669f8f2 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -14,6 +14,8 @@
#define pr_fmt(fmt) "%s: " fmt, __func__

#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -22,15 +24,361 @@
#include <linux/mfd/core.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/irq.h>
+
+#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
+
+#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
+#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
+#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
+#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
+#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
+#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
+#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
+#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 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 */
+#define PM_IRQF_CLR 0x08 /* clear interrupt */
+#define PM_IRQF_BITS_MASK 0x70
+#define PM_IRQF_BITS_SHIFT 4
+#define PM_IRQF_WRITE 0x80
+
+#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
+ PM_IRQF_MASK_RE)

#define REG_HWREV 0x002 /* PMIC4 revision */
#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */

+struct pm_irq_chip {
+ struct device *dev;
+ spinlock_t pm_irq_lock;
+ unsigned int devirq;
+ unsigned int irq_base;
+ unsigned int num_irqs;
+ unsigned int num_blocks;
+ unsigned int num_masters;
+ u8 config[0];
+};
+
struct pm8921 {
struct device *dev;
struct pm_irq_chip *irq_chip;
};

+static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
+{
+ return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
+}
+
+static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
+{
+ return pm8xxx_readb(chip->dev,
+ SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
+}
+
+static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
+{
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
+
+ rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
+ if (rc)
+ pr_err("Failed Reading Status rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
+}
+
+static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
+{
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
+
+ cp |= PM_IRQF_WRITE;
+ rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+ if (rc)
+ pr_err("Failed Configuring IRQ rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
+}
+
+static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
+{
+ int pmirq, irq, i, ret = 0;
+ u8 bits;
+
+ ret = pm8xxx_read_block_irq(chip, block, &bits);
+ if (ret) {
+ pr_err("Failed reading %d block ret=%d", block, ret);
+ return ret;
+ }
+ if (!bits) {
+ pr_err("block bit set in master but no irqs: %d", block);
+ return 0;
+ }
+
+ /* Check IRQ bits */
+ for (i = 0; i < 8; i++) {
+ if (bits & (1 << i)) {
+ pmirq = block * 8 + i;
+ irq = pmirq + chip->irq_base;
+ generic_handle_irq(irq);
+ }
+ }
+ return 0;
+}
+
+static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
+{
+ u8 blockbits;
+ int block_number, i, ret = 0;
+
+ ret = pm8xxx_read_master_irq(chip, master, &blockbits);
+ if (ret) {
+ pr_err("Failed to read master %d ret=%d\n", master, ret);
+ return ret;
+ }
+ if (!blockbits) {
+ pr_err("master bit set in root but no blocks: %d", master);
+ return 0;
+ }
+
+ for (i = 0; i < 8; i++)
+ if (blockbits & (1 << i)) {
+ block_number = master * 8 + i; /* block # */
+ ret |= pm8xxx_irq_block_handler(chip, block_number);
+ }
+ return ret;
+}
+
+static void pm8xxx_irq_handler(unsigned int irq, 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);
+ u8 root;
+ int i, ret, masters = 0;
+
+ ret = pm8xxx_read_root_irq(chip, &root);
+ if (ret) {
+ pr_err("Can't read root status ret=%d\n", ret);
+ return;
+ }
+
+ /* on pm8xxx series masters start from bit 1 of the root */
+ masters = root >> 1;
+
+ /* Read allowed masters for blocks. */
+ for (i = 0; i < chip->num_masters; i++)
+ if (masters & (1 << i))
+ pm8xxx_irq_master_handler(chip, i);
+
+ irq_chip->irq_ack(&desc->irq_data);
+}
+
+static void pm8xxx_irq_mask_ack(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = d->irq - chip->irq_base;
+ int master, irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ master = block / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static void pm8xxx_irq_unmask(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = d->irq - chip->irq_base;
+ int master, irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ master = block / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq];
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = d->irq - chip->irq_base;
+ int master, irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ master = block / 8;
+ irq_bit = pmirq % 8;
+
+ chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
+ | PM_IRQF_MASK_ALL;
+ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+ if (flow_type & IRQF_TRIGGER_RISING)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+ if (flow_type & IRQF_TRIGGER_FALLING)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+ } else {
+ chip->config[pmirq] |= PM_IRQF_LVL_SEL;
+
+ if (flow_type & IRQF_TRIGGER_HIGH)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+ else
+ chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+ }
+
+ config = chip->config[pmirq] | PM_IRQF_CLR;
+ return pm8xxx_config_irq(chip, block, config);
+}
+
+static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ return 0;
+}
+
+static struct irq_chip pm8xxx_irq_chip = {
+ .name = "pm8xxx",
+ .irq_mask_ack = pm8xxx_irq_mask_ack,
+ .irq_unmask = pm8xxx_irq_unmask,
+ .irq_set_type = pm8xxx_irq_set_type,
+ .irq_set_wake = pm8xxx_irq_set_wake,
+ .flags = IRQCHIP_MASK_ON_SUSPEND,
+};
+
+/**
+ * pm8xxx_get_irq_stat - get the status of the irq line
+ * @chip: pointer to identify a pmic irq controller
+ * @irq: the irq number
+ *
+ * The pm8xxx gpio and mpp rely on the interrupt block to read
+ * the values on their pins. This function is to facilitate reading
+ * the status of a gpio or an mpp line. The caller has to convert the
+ * gpio number to irq number.
+ *
+ * RETURNS:
+ * an int indicating the value read on that line
+ */
+static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
+{
+ int pmirq, rc;
+ u8 block, bits, bit;
+ unsigned long flags;
+
+ if (chip == NULL || irq < chip->irq_base ||
+ irq >= chip->irq_base + chip->num_irqs)
+ return -EINVAL;
+
+ pmirq = irq - chip->irq_base;
+
+ block = pmirq / 8;
+ bit = pmirq % 8;
+
+ spin_lock_irqsave(&chip->pm_irq_lock, flags);
+
+ rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+ if (rc) {
+ pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
+ irq, pmirq, block, rc);
+ goto bail_out;
+ }
+
+ rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+ if (rc) {
+ pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
+ irq, pmirq, block, rc);
+ goto bail_out;
+ }
+
+ rc = (bits & (1 << bit)) ? 1 : 0;
+
+bail_out:
+ spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
+
+ return rc;
+}
+
+static struct pm_irq_chip *pm8xxx_irq_init(struct device *dev,
+ const struct pm8xxx_irq_platform_data *pdata)
+{
+ struct pm_irq_chip *chip;
+ int devirq, rc;
+ unsigned int pmirq;
+
+ if (!pdata) {
+ pr_err("No platform data\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ devirq = pdata->devirq;
+ if (devirq < 0) {
+ pr_err("missing devirq\n");
+ rc = devirq;
+ return ERR_PTR(-EINVAL);
+ }
+
+ chip = kzalloc(sizeof(struct pm_irq_chip)
+ + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
+ if (!chip) {
+ pr_err("Cannot alloc pm_irq_chip struct\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ chip->dev = dev;
+ chip->devirq = devirq;
+ chip->irq_base = pdata->irq_base;
+ chip->num_irqs = pdata->irq_cdata.nirqs;
+ 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);
+
+ for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
+ irq_set_chip_and_handler(chip->irq_base + pmirq,
+ &pm8xxx_irq_chip,
+ handle_level_irq);
+ irq_set_chip_data(chip->irq_base + pmirq, chip);
+#ifdef CONFIG_ARM
+ set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
+#else
+ irq_set_noprobe(chip->irq_base + pmirq);
+#endif
+ }
+
+ irq_set_irq_type(devirq, pdata->irq_trigger_flag);
+ irq_set_handler_data(devirq, chip);
+ irq_set_chained_handler(devirq, pm8xxx_irq_handler);
+ set_irq_wake(devirq, 1);
+
+ return chip;
+}
+
+static int pm8xxx_irq_exit(struct pm_irq_chip *chip)
+{
+ irq_set_chained_handler(chip->devirq, NULL);
+ kfree(chip);
+ return 0;
+}
+
static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
{
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c
deleted file mode 100644
index 1360e20..0000000
--- a/drivers/mfd/pm8xxx-irq.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
-
-#define pr_fmt(fmt) "%s: " fmt, __func__
-
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/irq.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-/* PMIC8xxx IRQ */
-
-#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
-
-#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
-#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
-#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
-#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
-#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
-#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
-#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
-#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 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 */
-#define PM_IRQF_CLR 0x08 /* clear interrupt */
-#define PM_IRQF_BITS_MASK 0x70
-#define PM_IRQF_BITS_SHIFT 4
-#define PM_IRQF_WRITE 0x80
-
-#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
- PM_IRQF_MASK_RE)
-
-struct pm_irq_chip {
- struct device *dev;
- spinlock_t pm_irq_lock;
- unsigned int devirq;
- unsigned int irq_base;
- unsigned int num_irqs;
- unsigned int num_blocks;
- unsigned int num_masters;
- u8 config[0];
-};
-
-static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
-{
- return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
-}
-
-static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
-{
- return pm8xxx_readb(chip->dev,
- SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
-}
-
-static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
-{
- int rc;
-
- spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
- if (rc) {
- pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
- goto bail;
- }
-
- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
- if (rc)
- pr_err("Failed Reading Status rc=%d\n", rc);
-bail:
- spin_unlock(&chip->pm_irq_lock);
- return rc;
-}
-
-static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
-{
- int rc;
-
- spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
- if (rc) {
- pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
- goto bail;
- }
-
- cp |= PM_IRQF_WRITE;
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
- if (rc)
- pr_err("Failed Configuring IRQ rc=%d\n", rc);
-bail:
- spin_unlock(&chip->pm_irq_lock);
- return rc;
-}
-
-static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
-{
- int pmirq, irq, i, ret = 0;
- u8 bits;
-
- ret = pm8xxx_read_block_irq(chip, block, &bits);
- if (ret) {
- pr_err("Failed reading %d block ret=%d", block, ret);
- return ret;
- }
- if (!bits) {
- pr_err("block bit set in master but no irqs: %d", block);
- return 0;
- }
-
- /* Check IRQ bits */
- for (i = 0; i < 8; i++) {
- if (bits & (1 << i)) {
- pmirq = block * 8 + i;
- irq = pmirq + chip->irq_base;
- generic_handle_irq(irq);
- }
- }
- return 0;
-}
-
-static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
-{
- u8 blockbits;
- int block_number, i, ret = 0;
-
- ret = pm8xxx_read_master_irq(chip, master, &blockbits);
- if (ret) {
- pr_err("Failed to read master %d ret=%d\n", master, ret);
- return ret;
- }
- if (!blockbits) {
- pr_err("master bit set in root but no blocks: %d", master);
- return 0;
- }
-
- for (i = 0; i < 8; i++)
- if (blockbits & (1 << i)) {
- block_number = master * 8 + i; /* block # */
- ret |= pm8xxx_irq_block_handler(chip, block_number);
- }
- return ret;
-}
-
-static void pm8xxx_irq_handler(unsigned int irq, 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);
- u8 root;
- int i, ret, masters = 0;
-
- ret = pm8xxx_read_root_irq(chip, &root);
- if (ret) {
- pr_err("Can't read root status ret=%d\n", ret);
- return;
- }
-
- /* on pm8xxx series masters start from bit 1 of the root */
- masters = root >> 1;
-
- /* Read allowed masters for blocks. */
- for (i = 0; i < chip->num_masters; i++)
- if (masters & (1 << i))
- pm8xxx_irq_master_handler(chip, i);
-
- irq_chip->irq_ack(&desc->irq_data);
-}
-
-static void pm8xxx_irq_mask_ack(struct irq_data *d)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
- pm8xxx_config_irq(chip, block, config);
-}
-
-static void pm8xxx_irq_unmask(struct irq_data *d)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- config = chip->config[pmirq];
- pm8xxx_config_irq(chip, block, config);
-}
-
-static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
- | PM_IRQF_MASK_ALL;
- if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
- if (flow_type & IRQF_TRIGGER_RISING)
- chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
- if (flow_type & IRQF_TRIGGER_FALLING)
- chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
- } else {
- chip->config[pmirq] |= PM_IRQF_LVL_SEL;
-
- if (flow_type & IRQF_TRIGGER_HIGH)
- chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
- else
- chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
- }
-
- config = chip->config[pmirq] | PM_IRQF_CLR;
- return pm8xxx_config_irq(chip, block, config);
-}
-
-static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
-{
- return 0;
-}
-
-static struct irq_chip pm8xxx_irq_chip = {
- .name = "pm8xxx",
- .irq_mask_ack = pm8xxx_irq_mask_ack,
- .irq_unmask = pm8xxx_irq_unmask,
- .irq_set_type = pm8xxx_irq_set_type,
- .irq_set_wake = pm8xxx_irq_set_wake,
- .flags = IRQCHIP_MASK_ON_SUSPEND,
-};
-
-/**
- * pm8xxx_get_irq_stat - get the status of the irq line
- * @chip: pointer to identify a pmic irq controller
- * @irq: the irq number
- *
- * The pm8xxx gpio and mpp rely on the interrupt block to read
- * the values on their pins. This function is to facilitate reading
- * the status of a gpio or an mpp line. The caller has to convert the
- * gpio number to irq number.
- *
- * RETURNS:
- * an int indicating the value read on that line
- */
-int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
-{
- int pmirq, rc;
- u8 block, bits, bit;
- unsigned long flags;
-
- if (chip == NULL || irq < chip->irq_base ||
- irq >= chip->irq_base + chip->num_irqs)
- return -EINVAL;
-
- pmirq = irq - chip->irq_base;
-
- block = pmirq / 8;
- bit = pmirq % 8;
-
- spin_lock_irqsave(&chip->pm_irq_lock, flags);
-
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
- if (rc) {
- pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
- irq, pmirq, block, rc);
- goto bail_out;
- }
-
- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
- if (rc) {
- pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
- irq, pmirq, block, rc);
- goto bail_out;
- }
-
- rc = (bits & (1 << bit)) ? 1 : 0;
-
-bail_out:
- spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat);
-
-struct pm_irq_chip * pm8xxx_irq_init(struct device *dev,
- const struct pm8xxx_irq_platform_data *pdata)
-{
- struct pm_irq_chip *chip;
- int devirq, rc;
- unsigned int pmirq;
-
- if (!pdata) {
- pr_err("No platform data\n");
- return ERR_PTR(-EINVAL);
- }
-
- devirq = pdata->devirq;
- if (devirq < 0) {
- pr_err("missing devirq\n");
- rc = devirq;
- return ERR_PTR(-EINVAL);
- }
-
- chip = kzalloc(sizeof(struct pm_irq_chip)
- + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
- if (!chip) {
- pr_err("Cannot alloc pm_irq_chip struct\n");
- return ERR_PTR(-EINVAL);
- }
-
- chip->dev = dev;
- chip->devirq = devirq;
- chip->irq_base = pdata->irq_base;
- chip->num_irqs = pdata->irq_cdata.nirqs;
- 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);
-
- for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
- irq_set_chip_and_handler(chip->irq_base + pmirq,
- &pm8xxx_irq_chip,
- handle_level_irq);
- irq_set_chip_data(chip->irq_base + pmirq, chip);
-#ifdef CONFIG_ARM
- set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
-#else
- irq_set_noprobe(chip->irq_base + pmirq);
-#endif
- }
-
- irq_set_irq_type(devirq, pdata->irq_trigger_flag);
- irq_set_handler_data(devirq, chip);
- irq_set_chained_handler(devirq, pm8xxx_irq_handler);
- set_irq_wake(devirq, 1);
-
- return chip;
-}
-
-int pm8xxx_irq_exit(struct pm_irq_chip *chip)
-{
- irq_set_chained_handler(chip->devirq, NULL);
- kfree(chip);
- return 0;
-}
diff --git a/include/linux/mfd/pm8xxx/irq.h b/include/linux/mfd/pm8xxx/irq.h
index f83d6b4..1a11b02 100644
--- a/include/linux/mfd/pm8xxx/irq.h
+++ b/include/linux/mfd/pm8xxx/irq.h
@@ -33,27 +33,4 @@ struct pm8xxx_irq_platform_data {
int irq_trigger_flag;
};

-struct pm_irq_chip;
-
-#ifdef CONFIG_MFD_PM8XXX_IRQ
-int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq);
-struct pm_irq_chip *pm8xxx_irq_init(struct device *dev,
- const struct pm8xxx_irq_platform_data *pdata);
-int pm8xxx_irq_exit(struct pm_irq_chip *chip);
-#else
-static inline int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
-{
- return -ENXIO;
-}
-static inline struct pm_irq_chip *pm8xxx_irq_init(
- const struct device *dev,
- const struct pm8xxx_irq_platform_data *pdata)
-{
- return ERR_PTR(-ENXIO);
-}
-static inline int pm8xxx_irq_exit(struct pm_irq_chip *chip)
-{
- return -ENXIO;
-}
-#endif /* CONFIG_MFD_PM8XXX_IRQ */
#endif /* __MFD_PM8XXX_IRQ_H */

Stephen Boyd

unread,
Dec 23, 2013, 3:50:02 PM12/23/13
to
Allow this driver to probe based on devicetree.

Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/pm8921-core.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index d13cb6e..290f0da 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -420,6 +420,13 @@ static const struct regmap_config ssbi_regmap_config = {
.reg_write = ssbi_reg_write
};

+static const struct of_device_id pm8921_id_table[] = {
+ { .compatible = "qcom,pm8058", },
+ { .compatible = "qcom,pm8921", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm8921_id_table);
+
static int pm8921_probe(struct platform_device *pdev)
{
struct pm8921 *pmic;
@@ -429,7 +436,6 @@ static int pm8921_probe(struct platform_device *pdev)
unsigned int irq;
u32 rev;

-
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
@@ -494,6 +500,7 @@ static struct platform_driver pm8921_driver = {
.driver = {
.name = "pm8921-core",
.owner = THIS_MODULE,
+ .of_match_table = pm8921_id_table,
},
};

Mark Brown

unread,
Dec 24, 2013, 8:00:02 AM12/24/13
to
On Mon, Dec 23, 2013 at 12:46:00PM -0800, Stephen Boyd wrote:

> +int ssbi_reg_read(void *context, unsigned int reg, unsigned int *val)
> +{
> + *val = 0;
> + return ssbi_read(context, reg, (u8 *)val, 1);
> +}
> +EXPORT_SYMBOL_GPL(ssbi_reg_read);
> +
> +int ssbi_reg_write(void *context, unsigned int reg, unsigned int val)
> +{
> + return ssbi_write(context, reg, (u8 *)&val, 1);
> +}
> +EXPORT_SYMBOL_GPL(ssbi_reg_write);

Not a big deal but could these just be inlined in the headers?

Stephen Boyd

unread,
Dec 26, 2013, 2:50:01 PM12/26/13
to
On 12/24, Mark Brown wrote:
> On Mon, Dec 23, 2013 at 12:46:00PM -0800, Stephen Boyd wrote:
>
> > +int ssbi_reg_read(void *context, unsigned int reg, unsigned int *val)
> > +{
> > + *val = 0;
> > + return ssbi_read(context, reg, (u8 *)val, 1);
> > +}
> > +EXPORT_SYMBOL_GPL(ssbi_reg_read);
> > +
> > +int ssbi_reg_write(void *context, unsigned int reg, unsigned int val)
> > +{
> > + return ssbi_write(context, reg, (u8 *)&val, 1);
> > +}
> > +EXPORT_SYMBOL_GPL(ssbi_reg_write);
>
> Not a big deal but could these just be inlined in the headers?

Sure, I can do that if I need to resend? The only benefit I see
is two less symbols exported.

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

Mark Brown

unread,
Dec 28, 2013, 10:00:01 AM12/28/13
to
On Thu, Dec 26, 2013 at 11:41:33AM -0800, Stephen Boyd wrote:
> On 12/24, Mark Brown wrote:

> > Not a big deal but could these just be inlined in the headers?

> Sure, I can do that if I need to resend? The only benefit I see
> is two less symbols exported.

It also means that everything will be marginally faster since there
will be one less level of function call but yeah, there's little
practical benefit - it's a stylistic thing rather than a practical one.
signature.asc

Mark Brown

unread,
Dec 30, 2013, 7:40:01 AM12/30/13
to
On Mon, Dec 23, 2013 at 12:46:01PM -0800, Stephen Boyd wrote:
> Use a regmap so that the pm8xxx read/write APIs can be removed
> once all consumer drivers are converted.

Reviewed-by: Mark Brown <bro...@linaro.org>
signature.asc

Lee Jones

unread,
Jan 6, 2014, 7:00:02 AM1/6/14
to
On Thu, 26 Dec 2013, Stephen Boyd wrote:

> On 12/24, Mark Brown wrote:
> > On Mon, Dec 23, 2013 at 12:46:00PM -0800, Stephen Boyd wrote:
> >
> > > +int ssbi_reg_read(void *context, unsigned int reg, unsigned int *val)
> > > +{
> > > + *val = 0;
> > > + return ssbi_read(context, reg, (u8 *)val, 1);
> > > +}
> > > +EXPORT_SYMBOL_GPL(ssbi_reg_read);
> > > +
> > > +int ssbi_reg_write(void *context, unsigned int reg, unsigned int val)
> > > +{
> > > + return ssbi_write(context, reg, (u8 *)&val, 1);
> > > +}
> > > +EXPORT_SYMBOL_GPL(ssbi_reg_write);
> >
> > Not a big deal but could these just be inlined in the headers?
>
> Sure, I can do that if I need to resend? The only benefit I see
> is two less symbols exported.

Yes please. After this review.

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Lee Jones

unread,
Jan 6, 2014, 7:00:02 AM1/6/14
to
> PM8921 and PM8058 are PMICs found paired with MSM8960 and MSM8660
> devices respectively. They contain subdevices such as keypads,
> RTC, regulators, clocks, etc.
>
> Cc: <devic...@vger.kernel.org>

Still waiting on these guys.

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Lee Jones

unread,
Jan 6, 2014, 7:00:03 AM1/6/14
to
> Allow this driver to probe based on devicetree.
>
> Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
> ---
> drivers/mfd/pm8921-core.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
> index d13cb6e..290f0da 100644
> --- a/drivers/mfd/pm8921-core.c
> +++ b/drivers/mfd/pm8921-core.c
> @@ -420,6 +420,13 @@ static const struct regmap_config ssbi_regmap_config = {
> .reg_write = ssbi_reg_write
> };
>
> +static const struct of_device_id pm8921_id_table[] = {
> + { .compatible = "qcom,pm8058", },
> + { .compatible = "qcom,pm8921", },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, pm8921_id_table);
> +
> static int pm8921_probe(struct platform_device *pdev)
> {
> struct pm8921 *pmic;
> @@ -429,7 +436,6 @@ static int pm8921_probe(struct platform_device *pdev)
> unsigned int irq;
> u32 rev;
>
> -

Sneaky!

> irq = platform_get_irq(pdev, 0);
> if (irq < 0)
> return irq;
> @@ -494,6 +500,7 @@ static struct platform_driver pm8921_driver = {
> .driver = {
> .name = "pm8921-core",
> .owner = THIS_MODULE,
> + .of_match_table = pm8921_id_table,
> },
> };

Patch fine:
Acked-by: Lee Jones <lee....@linaro.org>

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Lee Jones

unread,
Jan 6, 2014, 9:50:01 AM1/6/14
to
On Mon, 23 Dec 2013, Stephen Boyd wrote:

> The pm8xxx-irq.c code is practically mandatory given that the
> pm8921-core driver will WARN about it missing and the Kconfig
> marks it as default y when a PM8xxx chips is enabled. The only
> reason the file was split out was because we planned to support
> other pm8xxx chips with different pm8xxx-core.c files. Now that
> we have DT on ARM this isn't necessary because we should be able
> to support all the ssbi based PM8xxx chips in one driver and one
> file with no data bloat. Let's move this code into the only
> driver that uses it right now (pm8921) so that it's always compiled when
> needed. In the future we can rename pm8921-core.c to something
> more generic.
>
> Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
> ---
> drivers/mfd/Kconfig | 10 --
> drivers/mfd/pm8921-core.c | 348 ++++++++++++++++++++++++++++++++++++++
> drivers/mfd/pm8xxx-irq.c | 371 -----------------------------------------
> include/linux/mfd/pm8xxx/irq.h | 23 ---
> 4 files changed, 348 insertions(+), 404 deletions(-)
> delete mode 100644 drivers/mfd/pm8xxx-irq.c

Acked-by: Lee Jones <lee....@linaro.org>

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Lee Jones

unread,
Jan 6, 2014, 11:00:02 AM1/6/14
to
> Convert this driver to use irqdomains so that the PMIC's child
> devices can be converted to devicetree.
>
> Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
> ---
> drivers/mfd/pm8921-core.c | 184 ++++++++++++++------------------------
> include/linux/mfd/pm8xxx/irq.h | 36 --------
> include/linux/mfd/pm8xxx/pm8921.h | 30 -------
> 3 files changed, 65 insertions(+), 185 deletions(-)
> delete mode 100644 include/linux/mfd/pm8xxx/irq.h
> delete mode 100644 include/linux/mfd/pm8xxx/pm8921.h
>

<snip>

> @@ -56,8 +56,7 @@
> struct pm_irq_chip {
> struct device *dev;
> spinlock_t pm_irq_lock;
> - unsigned int devirq;
> - unsigned int irq_base;
> + struct irq_domain *domain;

It's probably best to explicitly specify 'irqdomain' here in order to
eliminate any possibility of ambiguity.

> unsigned int num_irqs;
> unsigned int num_blocks;
> unsigned int num_masters;
> @@ -138,7 +137,7 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
> for (i = 0; i < 8; i++) {
> if (bits & (1 << i)) {
> pmirq = block * 8 + i;
> - irq = pmirq + chip->irq_base;
> + irq = irq_find_mapping(chip->domain, pmirq);

Going by this patch only, it appears you're calling irq_find_mapping()
before you've called irq_create_mapping(). This won't work, so unless
you've called the latter in a previous patch, you should ensure that
you do so.

> generic_handle_irq(irq);
> }
> }
> @@ -197,12 +196,11 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
> static void pm8xxx_irq_mask_ack(struct irq_data *d)
> {
> struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
> - unsigned int pmirq = d->irq - chip->irq_base;
> - int master, irq_bit;
> + unsigned int pmirq = irqd_to_hwirq(d);

Why don't you call it hwirq instead of pmirq?

> + int irq_bit;
> u8 block, config;
>
> block = pmirq / 8;

Ah, I guess you're keeping the original naming convention.

> - master = block / 8;

What was the point in this anyway? Was it completely superfluous?

<snip>

> +static int pm8xxx_irq_init(struct platform_device *pdev, unsigned int irq)
> +{
> + struct pm_irq_chip *chip;
> + unsigned int nirqs = 256;

No magic numbers please.

> + chip = devm_kzalloc(&pdev->dev, sizeof(*chip) + sizeof(u8) * nirqs,
> + GFP_KERNEL);

What does the sizeof(u8) add here?

<snip>

> + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
> +}

Can't you use the MFD core instead?

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Stephen Boyd

unread,
Jan 6, 2014, 1:10:02 PM1/6/14
to
Sorry, I messed up this patch. This removal is supposed to be in an
earlier patch. Fixed now.

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

Stephen Boyd

unread,
Jan 6, 2014, 3:40:02 PM1/6/14
to
On 01/06/14 07:53, Lee Jones wrote:
>
>> @@ -56,8 +56,7 @@
>> struct pm_irq_chip {
>> struct device *dev;
>> spinlock_t pm_irq_lock;
>> - unsigned int devirq;
>> - unsigned int irq_base;
>> + struct irq_domain *domain;
> It's probably best to explicitly specify 'irqdomain' here in order to
> eliminate any possibility of ambiguity.

Ok. Renamed.

>
>> unsigned int num_irqs;
>> unsigned int num_blocks;
>> unsigned int num_masters;
>> @@ -138,7 +137,7 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
>> for (i = 0; i < 8; i++) {
>> if (bits & (1 << i)) {
>> pmirq = block * 8 + i;
>> - irq = pmirq + chip->irq_base;
>> + irq = irq_find_mapping(chip->domain, pmirq);
> Going by this patch only, it appears you're calling irq_find_mapping()
> before you've called irq_create_mapping(). This won't work, so unless
> you've called the latter in a previous patch, you should ensure that
> you do so.
>

Interrupts seem to work. I think that's because the mapping is created
when the consumer drivers call request_irq().

From what I can tell, if we call irq_find_mapping() and there is no
mapping associated with it then we have a spurious irq. If that happens
we'll call handle_generic_irq() with 0 and that will cause
handle_bad_irq() to be called and a debug message to be logged. That
seems like a good outcome.

>> - master = block / 8;
> What was the point in this anyway? Was it completely superfluous?

I think it was just copy/paste without thinking if the variables are
actually used.

>
>> +static int pm8xxx_irq_init(struct platform_device *pdev, unsigned int irq)
>> +{
>> + struct pm_irq_chip *chip;
>> + unsigned int nirqs = 256;
> No magic numbers please.

Done.

>
>> + chip = devm_kzalloc(&pdev->dev, sizeof(*chip) + sizeof(u8) * nirqs,
>> + GFP_KERNEL);
> What does the sizeof(u8) add here?
>

This was just keeping the same code that was already there. I will do
sizeof(chip->config[0]) instead which is more future proof if that array
changes type later on.

>
>> + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
>> +}
> Can't you use the MFD core instead?
>

Are you suggesting using mfd_add_devices()? At first glance it looks
like that would require an array of mfd_cell structures that do nothing
besides match compatible strings in the DT. Using of_platform_populate()
achieves the same goal and doesn't require an array of mfd_cell
structures for each different pm8xxx chip that comes along, meaning
simpler code.

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

Stephen Boyd

unread,
Jan 6, 2014, 5:30:01 PM1/6/14
to
On 01/06/14 03:50, Lee Jones wrote:
> On Thu, 26 Dec 2013, Stephen Boyd wrote:
>
>> On 12/24, Mark Brown wrote:
>>> On Mon, Dec 23, 2013 at 12:46:00PM -0800, Stephen Boyd wrote:
>>>
>>>> +int ssbi_reg_read(void *context, unsigned int reg, unsigned int *val)
>>>> +{
>>>> + *val = 0;
>>>> + return ssbi_read(context, reg, (u8 *)val, 1);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(ssbi_reg_read);
>>>> +
>>>> +int ssbi_reg_write(void *context, unsigned int reg, unsigned int val)
>>>> +{
>>>> + return ssbi_write(context, reg, (u8 *)&val, 1);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(ssbi_reg_write);
>>> Not a big deal but could these just be inlined in the headers?
>> Sure, I can do that if I need to resend? The only benefit I see
>> is two less symbols exported.
> Yes please. After this review.
>

Ok done. Just waiting for you to reply on patch 3 until I resend.

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

Lee Jones

unread,
Jan 7, 2014, 3:30:02 AM1/7/14
to
> >> unsigned int num_irqs;
> >> unsigned int num_blocks;
> >> unsigned int num_masters;
> >> @@ -138,7 +137,7 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
> >> for (i = 0; i < 8; i++) {
> >> if (bits & (1 << i)) {
> >> pmirq = block * 8 + i;
> >> - irq = pmirq + chip->irq_base;
> >> + irq = irq_find_mapping(chip->domain, pmirq);
> > Going by this patch only, it appears you're calling irq_find_mapping()
> > before you've called irq_create_mapping(). This won't work, so unless
> > you've called the latter in a previous patch, you should ensure that
> > you do so.
> >
>
> Interrupts seem to work. I think that's because the mapping is created
> when the consumer drivers call request_irq().
>
> From what I can tell, if we call irq_find_mapping() and there is no
> mapping associated with it then we have a spurious irq. If that happens
> we'll call handle_generic_irq() with 0 and that will cause
> handle_bad_irq() to be called and a debug message to be logged. That
> seems like a good outcome.

I would try to adhere to the documentation in case we are missing
something or some of the semantics change. Please read:
Documentation/IRQ-domain.txt. Specifically, "=== irq_domain usage ==="
from line 39, which says to call irq_create_mapping() to indeed, create
the mapping.

> > What does the sizeof(u8) add here?
> >
>
> This was just keeping the same code that was already there. I will do
> sizeof(chip->config[0]) instead which is more future proof if that array
> changes type later on.

Ah, now I see what it's doing. Perhaps brackets would be of use to
ensure readers aren't confused. I also think the sizeof() would be
helpful too, so:

chip = devm_kzalloc(&pdev->dev, sizeof(*chip) +
(sizeof(chip->config[0]) * nirqs), GFP_KERNEL);

> >> + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
> >> +}
> > Can't you use the MFD core instead?
> >
>
> Are you suggesting using mfd_add_devices()? At first glance it looks
> like that would require an array of mfd_cell structures that do nothing
> besides match compatible strings in the DT. Using of_platform_populate()
> achieves the same goal and doesn't require an array of mfd_cell
> structures for each different pm8xxx chip that comes along, meaning
> simpler code.

I'm inclined to agree, but playing Devil's advocate here, as a device
using the MFD subsystem it's often clearer to readers and other people
looking for examples if the MFD core functionality is used. For
instance, I now have no idea what devices the PM8xxx encompasses
without looking at the DTS file. A small cell structure is a small
price to pay for code clarity IMHO.

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Stephen Boyd

unread,
Jan 7, 2014, 3:00:02 PM1/7/14
to
On 01/07, Lee Jones wrote:
> > >> + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
> > >> +}
> > > Can't you use the MFD core instead?
> > >
> >
> > Are you suggesting using mfd_add_devices()? At first glance it looks
> > like that would require an array of mfd_cell structures that do nothing
> > besides match compatible strings in the DT. Using of_platform_populate()
> > achieves the same goal and doesn't require an array of mfd_cell
> > structures for each different pm8xxx chip that comes along, meaning
> > simpler code.
>
> I'm inclined to agree, but playing Devil's advocate here, as a device
> using the MFD subsystem it's often clearer to readers and other people
> looking for examples if the MFD core functionality is used. For
> instance, I now have no idea what devices the PM8xxx encompasses
> without looking at the DTS file. A small cell structure is a small
> price to pay for code clarity IMHO.
>

Why not just put that information in the binding document? And
how is this different from adding a bunch of C files to match a
set of compatible strings that a dts file has just so that we can
add all the devices on a board (think board files for an SoC).
Sure it documents the devices on a board, but we've been moving
away from that by using of_platform_populate().

IMHO the code is clear. I want to add all subnodes of this
device's node as children struct devices. Using
of_platform_populate() says that, whereas mfd_add_devices() says
I want to add these specific subnodes of this device's node.

Also, as more drivers are written and more bindings are ratified
this platform driver will need to be updated with more cells and
more compatible strings, causing more inter-tree dependencies and
more patches. Please, let's avoid this if we can.

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

Lee Jones

unread,
Jan 8, 2014, 3:20:02 AM1/8/14
to
Okay, that's fine. There are a few discussions floating around about
this. If I find some time, I'll have to have a think about the pros
and cons of either approach. This is okay for now.

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Stephen Boyd

unread,
Jan 8, 2014, 1:40:01 PM1/8/14
to
The pm8xxx-irq.c code is practically mandatory given that the
pm8921-core driver will WARN about it missing and the Kconfig
marks it as default y when a PM8xxx chips is enabled. The only
reason the file was split out was because we planned to support
other pm8xxx chips with different pm8xxx-core.c files. Now that
we have DT on ARM this isn't necessary because we should be able
to support all the ssbi based PM8xxx chips in one driver and one
file with no data bloat. Let's move this code into the only
driver that uses it right now (pm8921) so that it's always compiled when
needed. In the future we can rename pm8921-core.c to something
more generic.

Acked-by: Lee Jones <lee....@linaro.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/Kconfig | 10 --
drivers/mfd/pm8921-core.c | 348 ++++++++++++++++++++++++++++++++++++++
drivers/mfd/pm8xxx-irq.c | 371 -----------------------------------------
include/linux/mfd/pm8xxx/irq.h | 23 ---
4 files changed, 348 insertions(+), 404 deletions(-)
delete mode 100644 drivers/mfd/pm8xxx-irq.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index dd671582c9a1..acd4e1cc2d3d 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -478,16 +478,6 @@ 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_PM8XXX_IRQ
- bool "Qualcomm PM8xxx IRQ features"
- depends on MFD_PM8XXX
- default y if MFD_PM8XXX
- help
- This is the IRQ driver for Qualcomm PM 8xxx PMIC chips.
-
- This is required to use certain other PM 8xxx features, such as GPIO
- and MPP.
-
config MFD_RDC321X
tristate "RDC R-321x southbridge"
select MFD_CORE
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 484fe66e6c88..50e0a9b69b9d 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
+static void pm8xxx_irq_handler(unsigned int irq, 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);
+ u8 root;
+ int i, ret, masters = 0;
+
+ ret = pm8xxx_read_root_irq(chip, &root);
+ if (ret) {
+ pr_err("Can't read root status ret=%d\n", ret);
+ return;
+ }
+
+ /* on pm8xxx series masters start from bit 1 of the root */
+ masters = root >> 1;
+
+ /* Read allowed masters for blocks. */
+ for (i = 0; i < chip->num_masters; i++)
+ if (masters & (1 << i))
+ pm8xxx_irq_master_handler(chip, i);
+
+ irq_chip->irq_ack(&desc->irq_data);
+}
+
+static void pm8xxx_irq_mask_ack(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = d->irq - chip->irq_base;
+ int master, irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ master = block / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static void pm8xxx_irq_unmask(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = d->irq - chip->irq_base;
+ int master, irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ master = block / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq];
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = d->irq - chip->irq_base;
+ int master, irq_bit;
+ u8 block, config;
+
+{
+ struct pm_irq_chip *chip;
index 1360e20adf11..000000000000
- spinlock_t pm_irq_lock;
- unsigned int devirq;
- unsigned int irq_base;
- for (i = 0; i < 8; i++) {
- if (bits & (1 << i)) {
- pmirq = block * 8 + i;
- irq = pmirq + chip->irq_base;
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
- pm8xxx_config_irq(chip, block, config);
-}
-
-static void pm8xxx_irq_unmask(struct irq_data *d)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- irq_bit = pmirq % 8;
-
- config = chip->config[pmirq];
- pm8xxx_config_irq(chip, block, config);
-}
-
-static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
-{
- struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
- u8 block, config;
-
- block = pmirq / 8;
- master = block / 8;
- pmirq = irq - chip->irq_base;
-
index f83d6b43ecbb..1a11b02383f0 100644
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

Stephen Boyd

unread,
Jan 8, 2014, 1:40:02 PM1/8/14
to
Use a regmap so that the pm8xxx read/write APIs can be removed
once all consumer drivers are converted.

Reviewed-by: Mark Brown <bro...@linaro.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/Kconfig | 1 +
drivers/mfd/pm8921-core.c | 66 +++++++++++++++++++++++++++--------------------
2 files changed, 39 insertions(+), 28 deletions(-)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 03bb43bb40b9..d33bc35d13a2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -467,6 +467,7 @@ config MFD_PM8921_CORE
depends on (ARCH_MSM || HEXAGON)
select MFD_CORE
select MFD_PM8XXX
+ select REGMAP
help
If you say yes to this option, support will be included for the
built-in PM8921 PMIC chip.
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index c25e7dae150b..e9340bd6d1ab 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/ssbi.h>
+#include <linux/regmap.h>
#include <linux/of_platform.h>
#include <linux/mfd/core.h>
#include <linux/mfd/pm8xxx/core.h>
@@ -57,6 +58,7 @@

struct pm_irq_chip {
struct device *dev;
+ struct regmap *regmap;
spinlock_t pm_irq_lock;
struct irq_domain *irqdomain;
unsigned int num_irqs;
@@ -70,29 +72,19 @@ struct pm8921 {
struct pm_irq_chip *irq_chip;
};

-static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
-{
- return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
-}
-
-static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
-{
- return pm8xxx_readb(chip->dev,
- SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
-}
-
-static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
+static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
+ unsigned int *ip)
{
int rc;

spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
if (rc) {
pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
goto bail;
}

- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
if (rc)
pr_err("Failed Reading Status rc=%d\n", rc);
bail:
@@ -100,19 +92,20 @@ bail:
return rc;
}

-static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
+static int
+pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp)
{
int rc;

spin_lock(&chip->pm_irq_lock);
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
if (rc) {
pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
goto bail;
}

cp |= PM_IRQF_WRITE;
- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_CONFIG, cp);
if (rc)
pr_err("Failed Configuring IRQ rc=%d\n", rc);
bail:
@@ -123,7 +116,7 @@ bail:
static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
{
int pmirq, irq, i, ret = 0;
- u8 bits;
+ unsigned int bits;

ret = pm8xxx_read_block_irq(chip, block, &bits);
if (ret) {
@@ -148,10 +141,11 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)

static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
{
- u8 blockbits;
+ unsigned int blockbits;
int block_number, i, ret = 0;

- ret = pm8xxx_read_master_irq(chip, master, &blockbits);
+ ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_M_STATUS1 + master,
+ &blockbits);
if (ret) {
pr_err("Failed to read master %d ret=%d\n", master, ret);
return ret;
@@ -173,12 +167,12 @@ static void pm8xxx_irq_handler(unsigned int irq, 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);
- u8 root;
+ unsigned int root;
int i, ret, masters = 0;

chained_irq_enter(irq_chip, desc);

- ret = pm8xxx_read_root_irq(chip, &root);
+ ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_ROOT, &root);
if (ret) {
pr_err("Can't read root status ret=%d\n", ret);
return;
@@ -283,7 +277,7 @@ static struct irq_chip pm8xxx_irq_chip = {
static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
{
int pmirq, rc;
- u8 block, bits, bit;
+ unsigned int block, bits, bit;
unsigned long flags;
struct irq_data *irq_data = irq_get_irq_data(irq);

@@ -294,14 +288,14 @@ static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)

spin_lock_irqsave(&chip->pm_irq_lock, flags);

- rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
if (rc) {
pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
irq, pmirq, block, rc);
goto bail_out;
}

- rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
if (rc) {
pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
irq, pmirq, block, rc);
@@ -389,11 +383,21 @@ static struct pm8xxx_drvdata pm8921_drvdata = {
.pmic_read_irq_stat = pm8921_read_irq_stat,
};

+static const struct regmap_config ssbi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0x3ff,
+ .fast_io = true,
+ .reg_read = ssbi_reg_read,
+ .reg_write = ssbi_reg_write
+};
+
static int pm8921_probe(struct platform_device *pdev)
{
struct pm8921 *pmic;
+ struct regmap *regmap;
int rc;
- u8 val;
+ unsigned int val;
unsigned int irq;
u32 rev;
struct pm_irq_chip *chip;
@@ -409,8 +413,13 @@ static int pm8921_probe(struct platform_device *pdev)
return -ENOMEM;
}

+ regmap = devm_regmap_init(&pdev->dev, NULL, pdev->dev.parent,
+ &ssbi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
/* Read PMIC chip revision */
- rc = ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+ rc = regmap_read(regmap, REG_HWREV, &val);
if (rc) {
pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
return rc;
@@ -419,7 +428,7 @@ static int pm8921_probe(struct platform_device *pdev)
rev = val;

/* Read PMIC chip revision 2 */
- rc = ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+ rc = regmap_read(regmap, REG_HWREV_2, &val);
if (rc) {
pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
REG_HWREV_2, rc);
@@ -440,6 +449,7 @@ static int pm8921_probe(struct platform_device *pdev)

pmic->irq_chip = chip;
chip->dev = &pdev->dev;
+ chip->regmap = regmap;
chip->num_irqs = nirqs;
chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);

Stephen Boyd

unread,
Jan 8, 2014, 1:40:01 PM1/8/14
to
Convert this driver to use irqdomains so that the PMIC's child
devices can be converted to devicetree.

Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/pm8921-core.c | 198 +++++++++++++++-----------------------
include/linux/mfd/pm8xxx/irq.h | 36 -------
include/linux/mfd/pm8xxx/pm8921.h | 30 ------
3 files changed, 75 insertions(+), 189 deletions(-)
delete mode 100644 include/linux/mfd/pm8xxx/irq.h
delete mode 100644 include/linux/mfd/pm8xxx/pm8921.h

diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 9ddc31f7a71d..c25e7dae150b 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -17,15 +17,15 @@
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/ssbi.h>
+#include <linux/of_platform.h>
#include <linux/mfd/core.h>
-#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/irq.h>

#define SSBI_REG_ADDR_IRQ_BASE 0x1BB

@@ -53,11 +53,12 @@
#define REG_HWREV 0x002 /* PMIC4 revision */
#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */

+#define PM8921_NR_IRQS 256
+
struct pm_irq_chip {
struct device *dev;
spinlock_t pm_irq_lock;
- unsigned int devirq;
- unsigned int irq_base;
+ struct irq_domain *irqdomain;
unsigned int num_irqs;
unsigned int num_blocks;
unsigned int num_masters;
@@ -138,7 +139,7 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
for (i = 0; i < 8; i++) {
if (bits & (1 << i)) {
pmirq = block * 8 + i;
- irq = pmirq + chip->irq_base;
+ irq = irq_find_mapping(chip->irqdomain, pmirq);
generic_handle_irq(irq);
}
}
@@ -197,12 +198,11 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
static void pm8xxx_irq_mask_ack(struct irq_data *d)
{
struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
u8 block, config;

block = pmirq / 8;
- master = block / 8;
irq_bit = pmirq % 8;

config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
@@ -212,12 +212,11 @@ static void pm8xxx_irq_mask_ack(struct irq_data *d)
static void pm8xxx_irq_unmask(struct irq_data *d)
{
struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
u8 block, config;

block = pmirq / 8;
- master = block / 8;
irq_bit = pmirq % 8;

config = chip->config[pmirq];
@@ -227,12 +226,11 @@ static void pm8xxx_irq_unmask(struct irq_data *d)
static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
- unsigned int pmirq = d->irq - chip->irq_base;
- int master, irq_bit;
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit;
u8 block, config;

block = pmirq / 8;
- master = block / 8;
irq_bit = pmirq % 8;

chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
@@ -287,12 +285,9 @@ static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
int pmirq, rc;
u8 block, bits, bit;
unsigned long flags;
+ struct irq_data *irq_data = irq_get_irq_data(irq);

- if (chip == NULL || irq < chip->irq_base ||
- irq >= chip->irq_base + chip->num_irqs)
- return -EINVAL;
-
- pmirq = irq - chip->irq_base;
+ pmirq = irq_data->hwirq;

block = pmirq / 8;
bit = pmirq % 8;
@@ -321,67 +316,29 @@ bail_out:
return rc;
}

-static struct pm_irq_chip *pm8xxx_irq_init(struct device *dev,
- const struct pm8xxx_irq_platform_data *pdata)
-{
- struct pm_irq_chip *chip;
- int devirq, rc;
- unsigned int pmirq;
-
- if (!pdata) {
- pr_err("No platform data\n");
- return ERR_PTR(-EINVAL);
- }
+static struct lock_class_key pm8xxx_irq_lock_class;

- devirq = pdata->devirq;
- if (devirq < 0) {
- pr_err("missing devirq\n");
- rc = devirq;
- return ERR_PTR(-EINVAL);
- }
-
- chip = kzalloc(sizeof(struct pm_irq_chip)
- + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
- if (!chip) {
- pr_err("Cannot alloc pm_irq_chip struct\n");
- return ERR_PTR(-EINVAL);
- }
-
- chip->dev = dev;
- chip->devirq = devirq;
- chip->irq_base = pdata->irq_base;
- chip->num_irqs = pdata->irq_cdata.nirqs;
- 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);
+static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct pm_irq_chip *chip = d->host_data;

- for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
- irq_set_chip_and_handler(chip->irq_base + pmirq,
- &pm8xxx_irq_chip,
- handle_level_irq);
- irq_set_chip_data(chip->irq_base + pmirq, chip);
+ irq_set_lockdep_class(irq, &pm8xxx_irq_lock_class);
+ irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, chip);
#ifdef CONFIG_ARM
- set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
+ set_irq_flags(irq, IRQF_VALID);
#else
- irq_set_noprobe(chip->irq_base + pmirq);
+ irq_set_noprobe(irq);
#endif
- }
-
- irq_set_irq_type(devirq, pdata->irq_trigger_flag);
- irq_set_handler_data(devirq, chip);
- irq_set_chained_handler(devirq, pm8xxx_irq_handler);
- irq_set_irq_wake(devirq, 1);
-
- return chip;
-}
-
-static int pm8xxx_irq_exit(struct pm_irq_chip *chip)
-{
- irq_set_chained_handler(chip->devirq, NULL);
- kfree(chip);
return 0;
}

+static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
+ .xlate = irq_domain_xlate_twocell,
+ .map = pm8xxx_irq_domain_map,
+};
+
static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
{
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
@@ -432,42 +389,19 @@ static struct pm8xxx_drvdata pm8921_drvdata = {
.pmic_read_irq_stat = pm8921_read_irq_stat,
};

-static int pm8921_add_subdevices(const struct pm8921_platform_data
- *pdata,
- struct pm8921 *pmic,
- u32 rev)
-{
- int ret = 0, irq_base = 0;
- struct pm_irq_chip *irq_chip;
-
- if (pdata->irq_pdata) {
- pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
- pdata->irq_pdata->irq_cdata.rev = rev;
- irq_base = pdata->irq_pdata->irq_base;
- irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
-
- if (IS_ERR(irq_chip)) {
- pr_err("Failed to init interrupts ret=%ld\n",
- PTR_ERR(irq_chip));
- return PTR_ERR(irq_chip);
- }
- pmic->irq_chip = irq_chip;
- }
- return ret;
-}
-
static int pm8921_probe(struct platform_device *pdev)
{
- const struct pm8921_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct pm8921 *pmic;
int rc;
u8 val;
+ unsigned int irq;
u32 rev;
+ struct pm_irq_chip *chip;
+ unsigned int nirqs = PM8921_NR_IRQS;

- if (!pdata) {
- pr_err("missing platform data\n");
- return -EINVAL;
- }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;

pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8921), GFP_KERNEL);
if (!pmic) {
@@ -498,37 +432,55 @@ static int pm8921_probe(struct platform_device *pdev)
pm8921_drvdata.pm_chip_data = pmic;
platform_set_drvdata(pdev, &pm8921_drvdata);

- rc = pm8921_add_subdevices(pdata, pmic, rev);
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip) +
+ sizeof(chip->config[0]) * nirqs,
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ pmic->irq_chip = chip;
+ chip->dev = &pdev->dev;
+ chip->num_irqs = nirqs;
+ 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);
+ if (!chip->irqdomain)
+ return -ENODEV;
+
+ irq_set_handler_data(irq, chip);
+ irq_set_chained_handler(irq, pm8xxx_irq_handler);
+ irq_set_irq_wake(irq, 1);
+
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
if (rc) {
- pr_err("Cannot add subdevices rc=%d\n", rc);
- goto err;
+ irq_set_chained_handler(irq, NULL);
+ irq_set_handler_data(irq, NULL);
+ irq_domain_remove(chip->irqdomain);
}

- /* gpio might not work if no irq device is found */
- WARN_ON(pmic->irq_chip == NULL);
+ return rc;
+}

+static int pm8921_remove_child(struct device *dev, void *unused)
+{
+ platform_device_unregister(to_platform_device(dev));
return 0;
-
-err:
- mfd_remove_devices(pmic->dev);
- return rc;
}

static int pm8921_remove(struct platform_device *pdev)
{
- struct pm8xxx_drvdata *drvdata;
- struct pm8921 *pmic = NULL;
-
- drvdata = platform_get_drvdata(pdev);
- if (drvdata)
- pmic = drvdata->pm_chip_data;
- if (pmic) {
- mfd_remove_devices(pmic->dev);
- if (pmic->irq_chip) {
- pm8xxx_irq_exit(pmic->irq_chip);
- pmic->irq_chip = NULL;
- }
- }
+ int irq = platform_get_irq(pdev, 0);
+ struct pm8921 *pmic = pm8921_drvdata.pm_chip_data;
+ struct pm_irq_chip *chip = pmic->irq_chip;
+
+ device_for_each_child(&pdev->dev, NULL, pm8921_remove_child);
+ irq_set_chained_handler(irq, NULL);
+ irq_set_handler_data(irq, NULL);
+ irq_domain_remove(chip->irqdomain);

return 0;
}
diff --git a/include/linux/mfd/pm8xxx/irq.h b/include/linux/mfd/pm8xxx/irq.h
deleted file mode 100644
index 1a11b02383f0..000000000000
--- a/include/linux/mfd/pm8xxx/irq.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
deleted file mode 100644
index 00fa3de7659d..000000000000
--- a/include/linux/mfd/pm8xxx/pm8921.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
-/*
- * Qualcomm PMIC 8921 driver header file
- *
- */
-
-#ifndef __MFD_PM8921_H
-#define __MFD_PM8921_H
-
-#include <linux/mfd/pm8xxx/irq.h>
-
-#define PM8921_NR_IRQS 256
-
-struct pm8921_platform_data {
- int irq_base;
- struct pm8xxx_irq_platform_data *irq_pdata;
-};
-
-#endif

Stephen Boyd

unread,
Jan 8, 2014, 1:40:02 PM1/8/14
to
These patches lay the groundwork for converting the pm8921 sub-devices
to devicetree as well as simplify the API by migrating the core code
to use the regmap API instead of the custom pm8xxx read/write wrapper.

Changes since v2:
* Picked up reviewed-by tags
* Fixed irqdomain teardown in driver remove
* No magical 256 constant
* Renamed domain to irqdomain
* Clarified kzalloc call
* Pushed ssbi regmap helpers into header file
* Fixed whitespace noise in patch 6

Changes since v1:
* First 3 cleanup patches dropped because they're applied upstream
* New regmap read/write helpers
* New patch for DT match table
* New binding document

Stephen Boyd (7):
mfd: Move pm8xxx-irq.c contents into only driver that uses it
mfd: pm8921: Update for genirq changes
mfd: pm8921: Migrate to irqdomains
mfd: ssbi: Add regmap read/write helpers
mfd: pm8921: Use ssbi regmap
mfd: pm8921: Add DT match table
devicetree: bindings: Document PM8921/8058 PMICs

.../devicetree/bindings/mfd/qcom,pm8xxx.txt | 63 +++
drivers/mfd/Kconfig | 12 +-
drivers/mfd/pm8921-core.c | 427 ++++++++++++++++++---
drivers/mfd/pm8xxx-irq.c | 371 ------------------
include/linux/mfd/pm8xxx/irq.h | 59 ---
include/linux/mfd/pm8xxx/pm8921.h | 30 --
include/linux/ssbi.h | 13 +
7 files changed, 451 insertions(+), 524 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
delete mode 100644 drivers/mfd/pm8xxx-irq.c
delete mode 100644 include/linux/mfd/pm8xxx/irq.h
delete mode 100644 include/linux/mfd/pm8xxx/pm8921.h

Stephen Boyd

unread,
Jan 8, 2014, 1:40:02 PM1/8/14
to
PM8921 and PM8058 are PMICs found paired with MSM8960 and MSM8660
devices respectively. They contain subdevices such as keypads,
RTCs, regulators, clocks, etc.

Cc: <devic...@vger.kernel.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
.../devicetree/bindings/mfd/qcom,pm8xxx.txt | 63 ++++++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt

diff --git a/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt b/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
new file mode 100644
index 000000000000..e3fe625ffd58

Stephen Boyd

unread,
Jan 8, 2014, 1:40:02 PM1/8/14
to
Allow this driver to probe based on devicetree.

Acked-by: Lee Jones <lee....@linaro.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/pm8921-core.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index e9340bd6d1ab..3aab6ace5eb5 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -392,6 +392,13 @@ static const struct regmap_config ssbi_regmap_config = {
.reg_write = ssbi_reg_write
};

+static const struct of_device_id pm8921_id_table[] = {
+ { .compatible = "qcom,pm8058", },
+ { .compatible = "qcom,pm8921", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm8921_id_table);
+
static int pm8921_probe(struct platform_device *pdev)
{
struct pm8921 *pmic;
@@ -501,6 +508,7 @@ static struct platform_driver pm8921_driver = {
.driver = {
.name = "pm8921-core",
.owner = THIS_MODULE,
+ .of_match_table = pm8921_id_table,
},
};

Stephen Boyd

unread,
Jan 8, 2014, 1:40:02 PM1/8/14
to
Add read and write helper functions that the pm8921-core driver
can use to read and write ssbi regsiters via a "no-bus" regmap.

Cc: Mark Brown <bro...@kernel.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
include/linux/ssbi.h | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/include/linux/ssbi.h b/include/linux/ssbi.h
index bcbb642a7641..3ffc02e479d2 100644
--- a/include/linux/ssbi.h
+++ b/include/linux/ssbi.h
@@ -20,4 +20,17 @@
int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len);
int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len);

+static inline int
+ssbi_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ *val = 0;
+ return ssbi_read(context, reg, (u8 *)val, 1);
+}
+
+static inline int
+ssbi_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ return ssbi_write(context, reg, (u8 *)&val, 1);
+}
+
#endif

Stephen Boyd

unread,
Jan 8, 2014, 1:50:02 PM1/8/14
to
Since this code has been marked broken for some time a few genirq
tree wide changes weren't made. set_irq_wake() was renamed to
irq_set_irq_wake() in commit a0cd9ca2b (genirq: Namespace
cleanup, 2011-02-10) and commit 10a8c383 (irq: introduce entry
and exit functions for chained handlers) introduced the chained
irq functions but this driver wasn't updated to use them. Fix
these problems and remove the BROKEN marking on this driver.

Acked-by: Lee Jones <lee....@linaro.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
drivers/mfd/Kconfig | 1 -
drivers/mfd/pm8921-core.c | 7 +++++--
2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index acd4e1cc2d3d..03bb43bb40b9 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -465,7 +465,6 @@ config MFD_PM8XXX
config MFD_PM8921_CORE
tristate "Qualcomm PM8921 PMIC chip"
depends on (ARCH_MSM || HEXAGON)
- depends on BROKEN
select MFD_CORE
select MFD_PM8XXX
help
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 50e0a9b69b9d..9ddc31f7a71d 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -15,6 +15,7 @@

#include <linux/kernel.h>
#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -174,6 +175,8 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
u8 root;
int i, ret, masters = 0;

+ chained_irq_enter(irq_chip, desc);
+
ret = pm8xxx_read_root_irq(chip, &root);
if (ret) {
pr_err("Can't read root status ret=%d\n", ret);
@@ -188,7 +191,7 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
if (masters & (1 << i))
pm8xxx_irq_master_handler(chip, i);

- irq_chip->irq_ack(&desc->irq_data);
+ chained_irq_exit(irq_chip, desc);
}

static void pm8xxx_irq_mask_ack(struct irq_data *d)
@@ -367,7 +370,7 @@ static struct pm_irq_chip *pm8xxx_irq_init(struct device *dev,
irq_set_irq_type(devirq, pdata->irq_trigger_flag);
irq_set_handler_data(devirq, chip);
irq_set_chained_handler(devirq, pm8xxx_irq_handler);
- set_irq_wake(devirq, 1);
+ irq_set_irq_wake(devirq, 1);

return chip;

Lee Jones

unread,
Jan 8, 2014, 4:40:02 PM1/8/14
to
> PM8921 and PM8058 are PMICs found paired with MSM8960 and MSM8660
> devices respectively. They contain subdevices such as keypads,
> RTCs, regulators, clocks, etc.
>
> Cc: <devic...@vger.kernel.org>
> Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
> ---
> .../devicetree/bindings/mfd/qcom,pm8xxx.txt | 63 ++++++++++++++++++++++
> 1 file changed, 63 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt

Just waiting on the Ack for this one now.

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Lee Jones

unread,
Jan 8, 2014, 4:40:03 PM1/8/14
to
On Wed, 08 Jan 2014, Stephen Boyd wrote:

> Convert this driver to use irqdomains so that the PMIC's child
> devices can be converted to devicetree.
>
> Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
> ---
> drivers/mfd/pm8921-core.c | 198 +++++++++++++++-----------------------
> include/linux/mfd/pm8xxx/irq.h | 36 -------
> include/linux/mfd/pm8xxx/pm8921.h | 30 ------
> 3 files changed, 75 insertions(+), 189 deletions(-)
> delete mode 100644 include/linux/mfd/pm8xxx/irq.h
> delete mode 100644 include/linux/mfd/pm8xxx/pm8921.h

I'm still a little dubious over the lack of an irq_create_mapping()
call, but if you say you've tested it and it works I guess something
else must be going on in its place.

Acked-by: Lee Jones <lee....@linaro.org>

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Courtney Cavin

unread,
Jan 8, 2014, 8:20:01 PM1/8/14
to
On Wed, Jan 08, 2014 at 07:37:47PM +0100, Stephen Boyd wrote:
> Add read and write helper functions that the pm8921-core driver
> can use to read and write ssbi regsiters via a "no-bus" regmap.
>

Nit: s/regsiters/registers/

> Cc: Mark Brown <bro...@kernel.org>
> Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
> ---
> include/linux/ssbi.h | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/include/linux/ssbi.h b/include/linux/ssbi.h
> index bcbb642a7641..3ffc02e479d2 100644
> --- a/include/linux/ssbi.h
> +++ b/include/linux/ssbi.h
> @@ -20,4 +20,17 @@
> int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len);
> int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len);
>
> +static inline int
> +ssbi_reg_read(void *context, unsigned int reg, unsigned int *val)
> +{
> + *val = 0;
> + return ssbi_read(context, reg, (u8 *)val, 1);
> +}
> +
> +static inline int
> +ssbi_reg_write(void *context, unsigned int reg, unsigned int val)
> +{
> + return ssbi_write(context, reg, (u8 *)&val, 1);
> +}

These functions are endian specific and just generally ugly. I
understand that these functions may make the ssbi regmap code cleaner,
but that's not really a good excuse for functions which by themselves
look horribly broken.

If these are really needed, perhaps something like the following would
be acceptable?

+static inline int
+ssbi_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ int rc;
+ u8 b;
+ rc = ssbi_read(context, reg, &b, 1);
+ if (rc == 1)
+ *val = b;
+ return rc;
+}
+
+static inline int
+ssbi_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ u8 b = val;
+ return ssbi_write(context, reg, &b, 1);
+}

-Courtney

Stephen Boyd

unread,
Jan 8, 2014, 9:30:02 PM1/8/14
to
Sure. I think you meant to check for a 0 return value from ssbi_read
though? Lee can you use this replacement patch please?

---8<---
From: Stephen Boyd <sb...@codeaurora.org>
Subject: [PATCH] mfd: ssbi: Add regmap read/write helpers

Add read and write helper functions that the pm8921-core driver
can use to read and write ssbi regsiters via a "no-bus" regmap.

Cc: Mark Brown <bro...@kernel.org>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---
include/linux/ssbi.h | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/include/linux/ssbi.h b/include/linux/ssbi.h
index bcbb642a7641..087b08a4d333 100644
--- a/include/linux/ssbi.h
+++ b/include/linux/ssbi.h
@@ -20,4 +20,24 @@
int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len);
int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len);

+static inline int
+ssbi_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ int ret;
+ u8 v;
+
+ ret = ssbi_read(context, reg, &v, 1);
+ if (!ret)
+ *val = v;
+
+ return ret;
+}
+
+static inline int
+ssbi_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ u8 v = val;
+ return ssbi_write(context, reg, &v, 1);
+}
+
#endif

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

Stephen Boyd

unread,
Feb 11, 2014, 1:40:02 AM2/11/14
to
On 01/08, Stephen Boyd wrote:
> These patches lay the groundwork for converting the pm8921 sub-devices
> to devicetree as well as simplify the API by migrating the core code
> to use the regmap API instead of the custom pm8xxx read/write wrapper.

Lee,

Can you pick up these patches now? I don't think we're going to
get an ack from the DT reviewers. Its been over a month and
according to the documentation[1] I think we've done our due
diligence.

[1] Documentation/devicetree/bindings/submitting-patches.txt

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,

Lee Jones

unread,
Feb 11, 2014, 4:30:03 AM2/11/14
to
> PM8921 and PM8058 are PMICs found paired with MSM8960 and MSM8660
> devices respectively. They contain subdevices such as keypads,
> RTCs, regulators, clocks, etc.
>
> Cc: <devic...@vger.kernel.org>
> Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
> ---
> .../devicetree/bindings/mfd/qcom,pm8xxx.txt | 63 ++++++++++++++++++++++
> 1 file changed, 63 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt b/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
> new file mode 100644
> index 000000000000..e3fe625ffd58
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt
> @@ -0,0 +1,63 @@

<snip>

> +- interrupts:
> + Usage: required
> + Value type: <prop-encoded-array>

Either provide an example or a comment to see the description of
#interrupt-cells

> + Definition: specifies the interrupt that indicates a subdevice
> + has generated an interrupt (summary interrupt). The
> + format of the specifier is defined by the binding document
> + describing the node's interrupt parent.
> +
> +- #interrupt-cells:
> + Usage: required
> + Value type : <u32>
> + Definition: must be 2. Specifies the number of cells needed to encode
> + an interrupt source. The 1st cell contains the interrupt
> + number. The 2nd cell is the trigger type and level flags
> + encoded as follows:
> +
> + 1 = low-to-high edge triggered
> + 2 = high-to-low edge triggered
> + 4 = active high level-sensitive
> + 8 = active low level-sensitive

Actually I'd prefer if you used the definitions in:
dt-bindings/interrupt-controller/irq.h

> +- interrupt-controller:
> + Usage: required
> + Value type: <empty>
> + Definition: identifies this node as an interrupt controller
> +
> +EXAMPLE
> +
> + pmicintc: pmic@0 {
> + compatible = "qcom,pm8921";
> + interrupts = <104 8>;

As above.

> + #interrupt-cells = <2>;
> + interrupt-controller;
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + pwrkey {
> + compatible = "qcom,pm8921-pwrkey";
> + interrupt-parent = <&pmicintc>;
> + interrupts = <50 1>, <51 1>;

As above.

> + };
> + };
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Stephen Boyd

unread,
Feb 13, 2014, 12:40:02 AM2/13/14
to
On 02/11, Lee Jones wrote:
>
> > +- interrupts:
> > + Usage: required
> > + Value type: <prop-encoded-array>
>
> Either provide an example or a comment to see the description of
> #interrupt-cells

It is part of the example. We also state that the format is
defined by the interrupt parent binding.

>
> > + Definition: specifies the interrupt that indicates a subdevice
> > + has generated an interrupt (summary interrupt). The
> > + format of the specifier is defined by the binding document
> > + describing the node's interrupt parent.
> > +
> > +- #interrupt-cells:
> > + Usage: required
> > + Value type : <u32>
> > + Definition: must be 2. Specifies the number of cells needed to encode
> > + an interrupt source. The 1st cell contains the interrupt
> > + number. The 2nd cell is the trigger type and level flags
> > + encoded as follows:
> > +
> > + 1 = low-to-high edge triggered
> > + 2 = high-to-low edge triggered
> > + 4 = active high level-sensitive
> > + 8 = active low level-sensitive
>
> Actually I'd prefer if you used the definitions in:
> dt-bindings/interrupt-controller/irq.h

These match the #defines in that file. I'd like to be explicit
about the numbers to prevent people from thinking they have to
use #defines and to match what other irq controllers have done
(gic, atmel-aic, etc.)

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
--

Lee Jones

unread,
Feb 13, 2014, 6:10:03 AM2/13/14
to
> > > +- interrupts:
> > > + Usage: required
> > > + Value type: <prop-encoded-array>
> >
> > Either provide an example or a comment to see the description of
> > #interrupt-cells
>
> It is part of the example. We also state that the format is
> defined by the interrupt parent binding.

Okay, fair enough.

> > > + Definition: specifies the interrupt that indicates a subdevice
> > > + has generated an interrupt (summary interrupt). The
> > > + format of the specifier is defined by the binding document
> > > + describing the node's interrupt parent.
> > > +
> > > +- #interrupt-cells:
> > > + Usage: required
> > > + Value type : <u32>
> > > + Definition: must be 2. Specifies the number of cells needed to encode
> > > + an interrupt source. The 1st cell contains the interrupt
> > > + number. The 2nd cell is the trigger type and level flags
> > > + encoded as follows:
> > > +
> > > + 1 = low-to-high edge triggered
> > > + 2 = high-to-low edge triggered
> > > + 4 = active high level-sensitive
> > > + 8 = active low level-sensitive
> >
> > Actually I'd prefer if you used the definitions in:
> > dt-bindings/interrupt-controller/irq.h
>
> These match the #defines in that file. I'd like to be explicit
> about the numbers to prevent people from thinking they have to
> use #defines and to match what other irq controllers have done
> (gic, atmel-aic, etc.)

I believe people _do_ have to use the #defines? Is there a good reason
for you not wanting to use them?

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Stephen Boyd

unread,
Feb 18, 2014, 1:50:01 PM2/18/14
to
On 02/13/14 03:06, Lee Jones wrote:
>> These match the #defines in that file. I'd like to be explicit
>> about the numbers to prevent people from thinking they have to
>> use #defines and to match what other irq controllers have done
>> (gic, atmel-aic, etc.)
> I believe people _do_ have to use the #defines? Is there a good reason
> for you not wanting to use them?
>

No, there isn't any requirement to use #defines in DT.

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

0 new messages