File Index
Files and directories
Sign in
FILE

upboard-fpga.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mfd/upboard-fpga.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * UP Board FPGA driver.
  4 *
  5 * FPGA provides more GPIO driving power, LEDS and pin mux function.
  6 *
  7 * Copyright (c) AAEON. All rights reserved.
  8 * Copyright (C) 2024 Bootlin
  9 *
 10 * Author: Gary Wang <garywang@aaeon.com.tw>
 11 * Author: Thomas Richard <thomas.richard@bootlin.com>
 12 */
 13
 14#include <linux/bitfield.h>
 15#include <linux/device.h>
 16#include <linux/err.h>
 17#include <linux/gpio/consumer.h>
 18#include <linux/mfd/core.h>
 19#include <linux/mfd/upboard-fpga.h>
 20#include <linux/module.h>
 21#include <linux/mod_devicetable.h>
 22#include <linux/platform_device.h>
 23#include <linux/property.h>
 24#include <linux/regmap.h>
 25#include <linux/sysfs.h>
 26
 27#define UPBOARD_AAEON_MANUFACTURER_ID	0x01
 28#define UPBOARD_MANUFACTURER_ID_MASK	GENMASK(7, 0)
 29
 30#define UPBOARD_ADDRESS_SIZE  7
 31#define UPBOARD_REGISTER_SIZE 16
 32
 33#define UPBOARD_READ_FLAG     BIT(UPBOARD_ADDRESS_SIZE)
 34
 35#define UPBOARD_FW_ID_MAJOR_SUPPORTED	0x0
 36
 37#define UPBOARD_FW_ID_BUILD_MASK	GENMASK(15, 12)
 38#define UPBOARD_FW_ID_MAJOR_MASK	GENMASK(11, 8)
 39#define UPBOARD_FW_ID_MINOR_MASK	GENMASK(7, 4)
 40#define UPBOARD_FW_ID_PATCH_MASK	GENMASK(3, 0)
 41
 42static int upboard_fpga_read(void *context, unsigned int reg, unsigned int *val)
 43{
 44	struct upboard_fpga *fpga = context;
 45	int i;
 46
 47	/* Clear to start new transaction */
 48	gpiod_set_value(fpga->clear_gpio, 0);
 49	gpiod_set_value(fpga->clear_gpio, 1);
 50
 51	reg |= UPBOARD_READ_FLAG;
 52
 53	/* Send clock and addr from strobe & datain pins */
 54	for (i = UPBOARD_ADDRESS_SIZE; i >= 0; i--) {
 55		gpiod_set_value(fpga->strobe_gpio, 0);
 56		gpiod_set_value(fpga->datain_gpio, !!(reg & BIT(i)));
 57		gpiod_set_value(fpga->strobe_gpio, 1);
 58	}
 59
 60	gpiod_set_value(fpga->strobe_gpio, 0);
 61	*val = 0;
 62
 63	/* Read data from dataout pin */
 64	for (i = UPBOARD_REGISTER_SIZE - 1; i >= 0; i--) {
 65		gpiod_set_value(fpga->strobe_gpio, 1);
 66		gpiod_set_value(fpga->strobe_gpio, 0);
 67		*val |= gpiod_get_value(fpga->dataout_gpio) << i;
 68	}
 69
 70	gpiod_set_value(fpga->strobe_gpio, 1);
 71
 72	return 0;
 73}
 74
 75static int upboard_fpga_write(void *context, unsigned int reg, unsigned int val)
 76{
 77	struct upboard_fpga *fpga = context;
 78	int i;
 79
 80	/* Clear to start new transcation */
 81	gpiod_set_value(fpga->clear_gpio, 0);
 82	gpiod_set_value(fpga->clear_gpio, 1);
 83
 84	/* Send clock and addr from strobe & datain pins */
 85	for (i = UPBOARD_ADDRESS_SIZE; i >= 0; i--) {
 86		gpiod_set_value(fpga->strobe_gpio, 0);
 87		gpiod_set_value(fpga->datain_gpio, !!(reg & BIT(i)));
 88		gpiod_set_value(fpga->strobe_gpio, 1);
 89	}
 90
 91	gpiod_set_value(fpga->strobe_gpio, 0);
 92
 93	/* Write data to datain pin */
 94	for (i = UPBOARD_REGISTER_SIZE - 1; i >= 0; i--) {
 95		gpiod_set_value(fpga->datain_gpio, !!(val & BIT(i)));
 96		gpiod_set_value(fpga->strobe_gpio, 1);
 97		gpiod_set_value(fpga->strobe_gpio, 0);
 98	}
 99
100	gpiod_set_value(fpga->strobe_gpio, 1);
101
102	return 0;
103}
104
105static const struct regmap_range upboard_up_readable_ranges[] = {
106	regmap_reg_range(UPBOARD_REG_PLATFORM_ID, UPBOARD_REG_FIRMWARE_ID),
107	regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN0),
108	regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN1),
109	regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_REG_GPIO_DIR1),
110};
111
112static const struct regmap_range upboard_up_writable_ranges[] = {
113	regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN0),
114	regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN1),
115	regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_REG_GPIO_DIR1),
116};
117
118static const struct regmap_access_table upboard_up_readable_table = {
119	.yes_ranges = upboard_up_readable_ranges,
120	.n_yes_ranges = ARRAY_SIZE(upboard_up_readable_ranges),
121};
122
123static const struct regmap_access_table upboard_up_writable_table = {
124	.yes_ranges = upboard_up_writable_ranges,
125	.n_yes_ranges = ARRAY_SIZE(upboard_up_writable_ranges),
126};
127
128static const struct regmap_config upboard_up_regmap_config = {
129	.reg_bits = UPBOARD_ADDRESS_SIZE,
130	.val_bits = UPBOARD_REGISTER_SIZE,
131	.max_register = UPBOARD_REG_MAX,
132	.reg_read = upboard_fpga_read,
133	.reg_write = upboard_fpga_write,
134	.fast_io = false,
135	.cache_type = REGCACHE_NONE,
136	.rd_table = &upboard_up_readable_table,
137	.wr_table = &upboard_up_writable_table,
138};
139
140static const struct regmap_range upboard_up2_readable_ranges[] = {
141	regmap_reg_range(UPBOARD_REG_PLATFORM_ID, UPBOARD_REG_FIRMWARE_ID),
142	regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN1),
143	regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN2),
144	regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_REG_GPIO_DIR2),
145};
146
147static const struct regmap_range upboard_up2_writable_ranges[] = {
148	regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN1),
149	regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN2),
150	regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_REG_GPIO_DIR2),
151};
152
153static const struct regmap_access_table upboard_up2_readable_table = {
154	.yes_ranges = upboard_up2_readable_ranges,
155	.n_yes_ranges = ARRAY_SIZE(upboard_up2_readable_ranges),
156};
157
158static const struct regmap_access_table upboard_up2_writable_table = {
159	.yes_ranges = upboard_up2_writable_ranges,
160	.n_yes_ranges = ARRAY_SIZE(upboard_up2_writable_ranges),
161};
162
163static const struct regmap_config upboard_up2_regmap_config = {
164	.reg_bits = UPBOARD_ADDRESS_SIZE,
165	.val_bits = UPBOARD_REGISTER_SIZE,
166	.max_register = UPBOARD_REG_MAX,
167	.reg_read = upboard_fpga_read,
168	.reg_write = upboard_fpga_write,
169	.fast_io = false,
170	.cache_type = REGCACHE_NONE,
171	.rd_table = &upboard_up2_readable_table,
172	.wr_table = &upboard_up2_writable_table,
173};
174
175static const struct mfd_cell upboard_up_mfd_cells[] = {
176	{ .name = "upboard-pinctrl" },
177	{ .name = "upboard-leds" },
178};
179
180static const struct upboard_fpga_data upboard_up_fpga_data = {
181	.type = UPBOARD_UP_FPGA,
182	.regmap_config = &upboard_up_regmap_config,
183};
184
185static const struct upboard_fpga_data upboard_up2_fpga_data = {
186	.type = UPBOARD_UP2_FPGA,
187	.regmap_config = &upboard_up2_regmap_config,
188};
189
190static int upboard_fpga_gpio_init(struct upboard_fpga *fpga)
191{
192	fpga->enable_gpio = devm_gpiod_get(fpga->dev, "enable", GPIOD_ASIS);
193	if (IS_ERR(fpga->enable_gpio))
194		return PTR_ERR(fpga->enable_gpio);
195
196	fpga->clear_gpio = devm_gpiod_get(fpga->dev, "clear", GPIOD_OUT_LOW);
197	if (IS_ERR(fpga->clear_gpio))
198		return PTR_ERR(fpga->clear_gpio);
199
200	fpga->strobe_gpio = devm_gpiod_get(fpga->dev, "strobe", GPIOD_OUT_LOW);
201	if (IS_ERR(fpga->strobe_gpio))
202		return PTR_ERR(fpga->strobe_gpio);
203
204	fpga->datain_gpio = devm_gpiod_get(fpga->dev, "datain", GPIOD_OUT_LOW);
205	if (IS_ERR(fpga->datain_gpio))
206		return PTR_ERR(fpga->datain_gpio);
207
208	fpga->dataout_gpio = devm_gpiod_get(fpga->dev, "dataout", GPIOD_IN);
209	if (IS_ERR(fpga->dataout_gpio))
210		return PTR_ERR(fpga->dataout_gpio);
211
212	gpiod_set_value(fpga->enable_gpio, 1);
213
214	return 0;
215}
216
217static int upboard_fpga_get_firmware_version(struct upboard_fpga *fpga)
218{
219	unsigned int platform_id, manufacturer_id;
220	int ret;
221
222	if (!fpga)
223		return -ENOMEM;
224
225	ret = regmap_read(fpga->regmap, UPBOARD_REG_PLATFORM_ID, &platform_id);
226	if (ret)
227		return ret;
228
229	manufacturer_id = platform_id & UPBOARD_MANUFACTURER_ID_MASK;
230	if (manufacturer_id != UPBOARD_AAEON_MANUFACTURER_ID)
231		return dev_err_probe(fpga->dev, -ENODEV,
232				     "driver not compatible with custom FPGA FW from manufacturer id %#02x.",
233				     manufacturer_id);
234
235	ret = regmap_read(fpga->regmap, UPBOARD_REG_FIRMWARE_ID, &fpga->firmware_version);
236	if (ret)
237		return ret;
238
239	if (FIELD_GET(UPBOARD_FW_ID_MAJOR_MASK, fpga->firmware_version) !=
240	    UPBOARD_FW_ID_MAJOR_SUPPORTED)
241		return dev_err_probe(fpga->dev, -ENODEV,
242				     "unsupported FPGA FW v%lu.%lu.%lu build %#02lx",
243				     FIELD_GET(UPBOARD_FW_ID_MAJOR_MASK, fpga->firmware_version),
244				     FIELD_GET(UPBOARD_FW_ID_MINOR_MASK, fpga->firmware_version),
245				     FIELD_GET(UPBOARD_FW_ID_PATCH_MASK, fpga->firmware_version),
246				     FIELD_GET(UPBOARD_FW_ID_BUILD_MASK, fpga->firmware_version));
247	return 0;
248}
249
250static ssize_t upboard_fpga_version_show(struct device *dev, struct device_attribute *attr,
251					 char *buf)
252{
253	struct upboard_fpga *fpga = dev_get_drvdata(dev);
254
255	return sysfs_emit(buf, "FPGA FW v%lu.%lu.%lu build %#02lx\n",
256			  FIELD_GET(UPBOARD_FW_ID_MAJOR_MASK, fpga->firmware_version),
257			  FIELD_GET(UPBOARD_FW_ID_MINOR_MASK, fpga->firmware_version),
258			  FIELD_GET(UPBOARD_FW_ID_PATCH_MASK, fpga->firmware_version),
259			  FIELD_GET(UPBOARD_FW_ID_BUILD_MASK, fpga->firmware_version));
260}
261
262static DEVICE_ATTR_RO(upboard_fpga_version);
263
264static struct attribute *upboard_fpga_attrs[] = {
265	&dev_attr_upboard_fpga_version.attr,
266	NULL
267};
268
269ATTRIBUTE_GROUPS(upboard_fpga);
270
271static int upboard_fpga_probe(struct platform_device *pdev)
272{
273	struct device *dev = &pdev->dev;
274	struct upboard_fpga *fpga;
275	int ret;
276
277	fpga = devm_kzalloc(dev, sizeof(*fpga), GFP_KERNEL);
278	if (!fpga)
279		return -ENOMEM;
280
281	fpga->fpga_data = device_get_match_data(dev);
282
283	fpga->dev = dev;
284
285	platform_set_drvdata(pdev, fpga);
286
287	fpga->regmap = devm_regmap_init(dev, NULL, fpga, fpga->fpga_data->regmap_config);
288	if (IS_ERR(fpga->regmap))
289		return PTR_ERR(fpga->regmap);
290
291	ret = upboard_fpga_gpio_init(fpga);
292	if (ret)
293		return dev_err_probe(dev, ret, "Failed to initialize FPGA common GPIOs");
294
295	ret = upboard_fpga_get_firmware_version(fpga);
296	if (ret)
297		return ret;
298
299	return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, upboard_up_mfd_cells,
300				    ARRAY_SIZE(upboard_up_mfd_cells), NULL, 0, NULL);
301}
302
303static const struct acpi_device_id upboard_fpga_acpi_match[] = {
304	{ "AANT0F01", (kernel_ulong_t)&upboard_up2_fpga_data },
305	{ "AANT0F04", (kernel_ulong_t)&upboard_up_fpga_data },
306	{}
307};
308MODULE_DEVICE_TABLE(acpi, upboard_fpga_acpi_match);
309
310static struct platform_driver upboard_fpga_driver = {
311	.driver = {
312		.name = "upboard-fpga",
313		.acpi_match_table = upboard_fpga_acpi_match,
314		.dev_groups	= upboard_fpga_groups,
315	},
316	.probe = upboard_fpga_probe,
317};
318
319module_platform_driver(upboard_fpga_driver);
320
321MODULE_AUTHOR("Gary Wang <garywang@aaeon.com.tw>");
322MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
323MODULE_DESCRIPTION("UP Board FPGA driver");
324MODULE_LICENSE("GPL");
~/ Read-only file preview.