|
|
@@ -15,22 +15,99 @@
|
|
|
* GNU General Public License for more details.
|
|
|
*/
|
|
|
|
|
|
-#include <linux/kernel.h>
|
|
|
#include <linux/device.h>
|
|
|
-#include <linux/module.h>
|
|
|
-#include <linux/platform_device.h>
|
|
|
+#include <linux/err.h>
|
|
|
#include <linux/init.h>
|
|
|
+#include <linux/interrupt.h>
|
|
|
#include <linux/i2c.h>
|
|
|
-#include <linux/slab.h>
|
|
|
-#include <linux/regmap.h>
|
|
|
-#include <linux/err.h>
|
|
|
+#include <linux/irq.h>
|
|
|
+#include <linux/irqdomain.h>
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/module.h>
|
|
|
#include <linux/of.h>
|
|
|
#include <linux/of_device.h>
|
|
|
+#include <linux/platform_device.h>
|
|
|
+#include <linux/regmap.h>
|
|
|
+#include <linux/slab.h>
|
|
|
|
|
|
#include <linux/mfd/core.h>
|
|
|
#include <linux/mfd/tps65217.h>
|
|
|
|
|
|
-static const struct mfd_cell tps65217s[] = {
|
|
|
+static struct resource charger_resources[] = {
|
|
|
+ DEFINE_RES_IRQ_NAMED(TPS65217_IRQ_AC, "AC"),
|
|
|
+ DEFINE_RES_IRQ_NAMED(TPS65217_IRQ_USB, "USB"),
|
|
|
+};
|
|
|
+
|
|
|
+struct tps65217_irq {
|
|
|
+ int mask;
|
|
|
+ int interrupt;
|
|
|
+};
|
|
|
+
|
|
|
+static const struct tps65217_irq tps65217_irqs[] = {
|
|
|
+ [TPS65217_IRQ_PB] = {
|
|
|
+ .mask = TPS65217_INT_PBM,
|
|
|
+ .interrupt = TPS65217_INT_PBI,
|
|
|
+ },
|
|
|
+ [TPS65217_IRQ_AC] = {
|
|
|
+ .mask = TPS65217_INT_ACM,
|
|
|
+ .interrupt = TPS65217_INT_ACI,
|
|
|
+ },
|
|
|
+ [TPS65217_IRQ_USB] = {
|
|
|
+ .mask = TPS65217_INT_USBM,
|
|
|
+ .interrupt = TPS65217_INT_USBI,
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+static void tps65217_irq_lock(struct irq_data *data)
|
|
|
+{
|
|
|
+ struct tps65217 *tps = irq_data_get_irq_chip_data(data);
|
|
|
+
|
|
|
+ mutex_lock(&tps->irq_lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void tps65217_irq_sync_unlock(struct irq_data *data)
|
|
|
+{
|
|
|
+ struct tps65217 *tps = irq_data_get_irq_chip_data(data);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = tps65217_reg_write(tps, TPS65217_REG_INT, tps->irq_mask,
|
|
|
+ TPS65217_PROTECT_NONE);
|
|
|
+ if (ret != 0)
|
|
|
+ dev_err(tps->dev, "Failed to sync IRQ masks\n");
|
|
|
+
|
|
|
+ mutex_unlock(&tps->irq_lock);
|
|
|
+}
|
|
|
+
|
|
|
+static const inline struct tps65217_irq *
|
|
|
+irq_to_tps65217_irq(struct tps65217 *tps, struct irq_data *data)
|
|
|
+{
|
|
|
+ return &tps65217_irqs[data->hwirq];
|
|
|
+}
|
|
|
+
|
|
|
+static void tps65217_irq_enable(struct irq_data *data)
|
|
|
+{
|
|
|
+ struct tps65217 *tps = irq_data_get_irq_chip_data(data);
|
|
|
+ const struct tps65217_irq *irq_data = irq_to_tps65217_irq(tps, data);
|
|
|
+
|
|
|
+ tps->irq_mask &= ~irq_data->mask;
|
|
|
+}
|
|
|
+
|
|
|
+static void tps65217_irq_disable(struct irq_data *data)
|
|
|
+{
|
|
|
+ struct tps65217 *tps = irq_data_get_irq_chip_data(data);
|
|
|
+ const struct tps65217_irq *irq_data = irq_to_tps65217_irq(tps, data);
|
|
|
+
|
|
|
+ tps->irq_mask |= irq_data->mask;
|
|
|
+}
|
|
|
+
|
|
|
+static struct irq_chip tps65217_irq_chip = {
|
|
|
+ .irq_bus_lock = tps65217_irq_lock,
|
|
|
+ .irq_bus_sync_unlock = tps65217_irq_sync_unlock,
|
|
|
+ .irq_enable = tps65217_irq_enable,
|
|
|
+ .irq_disable = tps65217_irq_disable,
|
|
|
+};
|
|
|
+
|
|
|
+static struct mfd_cell tps65217s[] = {
|
|
|
{
|
|
|
.name = "tps65217-pmic",
|
|
|
.of_compatible = "ti,tps65217-pmic",
|
|
|
@@ -41,10 +118,90 @@ static const struct mfd_cell tps65217s[] = {
|
|
|
},
|
|
|
{
|
|
|
.name = "tps65217-charger",
|
|
|
+ .num_resources = ARRAY_SIZE(charger_resources),
|
|
|
+ .resources = charger_resources,
|
|
|
.of_compatible = "ti,tps65217-charger",
|
|
|
},
|
|
|
};
|
|
|
|
|
|
+static irqreturn_t tps65217_irq_thread(int irq, void *data)
|
|
|
+{
|
|
|
+ struct tps65217 *tps = data;
|
|
|
+ unsigned int status;
|
|
|
+ bool handled = false;
|
|
|
+ int i;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = tps65217_reg_read(tps, TPS65217_REG_INT, &status);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(tps->dev, "Failed to read IRQ status: %d\n",
|
|
|
+ ret);
|
|
|
+ return IRQ_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(tps65217_irqs); i++) {
|
|
|
+ if (status & tps65217_irqs[i].interrupt) {
|
|
|
+ handle_nested_irq(irq_find_mapping(tps->irq_domain, i));
|
|
|
+ handled = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (handled)
|
|
|
+ return IRQ_HANDLED;
|
|
|
+
|
|
|
+ return IRQ_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+static int tps65217_irq_map(struct irq_domain *h, unsigned int virq,
|
|
|
+ irq_hw_number_t hw)
|
|
|
+{
|
|
|
+ struct tps65217 *tps = h->host_data;
|
|
|
+
|
|
|
+ irq_set_chip_data(virq, tps);
|
|
|
+ irq_set_chip_and_handler(virq, &tps65217_irq_chip, handle_edge_irq);
|
|
|
+ irq_set_nested_thread(virq, 1);
|
|
|
+ irq_set_parent(virq, tps->irq);
|
|
|
+ irq_set_noprobe(virq);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct irq_domain_ops tps65217_irq_domain_ops = {
|
|
|
+ .map = tps65217_irq_map,
|
|
|
+};
|
|
|
+
|
|
|
+static int tps65217_irq_init(struct tps65217 *tps, int irq)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ mutex_init(&tps->irq_lock);
|
|
|
+ tps->irq = irq;
|
|
|
+
|
|
|
+ /* Mask all interrupt sources */
|
|
|
+ tps->irq_mask = (TPS65217_INT_RESERVEDM | TPS65217_INT_PBM
|
|
|
+ | TPS65217_INT_ACM | TPS65217_INT_USBM);
|
|
|
+ tps65217_reg_write(tps, TPS65217_REG_INT, tps->irq_mask,
|
|
|
+ TPS65217_PROTECT_NONE);
|
|
|
+
|
|
|
+ tps->irq_domain = irq_domain_add_linear(tps->dev->of_node,
|
|
|
+ TPS65217_NUM_IRQ, &tps65217_irq_domain_ops, tps);
|
|
|
+ if (!tps->irq_domain) {
|
|
|
+ dev_err(tps->dev, "Could not create IRQ domain\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = devm_request_threaded_irq(tps->dev, irq, NULL,
|
|
|
+ tps65217_irq_thread, IRQF_ONESHOT,
|
|
|
+ "tps65217-irq", tps);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(tps->dev, "Failed to request IRQ %d: %d\n",
|
|
|
+ irq, ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* tps65217_reg_read: Read a single tps65217 register.
|
|
|
*
|
|
|
@@ -149,11 +306,22 @@ int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(tps65217_clear_bits);
|
|
|
|
|
|
+static bool tps65217_volatile_reg(struct device *dev, unsigned int reg)
|
|
|
+{
|
|
|
+ switch (reg) {
|
|
|
+ case TPS65217_REG_INT:
|
|
|
+ return true;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static const struct regmap_config tps65217_regmap_config = {
|
|
|
.reg_bits = 8,
|
|
|
.val_bits = 8,
|
|
|
|
|
|
.max_register = TPS65217_REG_MAX,
|
|
|
+ .volatile_reg = tps65217_volatile_reg,
|
|
|
};
|
|
|
|
|
|
static const struct of_device_id tps65217_of_match[] = {
|
|
|
@@ -205,8 +373,19 @@ static int tps65217_probe(struct i2c_client *client,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+ if (client->irq) {
|
|
|
+ tps65217_irq_init(tps, client->irq);
|
|
|
+ } else {
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Don't tell children about IRQ resources which won't fire */
|
|
|
+ for (i = 0; i < ARRAY_SIZE(tps65217s); i++)
|
|
|
+ tps65217s[i].num_resources = 0;
|
|
|
+ }
|
|
|
+
|
|
|
ret = devm_mfd_add_devices(tps->dev, -1, tps65217s,
|
|
|
- ARRAY_SIZE(tps65217s), NULL, 0, NULL);
|
|
|
+ ARRAY_SIZE(tps65217s), NULL, 0,
|
|
|
+ tps->irq_domain);
|
|
|
if (ret < 0) {
|
|
|
dev_err(tps->dev, "mfd_add_devices failed: %d\n", ret);
|
|
|
return ret;
|