mfd: Add Ampere's Altra SMpro MFD driver

Adds Multi-function devices driver for SMpro co-processor found on the
Mt.Jade hardware reference platform with Ampere's Altra processor family.

Signed-off-by: Quan Nguyen <quan@os.amperecomputing.com>
Signed-off-by: Lee Jones <lee@kernel.org>
Link: https://lore.kernel.org/r/20220929094321.770125-9-quan@os.amperecomputing.com
This commit is contained in:
Quan Nguyen 2022-09-29 16:43:20 +07:00 committed by Lee Jones
parent 763ab98687
commit f19fd81ba1
3 changed files with 151 additions and 0 deletions

View file

@ -77,6 +77,18 @@ config MFD_AS3711
help
Support for the AS3711 PMIC from AMS
config MFD_SMPRO
tristate "Ampere Computing SMpro core driver"
depends on I2C
select MFD_CORE
select REGMAP_I2C
help
Say yes here to enable SMpro driver support for Ampere's Altra
processor family.
Ampere's Altra SMpro exposes an I2C regmap interface that can
be accessed by child devices.
config MFD_AS3722
tristate "ams AS3722 Power Management IC"
select MFD_CORE

View file

@ -271,6 +271,7 @@ obj-$(CONFIG_MFD_QCOM_PM8008) += qcom-pm8008.o
obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o
obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o
obj-$(CONFIG_MFD_SMPRO) += smpro-core.o
obj-$(CONFIG_MFD_INTEL_M10_BMC) += intel-m10-bmc.o
obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o

138
drivers/mfd/smpro-core.c Normal file
View file

@ -0,0 +1,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_new = 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");