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