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

88pm80x.c (3879B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * I2C driver for Marvell 88PM80x
      4 *
      5 * Copyright (C) 2012 Marvell International Ltd.
      6 * Haojian Zhuang <haojian.zhuang@marvell.com>
      7 * Joseph(Yossi) Hanin <yhanin@marvell.com>
      8 * Qiao Zhou <zhouqiao@marvell.com>
      9 */
     10#include <linux/kernel.h>
     11#include <linux/module.h>
     12#include <linux/i2c.h>
     13#include <linux/mfd/88pm80x.h>
     14#include <linux/slab.h>
     15#include <linux/uaccess.h>
     16#include <linux/err.h>
     17
     18/* 88pm80x chips have same definition for chip id register. */
     19#define PM80X_CHIP_ID			(0x00)
     20#define PM80X_CHIP_ID_NUM(x)		(((x) >> 5) & 0x7)
     21#define PM80X_CHIP_ID_REVISION(x)	((x) & 0x1F)
     22
     23struct pm80x_chip_mapping {
     24	unsigned int	id;
     25	int		type;
     26};
     27
     28static struct pm80x_chip_mapping chip_mapping[] = {
     29	/* 88PM800 chip id number */
     30	{0x3,	CHIP_PM800},
     31	/* 88PM805 chip id number */
     32	{0x0,	CHIP_PM805},
     33	/* 88PM860 chip id number */
     34	{0x4,	CHIP_PM860},
     35};
     36
     37/*
     38 * workaround: some registers needed by pm805 are defined in pm800, so
     39 * need to use this global variable to maintain the relation between
     40 * pm800 and pm805. would remove it after HW chip fixes the issue.
     41 */
     42static struct pm80x_chip *g_pm80x_chip;
     43
     44const struct regmap_config pm80x_regmap_config = {
     45	.reg_bits = 8,
     46	.val_bits = 8,
     47};
     48EXPORT_SYMBOL_GPL(pm80x_regmap_config);
     49
     50
     51int pm80x_init(struct i2c_client *client)
     52{
     53	struct pm80x_chip *chip;
     54	struct regmap *map;
     55	unsigned int val;
     56	int i, ret = 0;
     57
     58	chip =
     59	    devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL);
     60	if (!chip)
     61		return -ENOMEM;
     62
     63	map = devm_regmap_init_i2c(client, &pm80x_regmap_config);
     64	if (IS_ERR(map)) {
     65		ret = PTR_ERR(map);
     66		dev_err(&client->dev, "Failed to allocate register map: %d\n",
     67			ret);
     68		return ret;
     69	}
     70
     71	chip->client = client;
     72	chip->regmap = map;
     73
     74	chip->irq = client->irq;
     75
     76	chip->dev = &client->dev;
     77	dev_set_drvdata(chip->dev, chip);
     78	i2c_set_clientdata(chip->client, chip);
     79
     80	ret = regmap_read(chip->regmap, PM80X_CHIP_ID, &val);
     81	if (ret < 0) {
     82		dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
     83		return ret;
     84	}
     85
     86	for (i = 0; i < ARRAY_SIZE(chip_mapping); i++) {
     87		if (chip_mapping[i].id == PM80X_CHIP_ID_NUM(val)) {
     88			chip->type = chip_mapping[i].type;
     89			break;
     90		}
     91	}
     92
     93	if (i == ARRAY_SIZE(chip_mapping)) {
     94		dev_err(chip->dev,
     95			"Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val);
     96		return -EINVAL;
     97	}
     98
     99	device_init_wakeup(&client->dev, 1);
    100
    101	/*
    102	 * workaround: set g_pm80x_chip to the first probed chip. if the
    103	 * second chip is probed, just point to the companion to each
    104	 * other so that pm805 can access those specific register. would
    105	 * remove it after HW chip fixes the issue.
    106	 */
    107	if (!g_pm80x_chip)
    108		g_pm80x_chip = chip;
    109	else {
    110		chip->companion = g_pm80x_chip->client;
    111		g_pm80x_chip->companion = chip->client;
    112	}
    113
    114	return 0;
    115}
    116EXPORT_SYMBOL_GPL(pm80x_init);
    117
    118int pm80x_deinit(void)
    119{
    120	/*
    121	 * workaround: clear the dependency between pm800 and pm805.
    122	 * would remove it after HW chip fixes the issue.
    123	 */
    124	if (g_pm80x_chip->companion)
    125		g_pm80x_chip->companion = NULL;
    126	else
    127		g_pm80x_chip = NULL;
    128	return 0;
    129}
    130EXPORT_SYMBOL_GPL(pm80x_deinit);
    131
    132#ifdef CONFIG_PM_SLEEP
    133static int pm80x_suspend(struct device *dev)
    134{
    135	struct i2c_client *client = to_i2c_client(dev);
    136	struct pm80x_chip *chip = i2c_get_clientdata(client);
    137
    138	if (chip && chip->wu_flag)
    139		if (device_may_wakeup(chip->dev))
    140			enable_irq_wake(chip->irq);
    141
    142	return 0;
    143}
    144
    145static int pm80x_resume(struct device *dev)
    146{
    147	struct i2c_client *client = to_i2c_client(dev);
    148	struct pm80x_chip *chip = i2c_get_clientdata(client);
    149
    150	if (chip && chip->wu_flag)
    151		if (device_may_wakeup(chip->dev))
    152			disable_irq_wake(chip->irq);
    153
    154	return 0;
    155}
    156#endif
    157
    158SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume);
    159EXPORT_SYMBOL_GPL(pm80x_pm_ops);
    160
    161MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x");
    162MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
    163MODULE_LICENSE("GPL");