cregit-Linux how code gets into the kernel

Release 4.14 drivers/mfd/motorola-cpcap.c

Directory: drivers/mfd
/*
 * Motorola CPCAP PMIC core driver
 *
 * Copyright (C) 2016 Tony Lindgren <tony@atomide.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/sysfs.h>

#include <linux/mfd/motorola-cpcap.h>
#include <linux/spi/spi.h>


#define CPCAP_NR_IRQ_REG_BANKS	6

#define CPCAP_NR_IRQ_CHIPS	3

#define CPCAP_REGISTER_SIZE	4

#define CPCAP_REGISTER_BITS	16


struct cpcap_ddata {
	
struct spi_device *spi;
	
struct regmap_irq *irqs;
	
struct regmap_irq_chip_data *irqdata[CPCAP_NR_IRQ_CHIPS];
	
const struct regmap_config *regmap_conf;
	
struct regmap *regmap;
};


static int cpcap_sense_irq(struct regmap *regmap, int irq) { int regnum = irq / CPCAP_REGISTER_BITS; int mask = BIT(irq % CPCAP_REGISTER_BITS); int reg = CPCAP_REG_INTS1 + (regnum * CPCAP_REGISTER_SIZE); int err, val; if (reg < CPCAP_REG_INTS1 || reg > CPCAP_REG_INTS4) return -EINVAL; err = regmap_read(regmap, reg, &val); if (err) return err; return !!(val & mask); }

Contributors

PersonTokensPropCommitsCommitProp
Sebastian Reichel89100.00%1100.00%
Total89100.00%1100.00%


int cpcap_sense_virq(struct regmap *regmap, int virq) { struct regmap_irq_chip_data *d = irq_get_chip_data(virq); int irq_base = regmap_irq_chip_get_base(d); return cpcap_sense_irq(regmap, virq - irq_base); }

Contributors

PersonTokensPropCommitsCommitProp
Sebastian Reichel41100.00%1100.00%
Total41100.00%1100.00%

EXPORT_SYMBOL_GPL(cpcap_sense_virq);
static int cpcap_check_revision(struct cpcap_ddata *cpcap) { u16 vendor, rev; int ret; ret = cpcap_get_vendor(&cpcap->spi->dev, cpcap->regmap, &vendor); if (ret) return ret; ret = cpcap_get_revision(&cpcap->spi->dev, cpcap->regmap, &rev); if (ret) return ret; dev_info(&cpcap->spi->dev, "CPCAP vendor: %s rev: %i.%i (%x)\n", vendor == CPCAP_VENDOR_ST ? "ST" : "TI", CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev), rev); if (rev < CPCAP_REVISION_2_1) { dev_info(&cpcap->spi->dev, "Please add old CPCAP revision support as needed\n"); return -ENODEV; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Tony Lindgren130100.00%1100.00%
Total130100.00%1100.00%

/* * First two irq chips are the two private macro interrupt chips, the third * irq chip is for register banks 1 - 4 and is available for drivers to use. */ static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = { { .name = "cpcap-m2", .num_regs = 1, .status_base = CPCAP_REG_MI1, .ack_base = CPCAP_REG_MI1, .mask_base = CPCAP_REG_MIM1, .use_ack = true, .ack_invert = true, }, { .name = "cpcap-m2", .num_regs = 1, .status_base = CPCAP_REG_MI2, .ack_base = CPCAP_REG_MI2, .mask_base = CPCAP_REG_MIM2, .use_ack = true, .ack_invert = true, }, { .name = "cpcap1-4", .num_regs = 4, .status_base = CPCAP_REG_INT1, .ack_base = CPCAP_REG_INT1, .mask_base = CPCAP_REG_INTM1, .use_ack = true, .ack_invert = true, }, };
static void cpcap_init_one_regmap_irq(struct cpcap_ddata *cpcap, struct regmap_irq *rirq, int irq_base, int irq) { unsigned int reg_offset; unsigned int bit, mask; reg_offset = irq - irq_base; reg_offset /= cpcap->regmap_conf->val_bits; reg_offset *= cpcap->regmap_conf->reg_stride; bit = irq % cpcap->regmap_conf->val_bits; mask = (1 << bit); rirq->reg_offset = reg_offset; rirq->mask = mask; }

Contributors

PersonTokensPropCommitsCommitProp
Tony Lindgren84100.00%1100.00%
Total84100.00%1100.00%


