cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hms-profinet.c (5649B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * HMS Profinet Client Driver
      4 *
      5 * Copyright (C) 2018 Arcx Inc
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/module.h>
     10#include <linux/init.h>
     11#include <linux/slab.h>
     12
     13/* move to <linux/fieldbus_dev.h> when taking this out of staging */
     14#include "../fieldbus_dev.h"
     15
     16/* move to <linux/anybuss-client.h> when taking this out of staging */
     17#include "anybuss-client.h"
     18
     19#define PROFI_DPRAM_SIZE	512
     20
     21/*
     22 * ---------------------------------------------------------------
     23 * Anybus Profinet mailbox messages - definitions
     24 * ---------------------------------------------------------------
     25 * note that we're depending on the layout of these structures being
     26 * exactly as advertised.
     27 */
     28
     29struct msg_mac_addr {
     30	u8 addr[6];
     31};
     32
     33struct profi_priv {
     34	struct fieldbus_dev fbdev;
     35	struct anybuss_client *client;
     36	struct mutex enable_lock; /* serializes card enable */
     37	bool power_on;
     38};
     39
     40static ssize_t
     41profi_read_area(struct fieldbus_dev *fbdev, char __user *buf, size_t size,
     42		loff_t *offset)
     43{
     44	struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
     45
     46	return anybuss_read_output(priv->client, buf, size, offset);
     47}
     48
     49static ssize_t
     50profi_write_area(struct fieldbus_dev *fbdev, const char __user *buf,
     51		 size_t size, loff_t *offset)
     52{
     53	struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
     54
     55	return anybuss_write_input(priv->client, buf, size, offset);
     56}
     57
     58static int profi_id_get(struct fieldbus_dev *fbdev, char *buf,
     59			size_t max_size)
     60{
     61	struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
     62	struct msg_mac_addr response;
     63	int ret;
     64
     65	ret = anybuss_recv_msg(priv->client, 0x0010, &response,
     66			       sizeof(response));
     67	if (ret < 0)
     68		return ret;
     69	return snprintf(buf, max_size, "%pM\n", response.addr);
     70}
     71
     72static bool profi_enable_get(struct fieldbus_dev *fbdev)
     73{
     74	struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
     75	bool power_on;
     76
     77	mutex_lock(&priv->enable_lock);
     78	power_on = priv->power_on;
     79	mutex_unlock(&priv->enable_lock);
     80
     81	return power_on;
     82}
     83
     84static int __profi_enable(struct profi_priv *priv)
     85{
     86	int ret;
     87	struct anybuss_client *client = priv->client;
     88	/* Initialization Sequence, Generic Anybus Mode */
     89	const struct anybuss_memcfg mem_cfg = {
     90		.input_io = 220,
     91		.input_dpram = PROFI_DPRAM_SIZE,
     92		.input_total = PROFI_DPRAM_SIZE,
     93		.output_io = 220,
     94		.output_dpram = PROFI_DPRAM_SIZE,
     95		.output_total = PROFI_DPRAM_SIZE,
     96		.offl_mode = FIELDBUS_DEV_OFFL_MODE_CLEAR,
     97	};
     98
     99	/*
    100	 * switch anybus off then on, this ensures we can do a complete
    101	 * configuration cycle in case anybus was already on.
    102	 */
    103	anybuss_set_power(client, false);
    104	ret = anybuss_set_power(client, true);
    105	if (ret)
    106		goto err;
    107	ret = anybuss_start_init(client, &mem_cfg);
    108	if (ret)
    109		goto err;
    110	ret = anybuss_finish_init(client);
    111	if (ret)
    112		goto err;
    113	priv->power_on = true;
    114	return 0;
    115
    116err:
    117	anybuss_set_power(client, false);
    118	priv->power_on = false;
    119	return ret;
    120}
    121
    122static int __profi_disable(struct profi_priv *priv)
    123{
    124	struct anybuss_client *client = priv->client;
    125
    126	anybuss_set_power(client, false);
    127	priv->power_on = false;
    128	return 0;
    129}
    130
    131static int profi_simple_enable(struct fieldbus_dev *fbdev, bool enable)
    132{
    133	int ret;
    134	struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
    135
    136	mutex_lock(&priv->enable_lock);
    137	if (enable)
    138		ret = __profi_enable(priv);
    139	else
    140		ret = __profi_disable(priv);
    141	mutex_unlock(&priv->enable_lock);
    142
    143	return ret;
    144}
    145
    146static void profi_on_area_updated(struct anybuss_client *client)
    147{
    148	struct profi_priv *priv = anybuss_get_drvdata(client);
    149
    150	fieldbus_dev_area_updated(&priv->fbdev);
    151}
    152
    153static void profi_on_online_changed(struct anybuss_client *client, bool online)
    154{
    155	struct profi_priv *priv = anybuss_get_drvdata(client);
    156
    157	fieldbus_dev_online_changed(&priv->fbdev, online);
    158}
    159
    160static int profinet_probe(struct anybuss_client *client)
    161{
    162	struct profi_priv *priv;
    163	struct device *dev = &client->dev;
    164	int err;
    165
    166	client->on_area_updated = profi_on_area_updated;
    167	client->on_online_changed = profi_on_online_changed;
    168	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    169	if (!priv)
    170		return -ENOMEM;
    171	mutex_init(&priv->enable_lock);
    172	priv->client = client;
    173	priv->fbdev.read_area_sz = PROFI_DPRAM_SIZE;
    174	priv->fbdev.write_area_sz = PROFI_DPRAM_SIZE;
    175	priv->fbdev.card_name = "HMS Profinet IRT (Anybus-S)";
    176	priv->fbdev.fieldbus_type = FIELDBUS_DEV_TYPE_PROFINET;
    177	priv->fbdev.read_area = profi_read_area;
    178	priv->fbdev.write_area = profi_write_area;
    179	priv->fbdev.fieldbus_id_get = profi_id_get;
    180	priv->fbdev.enable_get = profi_enable_get;
    181	priv->fbdev.simple_enable_set = profi_simple_enable;
    182	priv->fbdev.parent = dev;
    183	err = fieldbus_dev_register(&priv->fbdev);
    184	if (err < 0)
    185		return err;
    186	dev_info(dev, "card detected, registered as %s",
    187		 dev_name(priv->fbdev.dev));
    188	anybuss_set_drvdata(client, priv);
    189
    190	return 0;
    191}
    192
    193static void profinet_remove(struct anybuss_client *client)
    194{
    195	struct profi_priv *priv = anybuss_get_drvdata(client);
    196
    197	fieldbus_dev_unregister(&priv->fbdev);
    198}
    199
    200static struct anybuss_client_driver profinet_driver = {
    201	.probe = profinet_probe,
    202	.remove = profinet_remove,
    203	.driver		= {
    204		.name   = "hms-profinet",
    205		.owner	= THIS_MODULE,
    206	},
    207	.anybus_id = 0x0089,
    208};
    209
    210static int __init profinet_init(void)
    211{
    212	return anybuss_client_driver_register(&profinet_driver);
    213}
    214module_init(profinet_init);
    215
    216static void __exit profinet_exit(void)
    217{
    218	return anybuss_client_driver_unregister(&profinet_driver);
    219}
    220module_exit(profinet_exit);
    221
    222MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
    223MODULE_DESCRIPTION("HMS Profinet IRT Driver (Anybus-S)");
    224MODULE_LICENSE("GPL v2");