/* * Device driver for regulators in Hi6421 IC * * Copyright (c) 2013 Linaro Ltd. * Copyright (c) 2011 Hisilicon. * * Guodong Xu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 8-bit register offset in PMIC */ #define HI6421_REG_IRQ1 1 #define HI6421_REG_IRQ2 2 #define HI6421_REG_IRQ3 3 #define HI6421_REG_IRQM1 4 #define HI6421_REG_IRQM2 5 #define HI6421_REG_IRQM3 6 static struct of_device_id of_hi6421_pmic_child_match_tbl[] = { /* regulators */ { .compatible = "hisilicon,hi6421-ldo", }, { .compatible = "hisilicon,hi6421-buck012", }, { .compatible = "hisilicon,hi6421-buck345", }, { /* end */ } }; static struct of_device_id of_hi6421_pmic_match_tbl[] = { { .compatible = "hisilicon,hi6421-pmic", }, { /* end */ } }; /* * The PMIC register is only 8-bit. * Hisilicon SoC use hardware to map PMIC register into SoC mapping. * At here, we are accessing SoC register with 32-bit. */ u32 hi6421_pmic_read(struct hi6421_pmic *pmic, int reg) { unsigned long flags; u32 ret; spin_lock_irqsave(&pmic->lock, flags); ret = readl_relaxed(pmic->regs + (reg << 2)); spin_unlock_irqrestore(&pmic->lock, flags); return ret; } EXPORT_SYMBOL(hi6421_pmic_read); void hi6421_pmic_write(struct hi6421_pmic *pmic, int reg, u32 val) { unsigned long flags; spin_lock_irqsave(&pmic->lock, flags); writel_relaxed(val, pmic->regs + (reg << 2)); spin_unlock_irqrestore(&pmic->lock, flags); } EXPORT_SYMBOL(hi6421_pmic_write); void hi6421_pmic_rmw(struct hi6421_pmic *pmic, int reg, u32 mask, u32 bits) { u32 data; spin_lock(&pmic->lock); data = readl_relaxed(pmic->regs + (reg << 2)) & ~mask; data |= mask & bits; writel_relaxed(data, pmic->regs + (reg << 2)); spin_unlock(&pmic->lock); } EXPORT_SYMBOL(hi6421_pmic_rmw); static int hi6421_to_irq(struct hi6421_pmic *pmic, unsigned offset) { return irq_find_mapping(pmic->domain, offset); } static irqreturn_t hi6421_irq_handler(int irq, void *data) { struct hi6421_pmic *pmic = (struct hi6421_pmic *)data; unsigned long pending; int i, offset, index; for (i = HI6421_REG_IRQ1; i <= HI6421_REG_IRQ3; i++) { spin_lock(&pmic->lock); pending = readl_relaxed(pmic->regs + (i << 2)); pending &= HI6421_MASK_FIELD; writel_relaxed(pending, pmic->regs + ((i + 3) << 2)); spin_unlock(&pmic->lock); if (pending) { for_each_set_bit(offset, &pending, HI6421_BITS) { index = offset + (i - HI6421_REG_IRQ1) * HI6421_BITS; generic_handle_irq(hi6421_to_irq(pmic, index)); } } spin_lock(&pmic->lock); writel_relaxed(0, pmic->regs + ((i + 3) << 2)); writel_relaxed(pending, pmic->regs + (i << 2)); spin_unlock(&pmic->lock); } return IRQ_HANDLED; } static void hi6421_irq_mask(struct irq_data *d) { struct hi6421_pmic *pmic = irq_data_get_irq_chip_data(d); u32 data, offset; offset = ((irqd_to_hwirq(d) >> 3) + HI6421_REG_IRQM1) << 2; spin_lock(&pmic->lock); data = readl_relaxed(pmic->regs + offset); data |= irqd_to_hwirq(d) % 8; writel_relaxed(data, pmic->regs + offset); spin_unlock(&pmic->lock); } static void hi6421_irq_unmask(struct irq_data *d) { struct hi6421_pmic *pmic = irq_data_get_irq_chip_data(d); u32 data, offset; offset = ((irqd_to_hwirq(d) >> 3) + HI6421_REG_IRQM1) << 2; spin_lock(&pmic->lock); data = readl_relaxed(pmic->regs + offset); data &= ~(irqd_to_hwirq(d) % 8); writel_relaxed(data, pmic->regs + offset); spin_unlock(&pmic->lock); } static struct irq_chip hi6421_irqchip = { .name = "pmic", .irq_mask = hi6421_irq_mask, .irq_unmask = hi6421_irq_unmask, }; static int hi6421_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) { struct hi6421_pmic *pmic = d->host_data; irq_set_chip_and_handler_name(virq, &hi6421_irqchip, handle_simple_irq, "hi6421"); irq_set_chip_data(virq, pmic); irq_set_irq_type(virq, IRQ_TYPE_NONE); return 0; } static struct irq_domain_ops hi6421_domain_ops = { .map = hi6421_irq_map, .xlate = irq_domain_xlate_twocell, }; static int hi6421_pmic_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct hi6421_pmic *pmic = NULL; enum of_gpio_flags flags; int i, ret; pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); if (!pmic) { dev_err(dev, "cannot allocate hi6421_pmic device info\n"); return -ENOMEM; } mutex_init(&pmic->enable_mutex); /* get resources */ pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!pmic->res) { dev_err(dev, "platform_get_resource err\n"); return -ENOENT; } if (!devm_request_mem_region(dev, pmic->res->start, resource_size(pmic->res), pdev->name)) { dev_err(dev, "cannot claim register memory\n"); return -ENOMEM; } pmic->regs = devm_ioremap(dev, pmic->res->start, resource_size(pmic->res)); if (!pmic->regs) { dev_err(dev, "cannot map register memory\n"); return -ENOMEM; } /* TODO: get and enable clk request */ spin_lock_init(&pmic->lock); pmic->gpio = of_get_gpio_flags(np, 0, &flags); if (pmic->gpio < 0) return pmic->gpio; if (!gpio_is_valid(pmic->gpio)) return -EINVAL; ret = gpio_request_one(pmic->gpio, GPIOF_IN, "pmic"); if (ret < 0) { dev_err(dev, "failed to request gpio%d\n", pmic->gpio); return ret; } pmic->irq = gpio_to_irq(pmic->gpio); /* clear IRQ status */ spin_lock(&pmic->lock); writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ1 << 2)); writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ2 << 2)); writel_relaxed(0xff, pmic->regs + (HI6421_REG_IRQ3 << 2)); spin_unlock(&pmic->lock); pmic->domain = irq_domain_add_simple(np, HI6421_NR_IRQ, 0, &hi6421_domain_ops, pmic); if (!pmic->domain) return -ENODEV; for (i = 0; i < HI6421_NR_IRQ; i++) { ret = irq_create_mapping(pmic->domain, i); if (ret == NO_IRQ) { dev_err(dev, "failed mapping hwirq %d\n", i); return -ENOMEM; } } ret = request_threaded_irq(pmic->irq, hi6421_irq_handler, NULL, IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND, "pmic", pmic); platform_set_drvdata(pdev, pmic); /* set over-current protection debounce 8ms*/ hi6421_pmic_rmw(pmic, OCP_DEB_CTRL_REG, \ OCP_DEB_SEL_MASK | OCP_EN_DEBOUNCE_MASK | OCP_AUTO_STOP_MASK, \ OCP_DEB_SEL_8MS | OCP_EN_DEBOUNCE_ENABLE); /* populate sub nodes */ of_platform_populate(np, of_hi6421_pmic_child_match_tbl, NULL, dev); return 0; } static int hi6421_pmic_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct hi6421_pmic *pmic = platform_get_drvdata(pdev); free_irq(pmic->irq, pmic); gpio_free(pmic->gpio); devm_iounmap(dev, pmic->regs); devm_release_mem_region(dev, pmic->res->start, resource_size(pmic->res)); devm_kfree(dev, pmic); platform_set_drvdata(pdev, NULL); return 0; } static struct platform_driver hi6421_pmic_driver = { .driver = { .name = "hi6421_pmic", .owner = THIS_MODULE, .of_match_table = of_hi6421_pmic_match_tbl, }, .probe = hi6421_pmic_probe, .remove = hi6421_pmic_remove, }; module_platform_driver(hi6421_pmic_driver); MODULE_AUTHOR("Guodong Xu "); MODULE_DESCRIPTION("Hi6421 PMIC driver"); MODULE_LICENSE("GPL v2");