1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Intel MAX 10 Board Management Controller chip |
4 | * |
5 | * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. |
6 | */ |
7 | #include <linux/bitfield.h> |
8 | #include <linux/dev_printk.h> |
9 | #include <linux/init.h> |
10 | #include <linux/mfd/core.h> |
11 | #include <linux/mfd/intel-m10-bmc.h> |
12 | #include <linux/module.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/spi/spi.h> |
15 | |
16 | static const struct regmap_range m10bmc_regmap_range[] = { |
17 | regmap_reg_range(M10BMC_N3000_LEGACY_BUILD_VER, M10BMC_N3000_LEGACY_BUILD_VER), |
18 | regmap_reg_range(M10BMC_N3000_SYS_BASE, M10BMC_N3000_SYS_END), |
19 | regmap_reg_range(M10BMC_N3000_FLASH_BASE, M10BMC_N3000_FLASH_END), |
20 | }; |
21 | |
22 | static const struct regmap_access_table m10bmc_access_table = { |
23 | .yes_ranges = m10bmc_regmap_range, |
24 | .n_yes_ranges = ARRAY_SIZE(m10bmc_regmap_range), |
25 | }; |
26 | |
27 | static struct regmap_config intel_m10bmc_regmap_config = { |
28 | .reg_bits = 32, |
29 | .val_bits = 32, |
30 | .reg_stride = 4, |
31 | .wr_table = &m10bmc_access_table, |
32 | .rd_table = &m10bmc_access_table, |
33 | .max_register = M10BMC_N3000_MEM_END, |
34 | }; |
35 | |
36 | static int check_m10bmc_version(struct intel_m10bmc *ddata) |
37 | { |
38 | unsigned int v; |
39 | int ret; |
40 | |
41 | /* |
42 | * This check is to filter out the very old legacy BMC versions. In the |
43 | * old BMC chips, the BMC version info is stored in the old version |
44 | * register (M10BMC_N3000_LEGACY_BUILD_VER), so its read out value would have |
45 | * not been M10BMC_N3000_VER_LEGACY_INVALID (0xffffffff). But in new BMC |
46 | * chips that the driver supports, the value of this register should be |
47 | * M10BMC_N3000_VER_LEGACY_INVALID. |
48 | */ |
49 | ret = m10bmc_raw_read(m10bmc: ddata, M10BMC_N3000_LEGACY_BUILD_VER, val: &v); |
50 | if (ret) |
51 | return -ENODEV; |
52 | |
53 | if (v != M10BMC_N3000_VER_LEGACY_INVALID) { |
54 | dev_err(ddata->dev, "bad version M10BMC detected\n" ); |
55 | return -ENODEV; |
56 | } |
57 | |
58 | return 0; |
59 | } |
60 | |
61 | static int intel_m10_bmc_spi_probe(struct spi_device *spi) |
62 | { |
63 | const struct spi_device_id *id = spi_get_device_id(sdev: spi); |
64 | const struct intel_m10bmc_platform_info *info; |
65 | struct device *dev = &spi->dev; |
66 | struct intel_m10bmc *ddata; |
67 | int ret; |
68 | |
69 | ddata = devm_kzalloc(dev, size: sizeof(*ddata), GFP_KERNEL); |
70 | if (!ddata) |
71 | return -ENOMEM; |
72 | |
73 | info = (struct intel_m10bmc_platform_info *)id->driver_data; |
74 | ddata->dev = dev; |
75 | |
76 | ddata->regmap = devm_regmap_init_spi_avmm(spi, &intel_m10bmc_regmap_config); |
77 | if (IS_ERR(ptr: ddata->regmap)) { |
78 | ret = PTR_ERR(ptr: ddata->regmap); |
79 | dev_err(dev, "Failed to allocate regmap: %d\n" , ret); |
80 | return ret; |
81 | } |
82 | |
83 | spi_set_drvdata(spi, data: ddata); |
84 | |
85 | ret = check_m10bmc_version(ddata); |
86 | if (ret) { |
87 | dev_err(dev, "Failed to identify m10bmc hardware\n" ); |
88 | return ret; |
89 | } |
90 | |
91 | return m10bmc_dev_init(m10bmc: ddata, info); |
92 | } |
93 | |
94 | static const struct m10bmc_csr_map m10bmc_n3000_csr_map = { |
95 | .base = M10BMC_N3000_SYS_BASE, |
96 | .build_version = M10BMC_N3000_BUILD_VER, |
97 | .fw_version = NIOS2_N3000_FW_VERSION, |
98 | .mac_low = M10BMC_N3000_MAC_LOW, |
99 | .mac_high = M10BMC_N3000_MAC_HIGH, |
100 | .doorbell = M10BMC_N3000_DOORBELL, |
101 | .auth_result = M10BMC_N3000_AUTH_RESULT, |
102 | .bmc_prog_addr = M10BMC_N3000_BMC_PROG_ADDR, |
103 | .bmc_reh_addr = M10BMC_N3000_BMC_REH_ADDR, |
104 | .bmc_magic = M10BMC_N3000_BMC_PROG_MAGIC, |
105 | .sr_prog_addr = M10BMC_N3000_SR_PROG_ADDR, |
106 | .sr_reh_addr = M10BMC_N3000_SR_REH_ADDR, |
107 | .sr_magic = M10BMC_N3000_SR_PROG_MAGIC, |
108 | .pr_prog_addr = M10BMC_N3000_PR_PROG_ADDR, |
109 | .pr_reh_addr = M10BMC_N3000_PR_REH_ADDR, |
110 | .pr_magic = M10BMC_N3000_PR_PROG_MAGIC, |
111 | .rsu_update_counter = M10BMC_N3000_STAGING_FLASH_COUNT, |
112 | }; |
113 | |
114 | static struct mfd_cell m10bmc_d5005_subdevs[] = { |
115 | { .name = "d5005bmc-hwmon" }, |
116 | { .name = "d5005bmc-sec-update" }, |
117 | }; |
118 | |
119 | static const struct regmap_range m10bmc_d5005_fw_handshake_regs[] = { |
120 | regmap_reg_range(M10BMC_N3000_TELEM_START, M10BMC_D5005_TELEM_END), |
121 | }; |
122 | |
123 | static struct mfd_cell m10bmc_pacn3000_subdevs[] = { |
124 | { .name = "n3000bmc-hwmon" }, |
125 | { .name = "n3000bmc-retimer" }, |
126 | { .name = "n3000bmc-sec-update" }, |
127 | }; |
128 | |
129 | static const struct regmap_range m10bmc_n3000_fw_handshake_regs[] = { |
130 | regmap_reg_range(M10BMC_N3000_TELEM_START, M10BMC_N3000_TELEM_END), |
131 | }; |
132 | |
133 | static struct mfd_cell m10bmc_n5010_subdevs[] = { |
134 | { .name = "n5010bmc-hwmon" }, |
135 | }; |
136 | |
137 | static const struct intel_m10bmc_platform_info m10bmc_spi_n3000 = { |
138 | .cells = m10bmc_pacn3000_subdevs, |
139 | .n_cells = ARRAY_SIZE(m10bmc_pacn3000_subdevs), |
140 | .handshake_sys_reg_ranges = m10bmc_n3000_fw_handshake_regs, |
141 | .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_n3000_fw_handshake_regs), |
142 | .csr_map = &m10bmc_n3000_csr_map, |
143 | }; |
144 | |
145 | static const struct intel_m10bmc_platform_info m10bmc_spi_d5005 = { |
146 | .cells = m10bmc_d5005_subdevs, |
147 | .n_cells = ARRAY_SIZE(m10bmc_d5005_subdevs), |
148 | .handshake_sys_reg_ranges = m10bmc_d5005_fw_handshake_regs, |
149 | .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_d5005_fw_handshake_regs), |
150 | .csr_map = &m10bmc_n3000_csr_map, |
151 | }; |
152 | |
153 | static const struct intel_m10bmc_platform_info m10bmc_spi_n5010 = { |
154 | .cells = m10bmc_n5010_subdevs, |
155 | .n_cells = ARRAY_SIZE(m10bmc_n5010_subdevs), |
156 | .handshake_sys_reg_ranges = m10bmc_n3000_fw_handshake_regs, |
157 | .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_n3000_fw_handshake_regs), |
158 | .csr_map = &m10bmc_n3000_csr_map, |
159 | }; |
160 | |
161 | static const struct spi_device_id m10bmc_spi_id[] = { |
162 | { "m10-n3000" , (kernel_ulong_t)&m10bmc_spi_n3000 }, |
163 | { "m10-d5005" , (kernel_ulong_t)&m10bmc_spi_d5005 }, |
164 | { "m10-n5010" , (kernel_ulong_t)&m10bmc_spi_n5010 }, |
165 | { } |
166 | }; |
167 | MODULE_DEVICE_TABLE(spi, m10bmc_spi_id); |
168 | |
169 | static struct spi_driver intel_m10bmc_spi_driver = { |
170 | .driver = { |
171 | .name = "intel-m10-bmc" , |
172 | .dev_groups = m10bmc_dev_groups, |
173 | }, |
174 | .probe = intel_m10_bmc_spi_probe, |
175 | .id_table = m10bmc_spi_id, |
176 | }; |
177 | module_spi_driver(intel_m10bmc_spi_driver); |
178 | |
179 | MODULE_DESCRIPTION("Intel MAX 10 BMC SPI bus interface" ); |
180 | MODULE_AUTHOR("Intel Corporation" ); |
181 | MODULE_LICENSE("GPL v2" ); |
182 | MODULE_ALIAS("spi:intel-m10-bmc" ); |
183 | MODULE_IMPORT_NS(INTEL_M10_BMC_CORE); |
184 | |