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