188 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 16struct priv {17 struct mcb_bus *bus;18 struct resource *mem;19 void __iomem *base;20};21 22static int mcb_lpc_probe(struct platform_device *pdev)23{24 struct resource *res;25 struct priv *priv;26 int ret = 0, table_size;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 goto out_mcb_bus;62 }63 64 table_size = ret;65 66 if (table_size < CHAM_HEADER_SIZE) {67 /* Release the previous resources */68 devm_iounmap(&pdev->dev, priv->base);69 devm_release_mem_region(&pdev->dev, priv->mem->start, resource_size(priv->mem));70 71 /* Then, allocate it again with the actual chameleon table size */72 res = devm_request_mem_region(&pdev->dev, priv->mem->start,73 table_size,74 KBUILD_MODNAME);75 if (!res) {76 dev_err(&pdev->dev, "Failed to request PCI memory\n");77 ret = -EBUSY;78 goto out_mcb_bus;79 }80 81 priv->base = devm_ioremap(&pdev->dev, priv->mem->start, table_size);82 if (!priv->base) {83 dev_err(&pdev->dev, "Cannot ioremap\n");84 ret = -ENOMEM;85 goto out_mcb_bus;86 }87 88 platform_set_drvdata(pdev, priv);89 }90 91 mcb_bus_add_devices(priv->bus);92 93 return 0;94 95out_mcb_bus:96 mcb_release_bus(priv->bus);97 return ret;98}99 100static void mcb_lpc_remove(struct platform_device *pdev)101{102 struct priv *priv = platform_get_drvdata(pdev);103 104 mcb_release_bus(priv->bus);105}106 107static struct platform_device *mcb_lpc_pdev;108 109static int mcb_lpc_create_platform_device(const struct dmi_system_id *id)110{111 struct resource *res = id->driver_data;112 int ret;113 114 mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1);115 if (!mcb_lpc_pdev)116 return -ENOMEM;117 118 ret = platform_device_add_resources(mcb_lpc_pdev, res, 1);119 if (ret)120 goto out_put;121 122 ret = platform_device_add(mcb_lpc_pdev);123 if (ret)124 goto out_put;125 126 return 0;127 128out_put:129 platform_device_put(mcb_lpc_pdev);130 return ret;131}132 133static struct resource sc24_fpga_resource = DEFINE_RES_MEM(0xe000e000, CHAM_HEADER_SIZE);134static struct resource sc31_fpga_resource = DEFINE_RES_MEM(0xf000e000, CHAM_HEADER_SIZE);135 136static struct platform_driver mcb_lpc_driver = {137 .driver = {138 .name = "mcb-lpc",139 },140 .probe = mcb_lpc_probe,141 .remove_new = mcb_lpc_remove,142};143 144static const struct dmi_system_id mcb_lpc_dmi_table[] = {145 {146 .ident = "SC24",147 .matches = {148 DMI_MATCH(DMI_SYS_VENDOR, "MEN"),149 DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"),150 },151 .driver_data = (void *)&sc24_fpga_resource,152 .callback = mcb_lpc_create_platform_device,153 },154 {155 .ident = "SC31",156 .matches = {157 DMI_MATCH(DMI_SYS_VENDOR, "MEN"),158 DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"),159 },160 .driver_data = (void *)&sc31_fpga_resource,161 .callback = mcb_lpc_create_platform_device,162 },163 {}164};165MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table);166 167static int __init mcb_lpc_init(void)168{169 if (!dmi_check_system(mcb_lpc_dmi_table))170 return -ENODEV;171 172 return platform_driver_register(&mcb_lpc_driver);173}174 175static void __exit mcb_lpc_exit(void)176{177 platform_device_unregister(mcb_lpc_pdev);178 platform_driver_unregister(&mcb_lpc_driver);179}180 181module_init(mcb_lpc_init);182module_exit(mcb_lpc_exit);183 184MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");185MODULE_LICENSE("GPL");186MODULE_DESCRIPTION("MCB over LPC support");187MODULE_IMPORT_NS(MCB);188