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; 262025b2caSRodrí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) { 612025b2caSRodríguez Barbarin, José Javier goto out_mcb_bus; 6273edc8f7SAndreas Werner } 6373edc8f7SAndreas Werner 642025b2caSRodríguez Barbarin, José Javier table_size = ret; 652025b2caSRodríguez Barbarin, José Javier 662025b2caSRodríguez Barbarin, José Javier if (table_size < CHAM_HEADER_SIZE) { 672025b2caSRodríguez Barbarin, José Javier /* Release the previous resources */ 682025b2caSRodríguez Barbarin, José Javier devm_iounmap(&pdev->dev, priv->base); 692025b2caSRodríguez Barbarin, José Javier devm_release_mem_region(&pdev->dev, priv->mem->start, resource_size(priv->mem)); 702025b2caSRodríguez Barbarin, José Javier 712025b2caSRodríguez Barbarin, José Javier /* Then, allocate it again with the actual chameleon table size */ 722025b2caSRodríguez Barbarin, José Javier res = devm_request_mem_region(&pdev->dev, priv->mem->start, 732025b2caSRodríguez Barbarin, José Javier table_size, 742025b2caSRodríguez Barbarin, José Javier KBUILD_MODNAME); 752025b2caSRodríguez Barbarin, José Javier if (!res) { 762025b2caSRodríguez Barbarin, José Javier dev_err(&pdev->dev, "Failed to request PCI memory\n"); 772025b2caSRodríguez Barbarin, José Javier ret = -EBUSY; 782025b2caSRodríguez Barbarin, José Javier goto out_mcb_bus; 792025b2caSRodríguez Barbarin, José Javier } 802025b2caSRodríguez Barbarin, José Javier 812025b2caSRodríguez Barbarin, José Javier priv->base = devm_ioremap(&pdev->dev, priv->mem->start, table_size); 822025b2caSRodríguez Barbarin, José Javier if (!priv->base) { 832025b2caSRodríguez Barbarin, José Javier dev_err(&pdev->dev, "Cannot ioremap\n"); 842025b2caSRodríguez Barbarin, José Javier ret = -ENOMEM; 852025b2caSRodríguez Barbarin, José Javier goto out_mcb_bus; 862025b2caSRodríguez Barbarin, José Javier } 872025b2caSRodríguez Barbarin, José Javier 882025b2caSRodríguez Barbarin, José Javier platform_set_drvdata(pdev, priv); 892025b2caSRodríguez Barbarin, José Javier } 9073edc8f7SAndreas Werner 9173edc8f7SAndreas Werner mcb_bus_add_devices(priv->bus); 9273edc8f7SAndreas Werner 9373edc8f7SAndreas Werner return 0; 9473edc8f7SAndreas Werner 952025b2caSRodríguez Barbarin, José Javier out_mcb_bus: 962025b2caSRodríguez Barbarin, José Javier mcb_release_bus(priv->bus); 972025b2caSRodríguez Barbarin, José Javier return ret; 9873edc8f7SAndreas Werner } 9973edc8f7SAndreas Werner 100*a2c72ed7SUwe Kleine-König static void 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 10773edc8f7SAndreas Werner static struct platform_device *mcb_lpc_pdev; 10873edc8f7SAndreas Werner 10973edc8f7SAndreas Werner static int mcb_lpc_create_platform_device(const struct dmi_system_id *id) 11073edc8f7SAndreas Werner { 11173edc8f7SAndreas Werner struct resource *res = id->driver_data; 11273edc8f7SAndreas Werner int ret; 11373edc8f7SAndreas Werner 11473edc8f7SAndreas Werner mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1); 11573edc8f7SAndreas Werner if (!mcb_lpc_pdev) 11673edc8f7SAndreas Werner return -ENOMEM; 11773edc8f7SAndreas Werner 11873edc8f7SAndreas Werner ret = platform_device_add_resources(mcb_lpc_pdev, res, 1); 11973edc8f7SAndreas Werner if (ret) 12073edc8f7SAndreas Werner goto out_put; 12173edc8f7SAndreas Werner 12273edc8f7SAndreas Werner ret = platform_device_add(mcb_lpc_pdev); 12373edc8f7SAndreas Werner if (ret) 12473edc8f7SAndreas Werner goto out_put; 12573edc8f7SAndreas Werner 12673edc8f7SAndreas Werner return 0; 12773edc8f7SAndreas Werner 12873edc8f7SAndreas Werner out_put: 12973edc8f7SAndreas Werner platform_device_put(mcb_lpc_pdev); 13073edc8f7SAndreas Werner return ret; 13173edc8f7SAndreas Werner } 13273edc8f7SAndreas Werner 1336f746d48SZhen Lei static struct resource sc24_fpga_resource = DEFINE_RES_MEM(0xe000e000, CHAM_HEADER_SIZE); 1346f746d48SZhen Lei static struct resource sc31_fpga_resource = DEFINE_RES_MEM(0xf000e000, CHAM_HEADER_SIZE); 135acf5e051SMichael Moese 13673edc8f7SAndreas Werner static struct platform_driver mcb_lpc_driver = { 13773edc8f7SAndreas Werner .driver = { 13873edc8f7SAndreas Werner .name = "mcb-lpc", 13973edc8f7SAndreas Werner }, 14073edc8f7SAndreas Werner .probe = mcb_lpc_probe, 141*a2c72ed7SUwe Kleine-König .remove_new = mcb_lpc_remove, 14273edc8f7SAndreas Werner }; 14373edc8f7SAndreas Werner 14473edc8f7SAndreas Werner static const struct dmi_system_id mcb_lpc_dmi_table[] = { 14573edc8f7SAndreas Werner { 14673edc8f7SAndreas Werner .ident = "SC24", 14773edc8f7SAndreas Werner .matches = { 14873edc8f7SAndreas Werner DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 14973edc8f7SAndreas Werner DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"), 15073edc8f7SAndreas Werner }, 15173edc8f7SAndreas Werner .driver_data = (void *)&sc24_fpga_resource, 15273edc8f7SAndreas Werner .callback = mcb_lpc_create_platform_device, 15373edc8f7SAndreas Werner }, 154acf5e051SMichael Moese { 155acf5e051SMichael Moese .ident = "SC31", 156acf5e051SMichael Moese .matches = { 157acf5e051SMichael Moese DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 158acf5e051SMichael Moese DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"), 159acf5e051SMichael Moese }, 160acf5e051SMichael Moese .driver_data = (void *)&sc31_fpga_resource, 161acf5e051SMichael Moese .callback = mcb_lpc_create_platform_device, 162acf5e051SMichael Moese }, 16373edc8f7SAndreas Werner {} 16473edc8f7SAndreas Werner }; 16573edc8f7SAndreas Werner MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table); 16673edc8f7SAndreas Werner 16773edc8f7SAndreas Werner static int __init mcb_lpc_init(void) 16873edc8f7SAndreas Werner { 16973edc8f7SAndreas Werner if (!dmi_check_system(mcb_lpc_dmi_table)) 17073edc8f7SAndreas Werner return -ENODEV; 17173edc8f7SAndreas Werner 17273edc8f7SAndreas Werner return platform_driver_register(&mcb_lpc_driver); 17373edc8f7SAndreas Werner } 17473edc8f7SAndreas Werner 17573edc8f7SAndreas Werner static void __exit mcb_lpc_exit(void) 17673edc8f7SAndreas Werner { 17773edc8f7SAndreas Werner platform_device_unregister(mcb_lpc_pdev); 17873edc8f7SAndreas Werner platform_driver_unregister(&mcb_lpc_driver); 17973edc8f7SAndreas Werner } 18073edc8f7SAndreas Werner 18173edc8f7SAndreas Werner module_init(mcb_lpc_init); 18273edc8f7SAndreas Werner module_exit(mcb_lpc_exit); 18373edc8f7SAndreas Werner 18473edc8f7SAndreas Werner MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); 18573edc8f7SAndreas Werner MODULE_LICENSE("GPL"); 18673edc8f7SAndreas Werner MODULE_DESCRIPTION("MCB over LPC support"); 187891e6036SJohannes Thumshirn MODULE_IMPORT_NS(MCB); 188