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

atc260x-core.c (9327B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Core support for ATC260x PMICs
      4 *
      5 * Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
      6 * Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
      7 */
      8
      9#include <linux/interrupt.h>
     10#include <linux/mfd/atc260x/core.h>
     11#include <linux/mfd/core.h>
     12#include <linux/module.h>
     13#include <linux/of.h>
     14#include <linux/of_device.h>
     15#include <linux/regmap.h>
     16
     17#define ATC260X_CHIP_REV_MAX	31
     18
     19struct atc260x_init_regs {
     20	unsigned int cmu_devrst;
     21	unsigned int cmu_devrst_ints;
     22	unsigned int ints_msk;
     23	unsigned int pad_en;
     24	unsigned int pad_en_extirq;
     25};
     26
     27static void regmap_lock_mutex(void *__mutex)
     28{
     29	struct mutex *mutex = __mutex;
     30
     31	/*
     32	 * Using regmap within an atomic context (e.g. accessing a PMIC when
     33	 * powering system down) is normally allowed only if the regmap type
     34	 * is MMIO and the regcache type is either REGCACHE_NONE or
     35	 * REGCACHE_FLAT. For slow buses like I2C and SPI, the regmap is
     36	 * internally protected by a mutex which is acquired non-atomically.
     37	 *
     38	 * Let's improve this by using a customized locking scheme inspired
     39	 * from I2C atomic transfer. See i2c_in_atomic_xfer_mode() for a
     40	 * starting point.
     41	 */
     42	if (system_state > SYSTEM_RUNNING && irqs_disabled())
     43		mutex_trylock(mutex);
     44	else
     45		mutex_lock(mutex);
     46}
     47
     48static void regmap_unlock_mutex(void *__mutex)
     49{
     50	struct mutex *mutex = __mutex;
     51
     52	mutex_unlock(mutex);
     53}
     54
     55static const struct regmap_config atc2603c_regmap_config = {
     56	.reg_bits = 8,
     57	.val_bits = 16,
     58	.max_register = ATC2603C_SADDR,
     59	.cache_type = REGCACHE_NONE,
     60};
     61
     62static const struct regmap_config atc2609a_regmap_config = {
     63	.reg_bits = 8,
     64	.val_bits = 16,
     65	.max_register = ATC2609A_SADDR,
     66	.cache_type = REGCACHE_NONE,
     67};
     68
     69static const struct regmap_irq atc2603c_regmap_irqs[] = {
     70	REGMAP_IRQ_REG(ATC2603C_IRQ_AUDIO,	0, ATC2603C_INTS_MSK_AUDIO),
     71	REGMAP_IRQ_REG(ATC2603C_IRQ_OV,		0, ATC2603C_INTS_MSK_OV),
     72	REGMAP_IRQ_REG(ATC2603C_IRQ_OC,		0, ATC2603C_INTS_MSK_OC),
     73	REGMAP_IRQ_REG(ATC2603C_IRQ_OT,		0, ATC2603C_INTS_MSK_OT),
     74	REGMAP_IRQ_REG(ATC2603C_IRQ_UV,		0, ATC2603C_INTS_MSK_UV),
     75	REGMAP_IRQ_REG(ATC2603C_IRQ_ALARM,	0, ATC2603C_INTS_MSK_ALARM),
     76	REGMAP_IRQ_REG(ATC2603C_IRQ_ONOFF,	0, ATC2603C_INTS_MSK_ONOFF),
     77	REGMAP_IRQ_REG(ATC2603C_IRQ_SGPIO,	0, ATC2603C_INTS_MSK_SGPIO),
     78	REGMAP_IRQ_REG(ATC2603C_IRQ_IR,		0, ATC2603C_INTS_MSK_IR),
     79	REGMAP_IRQ_REG(ATC2603C_IRQ_REMCON,	0, ATC2603C_INTS_MSK_REMCON),
     80	REGMAP_IRQ_REG(ATC2603C_IRQ_POWER_IN,	0, ATC2603C_INTS_MSK_POWERIN),
     81};
     82
     83static const struct regmap_irq atc2609a_regmap_irqs[] = {
     84	REGMAP_IRQ_REG(ATC2609A_IRQ_AUDIO,	0, ATC2609A_INTS_MSK_AUDIO),
     85	REGMAP_IRQ_REG(ATC2609A_IRQ_OV,		0, ATC2609A_INTS_MSK_OV),
     86	REGMAP_IRQ_REG(ATC2609A_IRQ_OC,		0, ATC2609A_INTS_MSK_OC),
     87	REGMAP_IRQ_REG(ATC2609A_IRQ_OT,		0, ATC2609A_INTS_MSK_OT),
     88	REGMAP_IRQ_REG(ATC2609A_IRQ_UV,		0, ATC2609A_INTS_MSK_UV),
     89	REGMAP_IRQ_REG(ATC2609A_IRQ_ALARM,	0, ATC2609A_INTS_MSK_ALARM),
     90	REGMAP_IRQ_REG(ATC2609A_IRQ_ONOFF,	0, ATC2609A_INTS_MSK_ONOFF),
     91	REGMAP_IRQ_REG(ATC2609A_IRQ_WKUP,	0, ATC2609A_INTS_MSK_WKUP),
     92	REGMAP_IRQ_REG(ATC2609A_IRQ_IR,		0, ATC2609A_INTS_MSK_IR),
     93	REGMAP_IRQ_REG(ATC2609A_IRQ_REMCON,	0, ATC2609A_INTS_MSK_REMCON),
     94	REGMAP_IRQ_REG(ATC2609A_IRQ_POWER_IN,	0, ATC2609A_INTS_MSK_POWERIN),
     95};
     96
     97static const struct regmap_irq_chip atc2603c_regmap_irq_chip = {
     98	.name = "atc2603c",
     99	.irqs = atc2603c_regmap_irqs,
    100	.num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs),
    101	.num_regs = 1,
    102	.status_base = ATC2603C_INTS_PD,
    103	.mask_base = ATC2603C_INTS_MSK,
    104	.mask_invert = true,
    105};
    106
    107static const struct regmap_irq_chip atc2609a_regmap_irq_chip = {
    108	.name = "atc2609a",
    109	.irqs = atc2609a_regmap_irqs,
    110	.num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs),
    111	.num_regs = 1,
    112	.status_base = ATC2609A_INTS_PD,
    113	.mask_base = ATC2609A_INTS_MSK,
    114	.mask_invert = true,
    115};
    116
    117static const struct resource atc2603c_onkey_resources[] = {
    118	DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF),
    119};
    120
    121static const struct resource atc2609a_onkey_resources[] = {
    122	DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF),
    123};
    124
    125static const struct mfd_cell atc2603c_mfd_cells[] = {
    126	{ .name = "atc260x-regulator" },
    127	{ .name = "atc260x-pwrc" },
    128	{
    129		.name = "atc260x-onkey",
    130		.num_resources = ARRAY_SIZE(atc2603c_onkey_resources),
    131		.resources = atc2603c_onkey_resources,
    132	},
    133};
    134
    135static const struct mfd_cell atc2609a_mfd_cells[] = {
    136	{ .name = "atc260x-regulator" },
    137	{ .name = "atc260x-pwrc" },
    138	{
    139		.name = "atc260x-onkey",
    140		.num_resources = ARRAY_SIZE(atc2609a_onkey_resources),
    141		.resources = atc2609a_onkey_resources,
    142	},
    143};
    144
    145static const struct atc260x_init_regs atc2603c_init_regs = {
    146	.cmu_devrst = ATC2603C_CMU_DEVRST,
    147	.cmu_devrst_ints = ATC2603C_CMU_DEVRST_INTS,
    148	.ints_msk = ATC2603C_INTS_MSK,
    149	.pad_en = ATC2603C_PAD_EN,
    150	.pad_en_extirq = ATC2603C_PAD_EN_EXTIRQ,
    151};
    152
    153static const struct atc260x_init_regs atc2609a_init_regs = {
    154	.cmu_devrst = ATC2609A_CMU_DEVRST,
    155	.cmu_devrst_ints = ATC2609A_CMU_DEVRST_INTS,
    156	.ints_msk = ATC2609A_INTS_MSK,
    157	.pad_en = ATC2609A_PAD_EN,
    158	.pad_en_extirq = ATC2609A_PAD_EN_EXTIRQ,
    159};
    160
    161static void atc260x_cmu_reset(struct atc260x *atc260x)
    162{
    163	const struct atc260x_init_regs *regs = atc260x->init_regs;
    164
    165	/* Assert reset */
    166	regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
    167			   regs->cmu_devrst_ints, ~regs->cmu_devrst_ints);
    168
    169	/* De-assert reset */
    170	regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
    171			   regs->cmu_devrst_ints, regs->cmu_devrst_ints);
    172}
    173
    174static void atc260x_dev_init(struct atc260x *atc260x)
    175{
    176	const struct atc260x_init_regs *regs = atc260x->init_regs;
    177
    178	/* Initialize interrupt block */
    179	atc260x_cmu_reset(atc260x);
    180
    181	/* Disable all interrupt sources */
    182	regmap_write(atc260x->regmap, regs->ints_msk, 0);
    183
    184	/* Enable EXTIRQ pad */
    185	regmap_update_bits(atc260x->regmap, regs->pad_en,
    186			   regs->pad_en_extirq, regs->pad_en_extirq);
    187}
    188
    189/**
    190 * atc260x_match_device(): Setup ATC260x variant related fields
    191 *
    192 * @atc260x: ATC260x device to setup (.dev field must be set)
    193 * @regmap_cfg: regmap config associated with this ATC260x device
    194 *
    195 * This lets the ATC260x core configure the MFD cells and register maps
    196 * for later use.
    197 */
    198int atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_cfg)
    199{
    200	struct device *dev = atc260x->dev;
    201	const void *of_data;
    202
    203	of_data = of_device_get_match_data(dev);
    204	if (!of_data)
    205		return -ENODEV;
    206
    207	atc260x->ic_type = (unsigned long)of_data;
    208
    209	switch (atc260x->ic_type) {
    210	case ATC2603C:
    211		*regmap_cfg = atc2603c_regmap_config;
    212		atc260x->regmap_irq_chip = &atc2603c_regmap_irq_chip;
    213		atc260x->cells = atc2603c_mfd_cells;
    214		atc260x->nr_cells = ARRAY_SIZE(atc2603c_mfd_cells);
    215		atc260x->type_name = "atc2603c";
    216		atc260x->rev_reg = ATC2603C_CHIP_VER;
    217		atc260x->init_regs = &atc2603c_init_regs;
    218		break;
    219	case ATC2609A:
    220		*regmap_cfg = atc2609a_regmap_config;
    221		atc260x->regmap_irq_chip = &atc2609a_regmap_irq_chip;
    222		atc260x->cells = atc2609a_mfd_cells;
    223		atc260x->nr_cells = ARRAY_SIZE(atc2609a_mfd_cells);
    224		atc260x->type_name = "atc2609a";
    225		atc260x->rev_reg = ATC2609A_CHIP_VER;
    226		atc260x->init_regs = &atc2609a_init_regs;
    227		break;
    228	default:
    229		dev_err(dev, "Unsupported ATC260x device type: %u\n",
    230			atc260x->ic_type);
    231		return -EINVAL;
    232	}
    233
    234	atc260x->regmap_mutex = devm_kzalloc(dev, sizeof(*atc260x->regmap_mutex),
    235					     GFP_KERNEL);
    236	if (!atc260x->regmap_mutex)
    237		return -ENOMEM;
    238
    239	mutex_init(atc260x->regmap_mutex);
    240
    241	regmap_cfg->lock = regmap_lock_mutex,
    242	regmap_cfg->unlock = regmap_unlock_mutex,
    243	regmap_cfg->lock_arg = atc260x->regmap_mutex;
    244
    245	return 0;
    246}
    247EXPORT_SYMBOL_GPL(atc260x_match_device);
    248
    249/**
    250 * atc260x_device_probe(): Probe a configured ATC260x device
    251 *
    252 * @atc260x: ATC260x device to probe (must be configured)
    253 *
    254 * This function lets the ATC260x core register the ATC260x MFD devices
    255 * and IRQCHIP. The ATC260x device passed in must be fully configured
    256 * with atc260x_match_device, its IRQ set, and regmap created.
    257 */
    258int atc260x_device_probe(struct atc260x *atc260x)
    259{
    260	struct device *dev = atc260x->dev;
    261	unsigned int chip_rev;
    262	int ret;
    263
    264	if (!atc260x->irq) {
    265		dev_err(dev, "No interrupt support\n");
    266		return -EINVAL;
    267	}
    268
    269	/* Initialize the hardware */
    270	atc260x_dev_init(atc260x);
    271
    272	ret = regmap_read(atc260x->regmap, atc260x->rev_reg, &chip_rev);
    273	if (ret) {
    274		dev_err(dev, "Failed to get chip revision\n");
    275		return ret;
    276	}
    277
    278	if (chip_rev > ATC260X_CHIP_REV_MAX) {
    279		dev_err(dev, "Unknown chip revision: %u\n", chip_rev);
    280		return -EINVAL;
    281	}
    282
    283	atc260x->ic_ver = __ffs(chip_rev + 1U);
    284
    285	dev_info(dev, "Detected chip type %s rev.%c\n",
    286		 atc260x->type_name, 'A' + atc260x->ic_ver);
    287
    288	ret = devm_regmap_add_irq_chip(dev, atc260x->regmap, atc260x->irq, IRQF_ONESHOT,
    289				       -1, atc260x->regmap_irq_chip, &atc260x->irq_data);
    290	if (ret) {
    291		dev_err(dev, "Failed to add IRQ chip: %d\n", ret);
    292		return ret;
    293	}
    294
    295	ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
    296				   atc260x->cells, atc260x->nr_cells, NULL, 0,
    297				   regmap_irq_get_domain(atc260x->irq_data));
    298	if (ret) {
    299		dev_err(dev, "Failed to add child devices: %d\n", ret);
    300		regmap_del_irq_chip(atc260x->irq, atc260x->irq_data);
    301	}
    302
    303	return ret;
    304}
    305EXPORT_SYMBOL_GPL(atc260x_device_probe);
    306
    307MODULE_DESCRIPTION("ATC260x PMICs Core support");
    308MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
    309MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
    310MODULE_LICENSE("GPL");