1*b886d83cSThomas 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; 2673edc8f7SAndreas Werner int ret = 0; 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) { 6173edc8f7SAndreas Werner mcb_release_bus(priv->bus); 6273edc8f7SAndreas Werner return ret; 6373edc8f7SAndreas Werner } 6473edc8f7SAndreas Werner 6573edc8f7SAndreas Werner dev_dbg(&pdev->dev, "Found %d cells\n", ret); 6673edc8f7SAndreas Werner 6773edc8f7SAndreas Werner mcb_bus_add_devices(priv->bus); 6873edc8f7SAndreas Werner 6973edc8f7SAndreas Werner return 0; 7073edc8f7SAndreas Werner 7173edc8f7SAndreas Werner } 7273edc8f7SAndreas Werner 7373edc8f7SAndreas Werner static int mcb_lpc_remove(struct platform_device *pdev) 7473edc8f7SAndreas Werner { 7573edc8f7SAndreas Werner struct priv *priv = platform_get_drvdata(pdev); 7673edc8f7SAndreas Werner 7773edc8f7SAndreas Werner mcb_release_bus(priv->bus); 7873edc8f7SAndreas Werner 7973edc8f7SAndreas Werner return 0; 8073edc8f7SAndreas Werner } 8173edc8f7SAndreas Werner 8273edc8f7SAndreas Werner static struct platform_device *mcb_lpc_pdev; 8373edc8f7SAndreas Werner 8473edc8f7SAndreas Werner static int mcb_lpc_create_platform_device(const struct dmi_system_id *id) 8573edc8f7SAndreas Werner { 8673edc8f7SAndreas Werner struct resource *res = id->driver_data; 8773edc8f7SAndreas Werner int ret; 8873edc8f7SAndreas Werner 8973edc8f7SAndreas Werner mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1); 9073edc8f7SAndreas Werner if (!mcb_lpc_pdev) 9173edc8f7SAndreas Werner return -ENOMEM; 9273edc8f7SAndreas Werner 9373edc8f7SAndreas Werner ret = platform_device_add_resources(mcb_lpc_pdev, res, 1); 9473edc8f7SAndreas Werner if (ret) 9573edc8f7SAndreas Werner goto out_put; 9673edc8f7SAndreas Werner 9773edc8f7SAndreas Werner ret = platform_device_add(mcb_lpc_pdev); 9873edc8f7SAndreas Werner if (ret) 9973edc8f7SAndreas Werner goto out_put; 10073edc8f7SAndreas Werner 10173edc8f7SAndreas Werner return 0; 10273edc8f7SAndreas Werner 10373edc8f7SAndreas Werner out_put: 10473edc8f7SAndreas Werner platform_device_put(mcb_lpc_pdev); 10573edc8f7SAndreas Werner return ret; 10673edc8f7SAndreas Werner } 10773edc8f7SAndreas Werner 10873edc8f7SAndreas Werner static struct resource sc24_fpga_resource = { 10973edc8f7SAndreas Werner .start = 0xe000e000, 11073edc8f7SAndreas Werner .end = 0xe000e000 + CHAM_HEADER_SIZE, 11173edc8f7SAndreas Werner .flags = IORESOURCE_MEM, 11273edc8f7SAndreas Werner }; 11373edc8f7SAndreas Werner 114acf5e051SMichael Moese static struct resource sc31_fpga_resource = { 115acf5e051SMichael Moese .start = 0xf000e000, 116acf5e051SMichael Moese .end = 0xf000e000 + CHAM_HEADER_SIZE, 117acf5e051SMichael Moese .flags = IORESOURCE_MEM, 118acf5e051SMichael Moese }; 119acf5e051SMichael Moese 12073edc8f7SAndreas Werner static struct platform_driver mcb_lpc_driver = { 12173edc8f7SAndreas Werner .driver = { 12273edc8f7SAndreas Werner .name = "mcb-lpc", 12373edc8f7SAndreas Werner }, 12473edc8f7SAndreas Werner .probe = mcb_lpc_probe, 12573edc8f7SAndreas Werner .remove = mcb_lpc_remove, 12673edc8f7SAndreas Werner }; 12773edc8f7SAndreas Werner 12873edc8f7SAndreas Werner static const struct dmi_system_id mcb_lpc_dmi_table[] = { 12973edc8f7SAndreas Werner { 13073edc8f7SAndreas Werner .ident = "SC24", 13173edc8f7SAndreas Werner .matches = { 13273edc8f7SAndreas Werner DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 13373edc8f7SAndreas Werner DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"), 13473edc8f7SAndreas Werner }, 13573edc8f7SAndreas Werner .driver_data = (void *)&sc24_fpga_resource, 13673edc8f7SAndreas Werner .callback = mcb_lpc_create_platform_device, 13773edc8f7SAndreas Werner }, 138acf5e051SMichael Moese { 139acf5e051SMichael Moese .ident = "SC31", 140acf5e051SMichael Moese .matches = { 141acf5e051SMichael Moese DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 142acf5e051SMichael Moese DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"), 143acf5e051SMichael Moese }, 144acf5e051SMichael Moese .driver_data = (void *)&sc31_fpga_resource, 145acf5e051SMichael Moese .callback = mcb_lpc_create_platform_device, 146acf5e051SMichael Moese }, 14773edc8f7SAndreas Werner {} 14873edc8f7SAndreas Werner }; 14973edc8f7SAndreas Werner MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table); 15073edc8f7SAndreas Werner 15173edc8f7SAndreas Werner static int __init mcb_lpc_init(void) 15273edc8f7SAndreas Werner { 15373edc8f7SAndreas Werner if (!dmi_check_system(mcb_lpc_dmi_table)) 15473edc8f7SAndreas Werner return -ENODEV; 15573edc8f7SAndreas Werner 15673edc8f7SAndreas Werner return platform_driver_register(&mcb_lpc_driver); 15773edc8f7SAndreas Werner } 15873edc8f7SAndreas Werner 15973edc8f7SAndreas Werner static void __exit mcb_lpc_exit(void) 16073edc8f7SAndreas Werner { 16173edc8f7SAndreas Werner platform_device_unregister(mcb_lpc_pdev); 16273edc8f7SAndreas Werner platform_driver_unregister(&mcb_lpc_driver); 16373edc8f7SAndreas Werner } 16473edc8f7SAndreas Werner 16573edc8f7SAndreas Werner module_init(mcb_lpc_init); 16673edc8f7SAndreas Werner module_exit(mcb_lpc_exit); 16773edc8f7SAndreas Werner 16873edc8f7SAndreas Werner MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); 16973edc8f7SAndreas Werner MODULE_LICENSE("GPL"); 17073edc8f7SAndreas Werner MODULE_DESCRIPTION("MCB over LPC support"); 171