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 29 struct msg_mac_addr { 30 u8 addr[6]; 31 }; 32 33 struct 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 40 static ssize_t 41 profi_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 49 static ssize_t 50 profi_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 58 static 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, "%02X:%02X:%02X:%02X:%02X:%02X\n", 70 response.addr[0], response.addr[1], 71 response.addr[2], response.addr[3], 72 response.addr[4], response.addr[5]); 73 } 74 75 static bool profi_enable_get(struct fieldbus_dev *fbdev) 76 { 77 struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev); 78 bool power_on; 79 80 mutex_lock(&priv->enable_lock); 81 power_on = priv->power_on; 82 mutex_unlock(&priv->enable_lock); 83 84 return power_on; 85 } 86 87 static int __profi_enable(struct profi_priv *priv) 88 { 89 int ret; 90 struct anybuss_client *client = priv->client; 91 /* Initialization Sequence, Generic Anybus Mode */ 92 const struct anybuss_memcfg mem_cfg = { 93 .input_io = 220, 94 .input_dpram = PROFI_DPRAM_SIZE, 95 .input_total = PROFI_DPRAM_SIZE, 96 .output_io = 220, 97 .output_dpram = PROFI_DPRAM_SIZE, 98 .output_total = PROFI_DPRAM_SIZE, 99 .offl_mode = FIELDBUS_DEV_OFFL_MODE_CLEAR, 100 }; 101 102 /* 103 * switch anybus off then on, this ensures we can do a complete 104 * configuration cycle in case anybus was already on. 105 */ 106 anybuss_set_power(client, false); 107 ret = anybuss_set_power(client, true); 108 if (ret) 109 goto err; 110 ret = anybuss_start_init(client, &mem_cfg); 111 if (ret) 112 goto err; 113 ret = anybuss_finish_init(client); 114 if (ret) 115 goto err; 116 priv->power_on = true; 117 return 0; 118 119 err: 120 anybuss_set_power(client, false); 121 priv->power_on = false; 122 return ret; 123 } 124 125 static int __profi_disable(struct profi_priv *priv) 126 { 127 struct anybuss_client *client = priv->client; 128 129 anybuss_set_power(client, false); 130 priv->power_on = false; 131 return 0; 132 } 133 134 static int profi_simple_enable(struct fieldbus_dev *fbdev, bool enable) 135 { 136 int ret; 137 struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev); 138 139 mutex_lock(&priv->enable_lock); 140 if (enable) 141 ret = __profi_enable(priv); 142 else 143 ret = __profi_disable(priv); 144 mutex_unlock(&priv->enable_lock); 145 146 return ret; 147 } 148 149 static void profi_on_area_updated(struct anybuss_client *client) 150 { 151 struct profi_priv *priv = anybuss_get_drvdata(client); 152 153 fieldbus_dev_area_updated(&priv->fbdev); 154 } 155 156 static void profi_on_online_changed(struct anybuss_client *client, bool online) 157 { 158 struct profi_priv *priv = anybuss_get_drvdata(client); 159 160 fieldbus_dev_online_changed(&priv->fbdev, online); 161 } 162 163 static int profinet_probe(struct anybuss_client *client) 164 { 165 struct profi_priv *priv; 166 struct device *dev = &client->dev; 167 int err; 168 169 client->on_area_updated = profi_on_area_updated; 170 client->on_online_changed = profi_on_online_changed; 171 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 172 if (!priv) 173 return -ENOMEM; 174 mutex_init(&priv->enable_lock); 175 priv->client = client; 176 priv->fbdev.read_area_sz = PROFI_DPRAM_SIZE; 177 priv->fbdev.write_area_sz = PROFI_DPRAM_SIZE; 178 priv->fbdev.card_name = "HMS Profinet IRT (Anybus-S)"; 179 priv->fbdev.fieldbus_type = FIELDBUS_DEV_TYPE_PROFINET; 180 priv->fbdev.read_area = profi_read_area; 181 priv->fbdev.write_area = profi_write_area; 182 priv->fbdev.fieldbus_id_get = profi_id_get; 183 priv->fbdev.enable_get = profi_enable_get; 184 priv->fbdev.simple_enable_set = profi_simple_enable; 185 priv->fbdev.parent = dev; 186 err = fieldbus_dev_register(&priv->fbdev); 187 if (err < 0) 188 return err; 189 dev_info(dev, "card detected, registered as %s", 190 dev_name(priv->fbdev.dev)); 191 anybuss_set_drvdata(client, priv); 192 193 return 0; 194 } 195 196 static int profinet_remove(struct anybuss_client *client) 197 { 198 struct profi_priv *priv = anybuss_get_drvdata(client); 199 200 fieldbus_dev_unregister(&priv->fbdev); 201 return 0; 202 } 203 204 static struct anybuss_client_driver profinet_driver = { 205 .probe = profinet_probe, 206 .remove = profinet_remove, 207 .driver = { 208 .name = "hms-profinet", 209 .owner = THIS_MODULE, 210 }, 211 .anybus_id = 0x0089, 212 }; 213 214 static int __init profinet_init(void) 215 { 216 return anybuss_client_driver_register(&profinet_driver); 217 } 218 module_init(profinet_init); 219 220 static void __exit profinet_exit(void) 221 { 222 return anybuss_client_driver_unregister(&profinet_driver); 223 } 224 module_exit(profinet_exit); 225 226 MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>"); 227 MODULE_DESCRIPTION("HMS Profinet IRT Driver (Anybus-S)"); 228 MODULE_LICENSE("GPL v2"); 229