1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 273edc8f7SAndreas Werner /* 373edc8f7SAndreas Werner * MEN Chameleon Bus. 473edc8f7SAndreas Werner * 573edc8f7SAndreas Werner * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) 673edc8f7SAndreas Werner * Author: Andreas Werner <andreas.werner@men.de> 773edc8f7SAndreas Werner */ 873edc8f7SAndreas Werner 973edc8f7SAndreas Werner #include <linux/platform_device.h> 1073edc8f7SAndreas Werner #include <linux/module.h> 1173edc8f7SAndreas Werner #include <linux/dmi.h> 1273edc8f7SAndreas Werner #include <linux/mcb.h> 1373edc8f7SAndreas Werner #include <linux/io.h> 1473edc8f7SAndreas Werner #include "mcb-internal.h" 1573edc8f7SAndreas Werner 1673edc8f7SAndreas Werner struct priv { 1773edc8f7SAndreas Werner struct mcb_bus *bus; 1873edc8f7SAndreas Werner struct resource *mem; 1973edc8f7SAndreas Werner void __iomem *base; 2073edc8f7SAndreas Werner }; 2173edc8f7SAndreas Werner 2273edc8f7SAndreas Werner static int mcb_lpc_probe(struct platform_device *pdev) 2373edc8f7SAndreas Werner { 2473edc8f7SAndreas Werner struct resource *res; 2573edc8f7SAndreas Werner struct priv *priv; 26*2025b2caSRodríguez Barbarin, José Javier int ret = 0, table_size; 2773edc8f7SAndreas Werner 2873edc8f7SAndreas Werner priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 2973edc8f7SAndreas Werner if (!priv) 3073edc8f7SAndreas Werner return -ENOMEM; 3173edc8f7SAndreas Werner 3273edc8f7SAndreas Werner priv->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3373edc8f7SAndreas Werner if (!priv->mem) { 3473edc8f7SAndreas Werner dev_err(&pdev->dev, "No Memory resource\n"); 3573edc8f7SAndreas Werner return -ENODEV; 3673edc8f7SAndreas Werner } 3773edc8f7SAndreas Werner 3873edc8f7SAndreas Werner res = devm_request_mem_region(&pdev->dev, priv->mem->start, 3973edc8f7SAndreas Werner resource_size(priv->mem), 4073edc8f7SAndreas Werner KBUILD_MODNAME); 4173edc8f7SAndreas Werner if (!res) { 4273edc8f7SAndreas Werner dev_err(&pdev->dev, "Failed to request IO memory\n"); 4373edc8f7SAndreas Werner return -EBUSY; 4473edc8f7SAndreas Werner } 4573edc8f7SAndreas Werner 4673edc8f7SAndreas Werner priv->base = devm_ioremap(&pdev->dev, priv->mem->start, 4773edc8f7SAndreas Werner resource_size(priv->mem)); 4873edc8f7SAndreas Werner if (!priv->base) { 4973edc8f7SAndreas Werner dev_err(&pdev->dev, "Cannot ioremap\n"); 5073edc8f7SAndreas Werner return -ENOMEM; 5173edc8f7SAndreas Werner } 5273edc8f7SAndreas Werner 5373edc8f7SAndreas Werner platform_set_drvdata(pdev, priv); 5473edc8f7SAndreas Werner 5573edc8f7SAndreas Werner priv->bus = mcb_alloc_bus(&pdev->dev); 5673edc8f7SAndreas Werner if (IS_ERR(priv->bus)) 5773edc8f7SAndreas Werner return PTR_ERR(priv->bus); 5873edc8f7SAndreas Werner 5973edc8f7SAndreas Werner ret = chameleon_parse_cells(priv->bus, priv->mem->start, priv->base); 6073edc8f7SAndreas Werner if (ret < 0) { 61*2025b2caSRodríguez Barbarin, José Javier goto out_mcb_bus; 6273edc8f7SAndreas Werner } 6373edc8f7SAndreas Werner 64*2025b2caSRodríguez Barbarin, José Javier table_size = ret; 65*2025b2caSRodríguez Barbarin, José Javier 66*2025b2caSRodríguez Barbarin, José Javier if (table_size < CHAM_HEADER_SIZE) { 67*2025b2caSRodríguez Barbarin, José Javier /* Release the previous resources */ 68*2025b2caSRodríguez Barbarin, José Javier devm_iounmap(&pdev->dev, priv->base); 69*2025b2caSRodríguez Barbarin, José Javier devm_release_mem_region(&pdev->dev, priv->mem->start, resource_size(priv->mem)); 70*2025b2caSRodríguez Barbarin, José Javier 71*2025b2caSRodríguez Barbarin, José Javier /* Then, allocate it again with the actual chameleon table size */ 72*2025b2caSRodríguez Barbarin, José Javier res = devm_request_mem_region(&pdev->dev, priv->mem->start, 73*2025b2caSRodríguez Barbarin, José Javier table_size, 74*2025b2caSRodríguez Barbarin, José Javier KBUILD_MODNAME); 75*2025b2caSRodríguez Barbarin, José Javier if (!res) { 76*2025b2caSRodríguez Barbarin, José Javier dev_err(&pdev->dev, "Failed to request PCI memory\n"); 77*2025b2caSRodríguez Barbarin, José Javier ret = -EBUSY; 78*2025b2caSRodríguez Barbarin, José Javier goto out_mcb_bus; 79*2025b2caSRodríguez Barbarin, José Javier } 80*2025b2caSRodríguez Barbarin, José Javier 81*2025b2caSRodríguez Barbarin, José Javier priv->base = devm_ioremap(&pdev->dev, priv->mem->start, table_size); 82*2025b2caSRodríguez Barbarin, José Javier if (!priv->base) { 83*2025b2caSRodríguez Barbarin, José Javier dev_err(&pdev->dev, "Cannot ioremap\n"); 84*2025b2caSRodríguez Barbarin, José Javier ret = -ENOMEM; 85*2025b2caSRodríguez Barbarin, José Javier goto out_mcb_bus; 86*2025b2caSRodríguez Barbarin, José Javier } 87*2025b2caSRodríguez Barbarin, José Javier 88*2025b2caSRodríguez Barbarin, José Javier platform_set_drvdata(pdev, priv); 89*2025b2caSRodríguez Barbarin, José Javier } 9073edc8f7SAndreas Werner 9173edc8f7SAndreas Werner mcb_bus_add_devices(priv->bus); 9273edc8f7SAndreas Werner 9373edc8f7SAndreas Werner return 0; 9473edc8f7SAndreas Werner 95*2025b2caSRodríguez Barbarin, José Javier out_mcb_bus: 96*2025b2caSRodríguez Barbarin, José Javier mcb_release_bus(priv->bus); 97*2025b2caSRodríguez Barbarin, José Javier return ret; 9873edc8f7SAndreas Werner } 9973edc8f7SAndreas Werner 10073edc8f7SAndreas Werner static int mcb_lpc_remove(struct platform_device *pdev) 10173edc8f7SAndreas Werner { 10273edc8f7SAndreas Werner struct priv *priv = platform_get_drvdata(pdev); 10373edc8f7SAndreas Werner 10473edc8f7SAndreas Werner mcb_release_bus(priv->bus); 10573edc8f7SAndreas Werner 10673edc8f7SAndreas Werner return 0; 10773edc8f7SAndreas Werner } 10873edc8f7SAndreas Werner 10973edc8f7SAndreas Werner static struct platform_device *mcb_lpc_pdev; 11073edc8f7SAndreas Werner 11173edc8f7SAndreas Werner static int mcb_lpc_create_platform_device(const struct dmi_system_id *id) 11273edc8f7SAndreas Werner { 11373edc8f7SAndreas Werner struct resource *res = id->driver_data; 11473edc8f7SAndreas Werner int ret; 11573edc8f7SAndreas Werner 11673edc8f7SAndreas Werner mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1); 11773edc8f7SAndreas Werner if (!mcb_lpc_pdev) 11873edc8f7SAndreas Werner return -ENOMEM; 11973edc8f7SAndreas Werner 12073edc8f7SAndreas Werner ret = platform_device_add_resources(mcb_lpc_pdev, res, 1); 12173edc8f7SAndreas Werner if (ret) 12273edc8f7SAndreas Werner goto out_put; 12373edc8f7SAndreas Werner 12473edc8f7SAndreas Werner ret = platform_device_add(mcb_lpc_pdev); 12573edc8f7SAndreas Werner if (ret) 12673edc8f7SAndreas Werner goto out_put; 12773edc8f7SAndreas Werner 12873edc8f7SAndreas Werner return 0; 12973edc8f7SAndreas Werner 13073edc8f7SAndreas Werner out_put: 13173edc8f7SAndreas Werner platform_device_put(mcb_lpc_pdev); 13273edc8f7SAndreas Werner return ret; 13373edc8f7SAndreas Werner } 13473edc8f7SAndreas Werner 1356f746d48SZhen Lei static struct resource sc24_fpga_resource = DEFINE_RES_MEM(0xe000e000, CHAM_HEADER_SIZE); 1366f746d48SZhen Lei static struct resource sc31_fpga_resource = DEFINE_RES_MEM(0xf000e000, CHAM_HEADER_SIZE); 137acf5e051SMichael Moese 13873edc8f7SAndreas Werner static struct platform_driver mcb_lpc_driver = { 13973edc8f7SAndreas Werner .driver = { 14073edc8f7SAndreas Werner .name = "mcb-lpc", 14173edc8f7SAndreas Werner }, 14273edc8f7SAndreas Werner .probe = mcb_lpc_probe, 14373edc8f7SAndreas Werner .remove = mcb_lpc_remove, 14473edc8f7SAndreas Werner }; 14573edc8f7SAndreas Werner 14673edc8f7SAndreas Werner static const struct dmi_system_id mcb_lpc_dmi_table[] = { 14773edc8f7SAndreas Werner { 14873edc8f7SAndreas Werner .ident = "SC24", 14973edc8f7SAndreas Werner .matches = { 15073edc8f7SAndreas Werner DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 15173edc8f7SAndreas Werner DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"), 15273edc8f7SAndreas Werner }, 15373edc8f7SAndreas Werner .driver_data = (void *)&sc24_fpga_resource, 15473edc8f7SAndreas Werner .callback = mcb_lpc_create_platform_device, 15573edc8f7SAndreas Werner }, 156acf5e051SMichael Moese { 157acf5e051SMichael Moese .ident = "SC31", 158acf5e051SMichael Moese .matches = { 159acf5e051SMichael Moese DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 160acf5e051SMichael Moese DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"), 161acf5e051SMichael Moese }, 162acf5e051SMichael Moese .driver_data = (void *)&sc31_fpga_resource, 163acf5e051SMichael Moese .callback = mcb_lpc_create_platform_device, 164acf5e051SMichael Moese }, 16573edc8f7SAndreas Werner {} 16673edc8f7SAndreas Werner }; 16773edc8f7SAndreas Werner MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table); 16873edc8f7SAndreas Werner 16973edc8f7SAndreas Werner static int __init mcb_lpc_init(void) 17073edc8f7SAndreas Werner { 17173edc8f7SAndreas Werner if (!dmi_check_system(mcb_lpc_dmi_table)) 17273edc8f7SAndreas Werner return -ENODEV; 17373edc8f7SAndreas Werner 17473edc8f7SAndreas Werner return platform_driver_register(&mcb_lpc_driver); 17573edc8f7SAndreas Werner } 17673edc8f7SAndreas Werner 17773edc8f7SAndreas Werner static void __exit mcb_lpc_exit(void) 17873edc8f7SAndreas Werner { 17973edc8f7SAndreas Werner platform_device_unregister(mcb_lpc_pdev); 18073edc8f7SAndreas Werner platform_driver_unregister(&mcb_lpc_driver); 18173edc8f7SAndreas Werner } 18273edc8f7SAndreas Werner 18373edc8f7SAndreas Werner module_init(mcb_lpc_init); 18473edc8f7SAndreas Werner module_exit(mcb_lpc_exit); 18573edc8f7SAndreas Werner 18673edc8f7SAndreas Werner MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); 18773edc8f7SAndreas Werner MODULE_LICENSE("GPL"); 18873edc8f7SAndreas Werner MODULE_DESCRIPTION("MCB over LPC support"); 189891e6036SJohannes Thumshirn MODULE_IMPORT_NS(MCB); 190