1*73edc8f7SAndreas Werner /* 2*73edc8f7SAndreas Werner * MEN Chameleon Bus. 3*73edc8f7SAndreas Werner * 4*73edc8f7SAndreas Werner * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) 5*73edc8f7SAndreas Werner * Author: Andreas Werner <andreas.werner@men.de> 6*73edc8f7SAndreas Werner * 7*73edc8f7SAndreas Werner * This program is free software; you can redistribute it and/or modify it 8*73edc8f7SAndreas Werner * under the terms of the GNU General Public License as published by the Free 9*73edc8f7SAndreas Werner * Software Foundation; version 2 of the License. 10*73edc8f7SAndreas Werner */ 11*73edc8f7SAndreas Werner 12*73edc8f7SAndreas Werner #include <linux/platform_device.h> 13*73edc8f7SAndreas Werner #include <linux/module.h> 14*73edc8f7SAndreas Werner #include <linux/dmi.h> 15*73edc8f7SAndreas Werner #include <linux/mcb.h> 16*73edc8f7SAndreas Werner #include <linux/io.h> 17*73edc8f7SAndreas Werner #include "mcb-internal.h" 18*73edc8f7SAndreas Werner 19*73edc8f7SAndreas Werner struct priv { 20*73edc8f7SAndreas Werner struct mcb_bus *bus; 21*73edc8f7SAndreas Werner struct resource *mem; 22*73edc8f7SAndreas Werner void __iomem *base; 23*73edc8f7SAndreas Werner }; 24*73edc8f7SAndreas Werner 25*73edc8f7SAndreas Werner static int mcb_lpc_probe(struct platform_device *pdev) 26*73edc8f7SAndreas Werner { 27*73edc8f7SAndreas Werner struct resource *res; 28*73edc8f7SAndreas Werner struct priv *priv; 29*73edc8f7SAndreas Werner int ret = 0; 30*73edc8f7SAndreas Werner 31*73edc8f7SAndreas Werner priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 32*73edc8f7SAndreas Werner if (!priv) 33*73edc8f7SAndreas Werner return -ENOMEM; 34*73edc8f7SAndreas Werner 35*73edc8f7SAndreas Werner priv->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 36*73edc8f7SAndreas Werner if (!priv->mem) { 37*73edc8f7SAndreas Werner dev_err(&pdev->dev, "No Memory resource\n"); 38*73edc8f7SAndreas Werner return -ENODEV; 39*73edc8f7SAndreas Werner } 40*73edc8f7SAndreas Werner 41*73edc8f7SAndreas Werner res = devm_request_mem_region(&pdev->dev, priv->mem->start, 42*73edc8f7SAndreas Werner resource_size(priv->mem), 43*73edc8f7SAndreas Werner KBUILD_MODNAME); 44*73edc8f7SAndreas Werner if (!res) { 45*73edc8f7SAndreas Werner dev_err(&pdev->dev, "Failed to request IO memory\n"); 46*73edc8f7SAndreas Werner return -EBUSY; 47*73edc8f7SAndreas Werner } 48*73edc8f7SAndreas Werner 49*73edc8f7SAndreas Werner priv->base = devm_ioremap(&pdev->dev, priv->mem->start, 50*73edc8f7SAndreas Werner resource_size(priv->mem)); 51*73edc8f7SAndreas Werner if (!priv->base) { 52*73edc8f7SAndreas Werner dev_err(&pdev->dev, "Cannot ioremap\n"); 53*73edc8f7SAndreas Werner return -ENOMEM; 54*73edc8f7SAndreas Werner } 55*73edc8f7SAndreas Werner 56*73edc8f7SAndreas Werner platform_set_drvdata(pdev, priv); 57*73edc8f7SAndreas Werner 58*73edc8f7SAndreas Werner priv->bus = mcb_alloc_bus(&pdev->dev); 59*73edc8f7SAndreas Werner if (IS_ERR(priv->bus)) 60*73edc8f7SAndreas Werner return PTR_ERR(priv->bus); 61*73edc8f7SAndreas Werner 62*73edc8f7SAndreas Werner ret = chameleon_parse_cells(priv->bus, priv->mem->start, priv->base); 63*73edc8f7SAndreas Werner if (ret < 0) { 64*73edc8f7SAndreas Werner mcb_release_bus(priv->bus); 65*73edc8f7SAndreas Werner return ret; 66*73edc8f7SAndreas Werner } 67*73edc8f7SAndreas Werner 68*73edc8f7SAndreas Werner dev_dbg(&pdev->dev, "Found %d cells\n", ret); 69*73edc8f7SAndreas Werner 70*73edc8f7SAndreas Werner mcb_bus_add_devices(priv->bus); 71*73edc8f7SAndreas Werner 72*73edc8f7SAndreas Werner return 0; 73*73edc8f7SAndreas Werner 74*73edc8f7SAndreas Werner } 75*73edc8f7SAndreas Werner 76*73edc8f7SAndreas Werner static int mcb_lpc_remove(struct platform_device *pdev) 77*73edc8f7SAndreas Werner { 78*73edc8f7SAndreas Werner struct priv *priv = platform_get_drvdata(pdev); 79*73edc8f7SAndreas Werner 80*73edc8f7SAndreas Werner mcb_release_bus(priv->bus); 81*73edc8f7SAndreas Werner 82*73edc8f7SAndreas Werner return 0; 83*73edc8f7SAndreas Werner } 84*73edc8f7SAndreas Werner 85*73edc8f7SAndreas Werner static struct platform_device *mcb_lpc_pdev; 86*73edc8f7SAndreas Werner 87*73edc8f7SAndreas Werner static int mcb_lpc_create_platform_device(const struct dmi_system_id *id) 88*73edc8f7SAndreas Werner { 89*73edc8f7SAndreas Werner struct resource *res = id->driver_data; 90*73edc8f7SAndreas Werner int ret; 91*73edc8f7SAndreas Werner 92*73edc8f7SAndreas Werner mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1); 93*73edc8f7SAndreas Werner if (!mcb_lpc_pdev) 94*73edc8f7SAndreas Werner return -ENOMEM; 95*73edc8f7SAndreas Werner 96*73edc8f7SAndreas Werner ret = platform_device_add_resources(mcb_lpc_pdev, res, 1); 97*73edc8f7SAndreas Werner if (ret) 98*73edc8f7SAndreas Werner goto out_put; 99*73edc8f7SAndreas Werner 100*73edc8f7SAndreas Werner ret = platform_device_add(mcb_lpc_pdev); 101*73edc8f7SAndreas Werner if (ret) 102*73edc8f7SAndreas Werner goto out_put; 103*73edc8f7SAndreas Werner 104*73edc8f7SAndreas Werner return 0; 105*73edc8f7SAndreas Werner 106*73edc8f7SAndreas Werner out_put: 107*73edc8f7SAndreas Werner platform_device_put(mcb_lpc_pdev); 108*73edc8f7SAndreas Werner return ret; 109*73edc8f7SAndreas Werner } 110*73edc8f7SAndreas Werner 111*73edc8f7SAndreas Werner static struct resource sc24_fpga_resource = { 112*73edc8f7SAndreas Werner .start = 0xe000e000, 113*73edc8f7SAndreas Werner .end = 0xe000e000 + CHAM_HEADER_SIZE, 114*73edc8f7SAndreas Werner .flags = IORESOURCE_MEM, 115*73edc8f7SAndreas Werner }; 116*73edc8f7SAndreas Werner 117*73edc8f7SAndreas Werner static struct platform_driver mcb_lpc_driver = { 118*73edc8f7SAndreas Werner .driver = { 119*73edc8f7SAndreas Werner .name = "mcb-lpc", 120*73edc8f7SAndreas Werner }, 121*73edc8f7SAndreas Werner .probe = mcb_lpc_probe, 122*73edc8f7SAndreas Werner .remove = mcb_lpc_remove, 123*73edc8f7SAndreas Werner }; 124*73edc8f7SAndreas Werner 125*73edc8f7SAndreas Werner static const struct dmi_system_id mcb_lpc_dmi_table[] = { 126*73edc8f7SAndreas Werner { 127*73edc8f7SAndreas Werner .ident = "SC24", 128*73edc8f7SAndreas Werner .matches = { 129*73edc8f7SAndreas Werner DMI_MATCH(DMI_SYS_VENDOR, "MEN"), 130*73edc8f7SAndreas Werner DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"), 131*73edc8f7SAndreas Werner }, 132*73edc8f7SAndreas Werner .driver_data = (void *)&sc24_fpga_resource, 133*73edc8f7SAndreas Werner .callback = mcb_lpc_create_platform_device, 134*73edc8f7SAndreas Werner }, 135*73edc8f7SAndreas Werner {} 136*73edc8f7SAndreas Werner }; 137*73edc8f7SAndreas Werner MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table); 138*73edc8f7SAndreas Werner 139*73edc8f7SAndreas Werner static int __init mcb_lpc_init(void) 140*73edc8f7SAndreas Werner { 141*73edc8f7SAndreas Werner if (!dmi_check_system(mcb_lpc_dmi_table)) 142*73edc8f7SAndreas Werner return -ENODEV; 143*73edc8f7SAndreas Werner 144*73edc8f7SAndreas Werner return platform_driver_register(&mcb_lpc_driver); 145*73edc8f7SAndreas Werner } 146*73edc8f7SAndreas Werner 147*73edc8f7SAndreas Werner static void __exit mcb_lpc_exit(void) 148*73edc8f7SAndreas Werner { 149*73edc8f7SAndreas Werner platform_device_unregister(mcb_lpc_pdev); 150*73edc8f7SAndreas Werner platform_driver_unregister(&mcb_lpc_driver); 151*73edc8f7SAndreas Werner } 152*73edc8f7SAndreas Werner 153*73edc8f7SAndreas Werner module_init(mcb_lpc_init); 154*73edc8f7SAndreas Werner module_exit(mcb_lpc_exit); 155*73edc8f7SAndreas Werner 156*73edc8f7SAndreas Werner MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); 157*73edc8f7SAndreas Werner MODULE_LICENSE("GPL"); 158*73edc8f7SAndreas Werner MODULE_DESCRIPTION("MCB over LPC support"); 159