static int cpcap_init_irq_chip(struct cpcap_ddata *cpcap, int irq_chip, int irq_start, int nr_irqs) { struct regmap_irq_chip *chip = &cpcap_irq_chip[irq_chip]; int i, ret; for (i = irq_start; i < irq_start + nr_irqs; i++) { struct regmap_irq *rirq = &cpcap->irqs[i]; cpcap_init_one_regmap_irq(cpcap, rirq, irq_start, i); } chip->irqs = &cpcap->irqs[irq_start]; chip->num_irqs = nr_irqs; chip->irq_drv_data = cpcap; ret = devm_regmap_add_irq_chip(&cpcap->spi->dev, cpcap->regmap, cpcap->spi->irq, irq_get_trigger_type(cpcap->spi->irq) | IRQF_SHARED, -1, chip, &cpcap->irqdata[irq_chip]); if (ret) { dev_err(&cpcap->spi->dev, "could not add irq chip %i: %i\n", irq_chip, ret); return ret; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Tony Lindgren175100.00%2100.00%
Total175100.00%2100.00%


static int cpcap_init_irq(struct cpcap_ddata *cpcap) { int ret; cpcap->irqs = devm_kzalloc(&cpcap->spi->dev, sizeof(*cpcap->irqs) * CPCAP_NR_IRQ_REG_BANKS * cpcap->regmap_conf->val_bits, GFP_KERNEL); if (!cpcap->irqs) return -ENOMEM; ret = cpcap_init_irq_chip(cpcap, 0, 0, 16); if (ret) return ret; ret = cpcap_init_irq_chip(cpcap, 1, 16, 16); if (ret) return ret; ret = cpcap_init_irq_chip(cpcap, 2, 32, 64); if (ret) return ret; enable_irq_wake(cpcap->spi->irq); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Tony Lindgren129100.00%1100.00%
Total129100.00%1100.00%

static const struct of_device_id cpcap_of_match[] = { { .compatible = "motorola,cpcap", }, { .compatible = "st,6556002", }, {}, }; MODULE_DEVICE_TABLE(of, cpcap_of_match); static const struct regmap_config cpcap_regmap_config = { .reg_bits = 16, .reg_stride = 4, .pad_bits = 0, .val_bits = 16, .write_flag_mask = 0x8000, .max_register = CPCAP_REG_ST_TEST2, .cache_type = REGCACHE_NONE, .reg_format_endian = REGMAP_ENDIAN_LITTLE, .val_format_endian = REGMAP_ENDIAN_LITTLE, };
static int cpcap_probe(struct spi_device *spi) { const struct of_device_id *match; struct cpcap_ddata *cpcap; int ret; match = of_match_device(of_match_ptr(cpcap_of_match), &spi->dev); if (!match) return -ENODEV; cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL); if (!cpcap) return -ENOMEM; cpcap->spi = spi; spi_set_drvdata(spi, cpcap); spi->bits_per_word = 16; spi->mode = SPI_MODE_0 | SPI_CS_HIGH; ret = spi_setup(spi); if (ret) return ret; cpcap->regmap_conf = &cpcap_regmap_config; cpcap->regmap = devm_regmap_init_spi(spi, &cpcap_regmap_config); if (IS_ERR(cpcap->regmap)) { ret = PTR_ERR(cpcap->regmap); dev_err(&cpcap->spi->dev, "Failed to initialize regmap: %d\n", ret); return ret; } ret = cpcap_check_revision(cpcap); if (ret) { dev_err(&cpcap->spi->dev, "Failed to detect CPCAP: %i\n", ret); return ret; } ret = cpcap_init_irq(cpcap); if (ret) return ret; return devm_of_platform_populate(&cpcap->spi->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Tony Lindgren22799.56%150.00%
Benjamin Gaignard10.44%150.00%
Total228100.00%2100.00%

static struct spi_driver cpcap_driver = { .driver = { .name = "cpcap-core", .of_match_table = cpcap_of_match, }, .probe = cpcap_probe, }; module_spi_driver(cpcap_driver); MODULE_ALIAS("platform:cpcap"); MODULE_DESCRIPTION("CPCAP driver"); MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Tony Lindgren108088.24%360.00%
Sebastian Reichel14311.68%120.00%
Benjamin Gaignard10.08%120.00%
Total1224100.00%5100.00%
Directory: drivers/mfd
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.