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