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