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 = {
 109        .start = 0xe000e000,
 110        .end = 0xe000e000 + CHAM_HEADER_SIZE,
 111        .flags = IORESOURCE_MEM,
 112};
 113
 114static struct resource sc31_fpga_resource = {
 115        .start = 0xf000e000,
 116        .end = 0xf000e000 + CHAM_HEADER_SIZE,
 117        .flags = IORESOURCE_MEM,
 118};
 119
 120static struct platform_driver mcb_lpc_driver = {
 121        .driver         = {
 122                .name = "mcb-lpc",
 123        },
 124        .probe          = mcb_lpc_probe,
 125        .remove         = mcb_lpc_remove,
 126};
 127
 128static const struct dmi_system_id mcb_lpc_dmi_table[] = {
 129        {
 130                .ident = "SC24",
 131                .matches = {
 132                        DMI_MATCH(DMI_SYS_VENDOR, "MEN"),
 133                        DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"),
 134                },
 135                .driver_data = (void *)&sc24_fpga_resource,
 136                .callback = mcb_lpc_create_platform_device,
 137        },
 138        {
 139                .ident = "SC31",
 140                .matches = {
 141                        DMI_MATCH(DMI_SYS_VENDOR, "MEN"),
 142                        DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"),
 143                },
 144                .driver_data = (void *)&sc31_fpga_resource,
 145                .callback = mcb_lpc_create_platform_device,
 146        },
 147        {}
 148};
 149MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table);
 150
 151static int __init mcb_lpc_init(void)
 152{
 153        if (!dmi_check_system(mcb_lpc_dmi_table))
 154                return -ENODEV;
 155
 156        return platform_driver_register(&mcb_lpc_driver);
 157}
 158
 159static void __exit mcb_lpc_exit(void)
 160{
 161        platform_device_unregister(mcb_lpc_pdev);
 162        platform_driver_unregister(&mcb_lpc_driver);
 163}
 164
 165module_init(mcb_lpc_init);
 166module_exit(mcb_lpc_exit);
 167
 168MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
 169MODULE_LICENSE("GPL");
 170MODULE_DESCRIPTION("MCB over LPC support");
 171MODULE_IMPORT_NS(MCB);
 172