cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rohm-bd9576.c (5659B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2021 ROHM Semiconductors
      4 *
      5 * ROHM BD9576MUF and BD9573MUF PMIC driver
      6 */
      7
      8#include <linux/i2c.h>
      9#include <linux/interrupt.h>
     10#include <linux/ioport.h>
     11#include <linux/irq.h>
     12#include <linux/mfd/core.h>
     13#include <linux/mfd/rohm-bd957x.h>
     14#include <linux/mfd/rohm-generic.h>
     15#include <linux/module.h>
     16#include <linux/of_device.h>
     17#include <linux/regmap.h>
     18#include <linux/types.h>
     19
     20enum {
     21	BD957X_REGULATOR_CELL,
     22	BD957X_WDT_CELL,
     23};
     24
     25/*
     26 * Due to the BD9576MUF nasty IRQ behaviour we don't always populate IRQs.
     27 * These will be added to regulator resources only if IRQ information for the
     28 * PMIC is populated in device-tree.
     29 */
     30static const struct resource bd9576_regulator_irqs[] = {
     31	DEFINE_RES_IRQ_NAMED(BD9576_INT_THERM, "bd9576-temp"),
     32	DEFINE_RES_IRQ_NAMED(BD9576_INT_OVD, "bd9576-ovd"),
     33	DEFINE_RES_IRQ_NAMED(BD9576_INT_UVD, "bd9576-uvd"),
     34};
     35
     36static struct mfd_cell bd9573_mfd_cells[] = {
     37	[BD957X_REGULATOR_CELL]	= { .name = "bd9573-regulator", },
     38	[BD957X_WDT_CELL]	= { .name = "bd9576-wdt", },
     39};
     40
     41static struct mfd_cell bd9576_mfd_cells[] = {
     42	[BD957X_REGULATOR_CELL]	= { .name = "bd9576-regulator", },
     43	[BD957X_WDT_CELL]	= { .name = "bd9576-wdt", },
     44};
     45
     46static const struct regmap_range volatile_ranges[] = {
     47	regmap_reg_range(BD957X_REG_SMRB_ASSERT, BD957X_REG_SMRB_ASSERT),
     48	regmap_reg_range(BD957X_REG_PMIC_INTERNAL_STAT,
     49			 BD957X_REG_PMIC_INTERNAL_STAT),
     50	regmap_reg_range(BD957X_REG_INT_THERM_STAT, BD957X_REG_INT_THERM_STAT),
     51	regmap_reg_range(BD957X_REG_INT_OVP_STAT, BD957X_REG_INT_SYS_STAT),
     52	regmap_reg_range(BD957X_REG_INT_MAIN_STAT, BD957X_REG_INT_MAIN_STAT),
     53};
     54
     55static const struct regmap_access_table volatile_regs = {
     56	.yes_ranges = &volatile_ranges[0],
     57	.n_yes_ranges = ARRAY_SIZE(volatile_ranges),
     58};
     59
     60static struct regmap_config bd957x_regmap = {
     61	.reg_bits = 8,
     62	.val_bits = 8,
     63	.volatile_table = &volatile_regs,
     64	.max_register = BD957X_MAX_REGISTER,
     65	.cache_type = REGCACHE_RBTREE,
     66};
     67
     68static struct regmap_irq bd9576_irqs[] = {
     69	REGMAP_IRQ_REG(BD9576_INT_THERM, 0, BD957X_MASK_INT_MAIN_THERM),
     70	REGMAP_IRQ_REG(BD9576_INT_OVP, 0, BD957X_MASK_INT_MAIN_OVP),
     71	REGMAP_IRQ_REG(BD9576_INT_SCP, 0, BD957X_MASK_INT_MAIN_SCP),
     72	REGMAP_IRQ_REG(BD9576_INT_OCP, 0, BD957X_MASK_INT_MAIN_OCP),
     73	REGMAP_IRQ_REG(BD9576_INT_OVD, 0, BD957X_MASK_INT_MAIN_OVD),
     74	REGMAP_IRQ_REG(BD9576_INT_UVD, 0, BD957X_MASK_INT_MAIN_UVD),
     75	REGMAP_IRQ_REG(BD9576_INT_UVP, 0, BD957X_MASK_INT_MAIN_UVP),
     76	REGMAP_IRQ_REG(BD9576_INT_SYS, 0, BD957X_MASK_INT_MAIN_SYS),
     77};
     78
     79static struct regmap_irq_chip bd9576_irq_chip = {
     80	.name = "bd9576_irq",
     81	.irqs = &bd9576_irqs[0],
     82	.num_irqs = ARRAY_SIZE(bd9576_irqs),
     83	.status_base = BD957X_REG_INT_MAIN_STAT,
     84	.mask_base = BD957X_REG_INT_MAIN_MASK,
     85	.ack_base = BD957X_REG_INT_MAIN_STAT,
     86	.init_ack_masked = true,
     87	.num_regs = 1,
     88	.irq_reg_stride = 1,
     89};
     90
     91static int bd957x_i2c_probe(struct i2c_client *i2c,
     92			     const struct i2c_device_id *id)
     93{
     94	int ret;
     95	struct regmap *regmap;
     96	struct mfd_cell *cells;
     97	int num_cells;
     98	unsigned long chip_type;
     99	struct irq_domain *domain;
    100	bool usable_irqs;
    101
    102	chip_type = (unsigned long)of_device_get_match_data(&i2c->dev);
    103
    104	switch (chip_type) {
    105	case ROHM_CHIP_TYPE_BD9576:
    106		cells = bd9576_mfd_cells;
    107		num_cells = ARRAY_SIZE(bd9576_mfd_cells);
    108		usable_irqs = !!i2c->irq;
    109		break;
    110	case ROHM_CHIP_TYPE_BD9573:
    111		cells = bd9573_mfd_cells;
    112		num_cells = ARRAY_SIZE(bd9573_mfd_cells);
    113		/*
    114		 * BD9573 only supports fatal IRQs which we can not handle
    115		 * because SoC is going to lose the power.
    116		 */
    117		usable_irqs = false;
    118		break;
    119	default:
    120		dev_err(&i2c->dev, "Unknown device type");
    121		return -EINVAL;
    122	}
    123
    124	regmap = devm_regmap_init_i2c(i2c, &bd957x_regmap);
    125	if (IS_ERR(regmap)) {
    126		dev_err(&i2c->dev, "Failed to initialize Regmap\n");
    127		return PTR_ERR(regmap);
    128	}
    129
    130	/*
    131	 * BD9576 behaves badly. It kepts IRQ line asserted for the whole
    132	 * duration of detected HW condition (like over temperature). So we
    133	 * don't require IRQ to be populated.
    134	 * If IRQ information is not given, then we mask all IRQs and do not
    135	 * provide IRQ resources to regulator driver - which then just omits
    136	 * the notifiers.
    137	 */
    138	if (usable_irqs) {
    139		struct regmap_irq_chip_data *irq_data;
    140		struct mfd_cell *regulators;
    141
    142		regulators = &bd9576_mfd_cells[BD957X_REGULATOR_CELL];
    143		regulators->resources = bd9576_regulator_irqs;
    144		regulators->num_resources = ARRAY_SIZE(bd9576_regulator_irqs);
    145
    146		ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq,
    147					       IRQF_ONESHOT, 0,
    148					       &bd9576_irq_chip, &irq_data);
    149		if (ret) {
    150			dev_err(&i2c->dev, "Failed to add IRQ chip\n");
    151			return ret;
    152		}
    153		domain = regmap_irq_get_domain(irq_data);
    154	} else {
    155		ret = regmap_update_bits(regmap, BD957X_REG_INT_MAIN_MASK,
    156					 BD957X_MASK_INT_ALL,
    157					 BD957X_MASK_INT_ALL);
    158		if (ret)
    159			return ret;
    160		domain = NULL;
    161	}
    162
    163	ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, cells,
    164				   num_cells, NULL, 0, domain);
    165	if (ret)
    166		dev_err(&i2c->dev, "Failed to create subdevices\n");
    167
    168	return ret;
    169}
    170
    171static const struct of_device_id bd957x_of_match[] = {
    172	{ .compatible = "rohm,bd9576", .data = (void *)ROHM_CHIP_TYPE_BD9576, },
    173	{ .compatible = "rohm,bd9573", .data = (void *)ROHM_CHIP_TYPE_BD9573, },
    174	{ },
    175};
    176MODULE_DEVICE_TABLE(of, bd957x_of_match);
    177
    178static struct i2c_driver bd957x_drv = {
    179	.driver = {
    180		.name = "rohm-bd957x",
    181		.of_match_table = bd957x_of_match,
    182	},
    183	.probe = &bd957x_i2c_probe,
    184};
    185module_i2c_driver(bd957x_drv);
    186
    187MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
    188MODULE_DESCRIPTION("ROHM BD9576MUF and BD9573MUF Power Management IC driver");
    189MODULE_LICENSE("GPL");