brintos

brintos / linux-shallow public Read only

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