18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MEN Chameleon Bus. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) 68c2ecf20Sopenharmony_ci * Author: Andreas Werner <andreas.werner@men.de> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/dmi.h> 128c2ecf20Sopenharmony_ci#include <linux/mcb.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include "mcb-internal.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistruct priv { 178c2ecf20Sopenharmony_ci struct mcb_bus *bus; 188c2ecf20Sopenharmony_ci struct resource *mem; 198c2ecf20Sopenharmony_ci void __iomem *base; 208c2ecf20Sopenharmony_ci}; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int mcb_lpc_probe(struct platform_device *pdev) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct resource *res; 258c2ecf20Sopenharmony_ci struct priv *priv; 268c2ecf20Sopenharmony_ci int ret = 0, table_size; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 298c2ecf20Sopenharmony_ci if (!priv) 308c2ecf20Sopenharmony_ci return -ENOMEM; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci priv->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 338c2ecf20Sopenharmony_ci if (!priv->mem) { 348c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No Memory resource\n"); 358c2ecf20Sopenharmony_ci return -ENODEV; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci res = devm_request_mem_region(&pdev->dev, priv->mem->start, 398c2ecf20Sopenharmony_ci resource_size(priv->mem), 408c2ecf20Sopenharmony_ci KBUILD_MODNAME); 418c2ecf20Sopenharmony_ci if (!res) { 428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request IO memory\n"); 438c2ecf20Sopenharmony_ci return -EBUSY; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci priv->base = devm_ioremap(&pdev->dev, priv->mem->start, 478c2ecf20Sopenharmony_ci resource_size(priv->mem)); 488c2ecf20Sopenharmony_ci if (!priv->base) { 498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot ioremap\n"); 508c2ecf20Sopenharmony_ci return -ENOMEM; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci priv->bus = mcb_alloc_bus(&pdev->dev); 568c2ecf20Sopenharmony_ci if (IS_ERR(priv->bus)) 578c2ecf20Sopenharmony_ci return PTR_ERR(priv->bus); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci ret = chameleon_parse_cells(priv->bus, priv->mem->start, priv->base); 608c2ecf20Sopenharmony_ci if (ret < 0) { 618c2ecf20Sopenharmony_ci goto out_mcb_bus; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci table_size = ret; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (table_size < CHAM_HEADER_SIZE) { 678c2ecf20Sopenharmony_ci /* Release the previous resources */ 688c2ecf20Sopenharmony_ci devm_iounmap(&pdev->dev, priv->base); 698c2ecf20Sopenharmony_ci devm_release_mem_region(&pdev->dev, priv->mem->start, resource_size(priv->mem)); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* Then, allocate it again with the actual chameleon table size */ 728c2ecf20Sopenharmony_ci res = devm_request_mem_region(&pdev->dev, priv->mem->start, 738c2ecf20Sopenharmony_ci table_size, 748c2ecf20Sopenharmony_ci KBUILD_MODNAME); 758c2ecf20Sopenharmony_ci if (!res) { 768c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request PCI memory\n"); 778c2ecf20Sopenharmony_ci ret = -EBUSY; 788c2ecf20Sopenharmony_ci goto out_mcb_bus; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci priv->base = devm_ioremap(&pdev->dev, priv->mem->start, table_size); 828c2ecf20Sopenharmony_ci if (!priv->base) { 838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot ioremap\n"); 848c2ecf20Sopenharmony_ci ret = -ENOMEM; 858c2ecf20Sopenharmony_ci goto out_mcb_bus; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci mcb_bus_add_devices(priv->bus); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ciout_mcb_bus: 968c2ecf20Sopenharmony_ci mcb_release_bus(priv->bus); 978c2ecf20Sopenharmony_ci return ret; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int mcb_lpc_remove(struct platform_device *pdev) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct priv *priv = platform_get_drvdata(pdev); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci mcb_release_bus(priv->bus); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct platform_device *mcb_lpc_pdev; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int mcb_lpc_create_platform_device(const struct dmi_system_id *id) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct resource *res = id->driver_data; 1148c2ecf20Sopenharmony_ci int ret; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1); 1178c2ecf20Sopenharmony_ci if (!mcb_lpc_pdev) 1188c2ecf20Sopenharmony_ci return -ENOMEM; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ret = platform_device_add_resources(mcb_lpc_pdev, res, 1); 1218c2ecf20Sopenharmony_ci if (ret) 1228c2ecf20Sopenharmony_ci goto out_put; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci ret = platform_device_add(mcb_lpc_pdev); 1258c2ecf20Sopenharmony_ci if (ret) 1268c2ecf20Sopenharmony_ci goto out_put; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ciout_put: 1318c2ecf20Sopenharmony_ci platform_device_put(mcb_lpc_pdev); 1328c2ecf20Sopenharmony_ci return ret; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic struct resource sc24_fpga_resource = { 1368c2ecf20Sopenharmony_ci .start = 0xe000e000, 1378c2ecf20Sopenharmony_ci .end = 0xe000e000 + CHAM_HEADER_SIZE, 1388c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic struct resource sc31_fpga_resource = { 1428c2ecf20Sopenharmony_ci .start = 0xf000e000, 1438c2ecf20Sopenharmony_ci .end = 0xf000e000 + CHAM_HEADER_SIZE, 1448c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic struct platform_driver mcb_lpc_driver = { 1488c2ecf20Sopenharmony_ci .driver = { 1498c2ecf20Sopenharmony_ci .name = "mcb-lpc", 1508c2ecf20Sopenharmony_ci }, 1518c2ecf20Sopenharmony_ci .probe = mcb_lpc_probe, 1528c2ecf20Sopenharmony_ci .remove = mcb_lpc_remove, 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic const struct dmi_system_id mcb_lpc_dmi_table[] = { 1568c2ecf20Sopenharmony_ci { 1578c2ecf20Sopenharmony_ci .ident = "SC24", 1588c2ecf20Sopenharmony_ci .matches = { 1598c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 1608c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"), 1618c2ecf20Sopenharmony_ci }, 1628c2ecf20Sopenharmony_ci .driver_data = (void *)&sc24_fpga_resource, 1638c2ecf20Sopenharmony_ci .callback = mcb_lpc_create_platform_device, 1648c2ecf20Sopenharmony_ci }, 1658c2ecf20Sopenharmony_ci { 1668c2ecf20Sopenharmony_ci .ident = "SC31", 1678c2ecf20Sopenharmony_ci .matches = { 1688c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 1698c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"), 1708c2ecf20Sopenharmony_ci }, 1718c2ecf20Sopenharmony_ci .driver_data = (void *)&sc31_fpga_resource, 1728c2ecf20Sopenharmony_ci .callback = mcb_lpc_create_platform_device, 1738c2ecf20Sopenharmony_ci }, 1748c2ecf20Sopenharmony_ci {} 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int __init mcb_lpc_init(void) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci if (!dmi_check_system(mcb_lpc_dmi_table)) 1818c2ecf20Sopenharmony_ci return -ENODEV; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return platform_driver_register(&mcb_lpc_driver); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void __exit mcb_lpc_exit(void) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci platform_device_unregister(mcb_lpc_pdev); 1898c2ecf20Sopenharmony_ci platform_driver_unregister(&mcb_lpc_driver); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cimodule_init(mcb_lpc_init); 1938c2ecf20Sopenharmony_cimodule_exit(mcb_lpc_exit); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); 1968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MCB over LPC support"); 1988c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(MCB); 199