aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/smpro-core.c
blob: 59d31590c69b741540e84935c28864e6ce405b03 (plain)
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");