1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* Ampere Altra Family SMPro core driver
* Copyright (c) 2022, Ampere Computing LLC
*/
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
/* Identification Registers */
#define MANUFACTURER_ID_REG 0x02
#define AMPERE_MANUFACTURER_ID 0xCD3A
#define CORE_CE_ERR_DATA 0x82
#define CORE_UE_ERR_DATA 0x85
#define MEM_CE_ERR_DATA 0x92
#define MEM_UE_ERR_DATA 0x95
#define PCIE_CE_ERR_DATA 0xC2
#define PCIE_UE_ERR_DATA 0xC5
#define OTHER_CE_ERR_DATA 0xD2
#define OTHER_UE_ERR_DATA 0xDA
static int smpro_core_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret;
ret = i2c_master_send(i2c, data, count);
if (unlikely(ret != count))
return (ret < 0) ? ret : -EIO;
return 0;
}
static int smpro_core_read(void *context, const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct i2c_msg xfer[2];
unsigned char buf[2];
int ret;
xfer[0].addr = i2c->addr;
xfer[0].flags = 0;
buf[0] = *(u8 *)reg;
buf[1] = val_size;
xfer[0].len = 2;
xfer[0].buf = buf;
xfer[1].addr = i2c->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = val_size;
xfer[1].buf = val;
ret = i2c_transfer(i2c->adapter, xfer, 2);
if (unlikely(ret != 2))
return (ret < 0) ? ret : -EIO;
return 0;
}
static const struct regmap_bus smpro_regmap_bus = {
.read = smpro_core_read,
.write = smpro_core_write,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
static bool smpro_core_readable_noinc_reg(struct device *dev, unsigned int reg)
{
return (reg == CORE_CE_ERR_DATA || reg == CORE_UE_ERR_DATA ||
reg == MEM_CE_ERR_DATA || reg == MEM_UE_ERR_DATA ||
reg == PCIE_CE_ERR_DATA || reg == PCIE_UE_ERR_DATA ||
reg == OTHER_CE_ERR_DATA || reg == OTHER_UE_ERR_DATA);
}
static const struct regmap_config smpro_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.readable_noinc_reg = smpro_core_readable_noinc_reg,
};
static const struct mfd_cell smpro_devs[] = {
MFD_CELL_NAME("smpro-hwmon"),
MFD_CELL_NAME("smpro-errmon"),
MFD_CELL_NAME("smpro-misc"),
};
static int smpro_core_probe(struct i2c_client *i2c)
{
const struct regmap_config *config;
struct regmap *regmap;
unsigned int val;
int ret;
config = device_get_match_data(&i2c->dev);
if (!config)
return -EINVAL;
regmap = devm_regmap_init(&i2c->dev, &smpro_regmap_bus, &i2c->dev, config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
ret = regmap_read(regmap, MANUFACTURER_ID_REG, &val);
if (ret)
return ret;
if (val != AMPERE_MANUFACTURER_ID)
return -ENODEV;
return devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
smpro_devs, ARRAY_SIZE(smpro_devs), NULL, 0, NULL);
}
static const struct of_device_id smpro_core_of_match[] = {
{ .compatible = "ampere,smpro", .data = &smpro_regmap_config },
{}
};
MODULE_DEVICE_TABLE(of, smpro_core_of_match);
static struct i2c_driver smpro_core_driver = {
.probe = smpro_core_probe,
.driver = {
.name = "smpro-core",
.of_match_table = smpro_core_of_match,
},
};
module_i2c_driver(smpro_core_driver);
MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");
MODULE_DESCRIPTION("SMPRO CORE - I2C driver");
MODULE_LICENSE("GPL");
|