1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * MEN Chameleon Bus. 4 * 5 * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) 6 * Author: Andreas Werner <andreas.werner@men.de> 7 */ 8 9 #include <linux/platform_device.h> 10 #include <linux/module.h> 11 #include <linux/dmi.h> 12 #include <linux/mcb.h> 13 #include <linux/io.h> 14 #include "mcb-internal.h" 15 16 struct priv { 17 struct mcb_bus *bus; 18 struct resource *mem; 19 void __iomem *base; 20 }; 21 22 static int mcb_lpc_probe(struct platform_device *pdev) 23 { 24 struct resource *res; 25 struct priv *priv; 26 int ret = 0; 27 28 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 29 if (!priv) 30 return -ENOMEM; 31 32 priv->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 33 if (!priv->mem) { 34 dev_err(&pdev->dev, "No Memory resource\n"); 35 return -ENODEV; 36 } 37 38 res = devm_request_mem_region(&pdev->dev, priv->mem->start, 39 resource_size(priv->mem), 40 KBUILD_MODNAME); 41 if (!res) { 42 dev_err(&pdev->dev, "Failed to request IO memory\n"); 43 return -EBUSY; 44 } 45 46 priv->base = devm_ioremap(&pdev->dev, priv->mem->start, 47 resource_size(priv->mem)); 48 if (!priv->base) { 49 dev_err(&pdev->dev, "Cannot ioremap\n"); 50 return -ENOMEM; 51 } 52 53 platform_set_drvdata(pdev, priv); 54 55 priv->bus = mcb_alloc_bus(&pdev->dev); 56 if (IS_ERR(priv->bus)) 57 return PTR_ERR(priv->bus); 58 59 ret = chameleon_parse_cells(priv->bus, priv->mem->start, priv->base); 60 if (ret < 0) { 61 mcb_release_bus(priv->bus); 62 return ret; 63 } 64 65 dev_dbg(&pdev->dev, "Found %d cells\n", ret); 66 67 mcb_bus_add_devices(priv->bus); 68 69 return 0; 70 71 } 72 73 static int mcb_lpc_remove(struct platform_device *pdev) 74 { 75 struct priv *priv = platform_get_drvdata(pdev); 76 77 mcb_release_bus(priv->bus); 78 79 return 0; 80 } 81 82 static struct platform_device *mcb_lpc_pdev; 83 84 static int mcb_lpc_create_platform_device(const struct dmi_system_id *id) 85 { 86 struct resource *res = id->driver_data; 87 int ret; 88 89 mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1); 90 if (!mcb_lpc_pdev) 91 return -ENOMEM; 92 93 ret = platform_device_add_resources(mcb_lpc_pdev, res, 1); 94 if (ret) 95 goto out_put; 96 97 ret = platform_device_add(mcb_lpc_pdev); 98 if (ret) 99 goto out_put; 100 101 return 0; 102 103 out_put: 104 platform_device_put(mcb_lpc_pdev); 105 return ret; 106 } 107 108 static struct resource sc24_fpga_resource = DEFINE_RES_MEM(0xe000e000, CHAM_HEADER_SIZE); 109 static struct resource sc31_fpga_resource = DEFINE_RES_MEM(0xf000e000, CHAM_HEADER_SIZE); 110 111 static struct platform_driver mcb_lpc_driver = { 112 .driver = { 113 .name = "mcb-lpc", 114 }, 115 .probe = mcb_lpc_probe, 116 .remove = mcb_lpc_remove, 117 }; 118 119 static const struct dmi_system_id mcb_lpc_dmi_table[] = { 120 { 121 .ident = "SC24", 122 .matches = { 123 DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 124 DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"), 125 }, 126 .driver_data = (void *)&sc24_fpga_resource, 127 .callback = mcb_lpc_create_platform_device, 128 }, 129 { 130 .ident = "SC31", 131 .matches = { 132 DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 133 DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"), 134 }, 135 .driver_data = (void *)&sc31_fpga_resource, 136 .callback = mcb_lpc_create_platform_device, 137 }, 138 {} 139 }; 140 MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table); 141 142 static int __init mcb_lpc_init(void) 143 { 144 if (!dmi_check_system(mcb_lpc_dmi_table)) 145 return -ENODEV; 146 147 return platform_driver_register(&mcb_lpc_driver); 148 } 149 150 static void __exit mcb_lpc_exit(void) 151 { 152 platform_device_unregister(mcb_lpc_pdev); 153 platform_driver_unregister(&mcb_lpc_driver); 154 } 155 156 module_init(mcb_lpc_init); 157 module_exit(mcb_lpc_exit); 158 159 MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); 160 MODULE_LICENSE("GPL"); 161 MODULE_DESCRIPTION("MCB over LPC support"); 162 MODULE_IMPORT_NS(MCB); 163