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");