Provides the generic BUS protocol abstraction layer that is used for the Control and Data communication with the Infineon's WLAN Device over the SDIO BUS types. Signed-off-by: Gokul Sivakumar --- .../net/wireless/infineon/inffmac/bus_proto.c | 162 +++++++ .../net/wireless/infineon/inffmac/bus_proto.h | 436 ++++++++++++++++++ 2 files changed, 598 insertions(+) create mode 100644 drivers/net/wireless/infineon/inffmac/bus_proto.c create mode 100644 drivers/net/wireless/infineon/inffmac/bus_proto.h diff --git a/drivers/net/wireless/infineon/inffmac/bus_proto.c b/drivers/net/wireless/infineon/inffmac/bus_proto.c new file mode 100644 index 000000000000..8ea899e2e037 --- /dev/null +++ b/drivers/net/wireless/infineon/inffmac/bus_proto.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2013 Broadcom Corporation + * + * Copyright (c) 2025-2026, Infineon Technologies AG, or an affiliate of Infineon Technologies AG. + * All rights reserved. + */ + +#include +#include +#include + +#include "main.h" +#include "cfg80211.h" +#include "debug.h" +#include "bus_proto.h" +#include "bcdc.h" +#include "icdc.h" + +static void inff_core_bus_reset(struct work_struct *work) +{ + struct inff_pub *drvr = container_of(work, struct inff_pub, + bus_reset); + + /* WLAN REG TOGGLE */ + inff_bus_wlan_regon_gpio_toggle(0); + msleep(100); + inff_bus_wlan_regon_gpio_toggle(1); + + inff_bus_reset(drvr->bus_if); +} + +static ssize_t +inff_bus_reset_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct inff_pub *drvr = file->private_data; + u8 value; + + if (kstrtou8_from_user(user_buf, count, 0, &value)) + return -EINVAL; + + if (value != 1) + return -EINVAL; + + schedule_work(&drvr->bus_reset); + + return count; +} + +static const struct file_operations bus_reset_fops = { + .open = simple_open, + .write = inff_bus_reset_write, +}; + +void inff_bus_proto_debugfs_create(struct inff_pub *drvr) +{ + struct inff_bus *bus = drvr->bus_if; + + debugfs_create_file("reset", 0600, inff_debugfs_get_devdir(drvr), + drvr, &bus_reset_fops); + + if (bus->ops->debugfs_create) + bus->ops->debugfs_create(bus->dev); + + if (drvr->proto->debugfs_create) + drvr->proto->debugfs_create(drvr); +} + +void inff_bus_change_state(struct inff_bus *bus, enum inff_bus_state state) +{ + struct inff_pub *drvr = bus->drvr; + struct net_device *ndev; + int ifidx; + + inff_dbg(TRACE, "%d -> %d\n", bus->state, state); + + if (!drvr) { + inff_dbg(INFO, "ignoring transition, bus not attached yet\n"); + return; + } + + bus->state = state; + + if (state == INFF_BUS_UP) { + for (ifidx = 0; ifidx < INFF_MAX_IFS; ifidx++) { + if (drvr->iflist[ifidx] && + drvr->iflist[ifidx]->ndev) { + ndev = drvr->iflist[ifidx]->ndev; + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + } + } + } +} + +void inff_bus_add_txhdrlen(struct device *dev, uint len) +{ + struct inff_bus *bus_if = dev_get_drvdata(dev); + struct inff_pub *drvr = bus_if->drvr; + + if (drvr) + drvr->hdrlen += len; +} + +void inff_bus_wlan_regon_gpio_toggle(u8 gpio_on) +{ + /* WLAN REG ON/OFF Toggle code to be added */ +} + +int inff_bus_proto_attach(struct inff_pub *drvr) +{ + struct inff_proto *proto; + + proto = kzalloc_obj(*proto, GFP_ATOMIC); + if (!proto) + goto fail; + + drvr->proto = proto; + + if (drvr->bus_if->proto_type == INFF_PROTO_ICDC) { + if (inff_proto_icdc_attach(drvr)) + goto fail; + } else if (drvr->bus_if->proto_type == INFF_PROTO_BCDC) { + if (inff_proto_bcdc_attach(drvr)) + goto fail; + } else { + iphy_err(drvr, "Unsupported proto type %d\n", + drvr->bus_if->proto_type); + goto fail; + } + if (!proto->tx_queue_data || + !proto->query_fwcmd || !proto->set_fwcmd || + !proto->configure_addr_mode || + !proto->delete_peer || + !proto->debugfs_create) { + iphy_err(drvr, "Not all proto handlers have been installed\n"); + goto fail; + } + + INIT_WORK(&drvr->bus_reset, inff_core_bus_reset); + + return 0; + +fail: + kfree(proto); + drvr->proto = NULL; + return -ENOMEM; +} + +void inff_bus_proto_detach(struct inff_pub *drvr) +{ + if (drvr->proto) { + if (drvr->bus_if->proto_type == INFF_PROTO_ICDC) + inff_proto_icdc_detach(drvr); + else if (drvr->bus_if->proto_type == INFF_PROTO_BCDC) + inff_proto_bcdc_detach(drvr); + + kfree(drvr->proto); + drvr->proto = NULL; + } +} diff --git a/drivers/net/wireless/infineon/inffmac/bus_proto.h b/drivers/net/wireless/infineon/inffmac/bus_proto.h new file mode 100644 index 000000000000..fffcc4f2db25 --- /dev/null +++ b/drivers/net/wireless/infineon/inffmac/bus_proto.h @@ -0,0 +1,436 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (c) 2013 Broadcom Corporation + * + * Copyright (c) 2025-2026, Infineon Technologies AG, or an affiliate of Infineon Technologies AG. + * All rights reserved. + */ + +#ifndef INFF_BUS_PROTO_H +#define INFF_BUS_PROTO_H + +/* State of bus for communication with the dongle */ +enum inff_bus_state { + INFF_BUS_DOWN, /* Not ready for frame transfers */ + INFF_BUS_UP /* Ready for frame transfers */ +}; + +/* Protocol of bus communication with the dongle */ +enum inff_bus_protocol_type { + INFF_PROTO_ICDC, + INFF_PROTO_BCDC +}; + +struct inff_bus_dcmd { + char *name; + char *param; + int param_len; + struct list_head list; +}; + +/* Firmware blobs that may be available */ +enum inff_blob_type { + INFF_BLOB_CLM, +}; + +/** + * struct inff_bus_ops - bus callback operations. + * + * @preinit: execute bus/device specific dongle init commands (optional). + * @init: prepare for communication with dongle. + * @stop: clear pending frames, disable data flow. + * @txdata: send a data frame to the dongle. When the data + * has been transferred, the common driver must be + * notified using inff_txcomplete(). The common + * driver calls this function with interrupts + * disabled. + * @txctl: transmit a control request message to dongle. + * @rxctl: receive a control response message from dongle. + * @gettxq: obtain a reference of bus transmit queue (optional). + * @get_rambase: obtain base address of wifiss. + * @get_ramsize: obtain memory size of wifiss. + * @get_cpuss_rambase: obtain base address of cpuss. + * @get_cpuss_ramsize: obtain memory size of cpuss. + * @get_blob: obtain a firmware blob. + * @remove: initiate unbind of the device. + * @interrupt_enable: enable the interrupts from the device. + * @interrupt_disable: disable the interrupts from the device. + * + * This structure provides an abstract interface towards the + * bus specific driver. For control messages to common driver + * will assure there is only one active transaction. Unless + * indicated otherwise these callbacks are mandatory. + */ +struct inff_bus_ops { + int (*preinit)(struct device *dev); + void (*stop)(struct device *dev); + int (*txdata)(struct device *dev, struct sk_buff *skb); + int (*txctl)(struct device *dev, unsigned char *msg, uint len); + int (*rxctl)(struct device *dev, unsigned char *msg, uint len); + struct pktq * (*gettxq)(struct device *dev); + size_t (*get_rambase)(struct device *dev); + size_t (*get_ramsize)(struct device *dev); + size_t (*get_cpuss_rambase)(struct device *dev); + size_t (*get_cpuss_ramsize)(struct device *dev); + int (*get_blob)(struct device *dev, const struct firmware **fw, + enum inff_blob_type type); + void (*debugfs_create)(struct device *dev); + int (*reset)(struct device *dev); + void (*remove)(struct device *dev); + int (*set_fcmode)(struct device *dev); + int (*napi_poll)(struct napi_struct *napi, int budget); + void (*interrupt_enable)(struct device *dev); + void (*interrupt_disable)(struct device *dev); +}; + +/** + * struct inff_bus_stats - bus statistic counters. + * + * @pktcowed: packets cowed for extra headroom/unorphan. + * @pktcow_failed: packets dropped due to failed cow-ing. + */ +struct inff_bus_stats { + atomic_t pktcowed; + atomic_t pktcow_failed; +}; + +/** + * struct inff_bus - interface structure between common and bus layer + * + * @bus_priv: pointer to private bus device. + * @proto_type: protocol type, icdc or bcdc + * @dev: device pointer of bus device. + * @drvr: public driver information. + * @state: operational state of the bus interface. + * @stats: statistics shared between common and bus layer. + * @maxctl: maximum size for rxctl request message. + * @chip: device identifier of the dongle chip. + * @always_use_fws_queue: bus wants use queue also when fwsignal is inactive. + * @chiprev: revision of the dongle chip. + */ +struct inff_bus { + union { + struct inff_sdio_dev *sdio; + } bus_priv; + enum inff_bus_protocol_type proto_type; + struct device *dev; + struct inff_pub *drvr; + enum inff_bus_state state; + struct inff_bus_stats stats; + uint maxctl; + u32 chip; + u32 chiprev; + bool always_use_fws_queue; + + const struct inff_bus_ops *ops; + struct inff_chip *chip_pub; +}; + +enum proto_addr_mode { + ADDR_INDIRECT = 0, + ADDR_DIRECT +}; + +struct inff_skb_reorder_data { + u8 *reorder; +}; + +struct inff_proto { + int (*query_cpcmd)(struct inff_pub *drvr, int ifidx, uint cmd, + void *buf, uint len, int *fwerr); + int (*set_cpcmd)(struct inff_pub *drvr, int ifidx, uint cmd, void *buf, + uint len, int *fwerr); + int (*query_fwcmd)(struct inff_pub *drvr, int ifidx, uint cmd, + void *buf, uint len, int *fwerr); + int (*set_fwcmd)(struct inff_pub *drvr, int ifidx, uint cmd, void *buf, + uint len, int *fwerr); + int (*hdrpull)(struct inff_pub *drvr, bool do_fws, + struct sk_buff *skb, struct inff_if **ifp); + int (*tx_queue_data)(struct inff_pub *drvr, int ifidx, + struct sk_buff *skb); + int (*txdata)(struct inff_pub *drvr, int ifidx, u8 offset, + struct sk_buff *skb); + void (*configure_addr_mode)(struct inff_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode); + void (*delete_peer)(struct inff_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]); + void (*rxreorder)(struct inff_if *ifp, struct sk_buff *skb, bool inirq); + void (*add_if)(struct inff_if *ifp); + void (*del_if)(struct inff_if *ifp); + void (*reset_if)(struct inff_if *ifp); + void (*cleanup_if)(struct inff_if *ifp); + int (*init_done)(struct inff_pub *drvr); + void (*debugfs_create)(struct inff_pub *drvr); + void *pd; +}; + +/* + * callback wrappers + */ +static inline int +inff_bus_preinit(struct inff_bus *bus) +{ + if (!bus->ops->preinit) + return 0; + return bus->ops->preinit(bus->dev); +} + +static inline void +inff_bus_stop(struct inff_bus *bus) +{ + bus->ops->stop(bus->dev); +} + +static inline int +inff_bus_txdata(struct inff_bus *bus, struct sk_buff *skb) +{ + return bus->ops->txdata(bus->dev, skb); +} + +static inline int +inff_bus_txctl(struct inff_bus *bus, unsigned char *msg, uint len) +{ + return bus->ops->txctl(bus->dev, msg, len); +} + +static inline int +inff_bus_rxctl(struct inff_bus *bus, unsigned char *msg, uint len) +{ + return bus->ops->rxctl(bus->dev, msg, len); +} + +static inline struct pktq * +inff_bus_gettxq(struct inff_bus *bus) +{ + if (!bus->ops->gettxq) + return ERR_PTR(-ENOENT); + + return bus->ops->gettxq(bus->dev); +} + +static inline size_t +inff_bus_get_rambase(struct inff_bus *bus) +{ + if (!bus->ops->get_rambase) + return 0; + + return bus->ops->get_rambase(bus->dev); +} + +static inline size_t +inff_bus_get_ramsize(struct inff_bus *bus) +{ + if (!bus->ops->get_ramsize) + return 0; + + return bus->ops->get_ramsize(bus->dev); +} + +static inline size_t +inff_bus_get_cpuss_rambase(struct inff_bus *bus) +{ + if (!bus->ops->get_cpuss_rambase) + return 0; + + return bus->ops->get_cpuss_rambase(bus->dev); +} + +static inline size_t +inff_bus_get_cpuss_ramsize(struct inff_bus *bus) +{ + if (!bus->ops->get_cpuss_ramsize) + return 0; + + return bus->ops->get_cpuss_ramsize(bus->dev); +} + +static inline int +inff_bus_get_blob(struct inff_bus *bus, const struct firmware **fw, + enum inff_blob_type type) +{ + return bus->ops->get_blob(bus->dev, fw, type); +} + +static inline int +inff_bus_reset(struct inff_bus *bus) +{ + if (!bus->ops->reset) + return -EOPNOTSUPP; + + return bus->ops->reset(bus->dev); +} + +static inline void +inff_bus_remove(struct inff_bus *bus) +{ + if (!bus->ops->remove) { + device_release_driver(bus->dev); + return; + } + + bus->ops->remove(bus->dev); +} + +static inline int +inff_bus_set_fcmode(struct inff_bus *bus) +{ + if (!bus->ops->set_fcmode) + return -EOPNOTSUPP; + + return bus->ops->set_fcmode(bus->dev); +} + +static inline void +inff_bus_interrupt_enable(struct inff_bus *bus) +{ + if (!bus->ops->interrupt_enable) + return; + + bus->ops->interrupt_enable(bus->dev); +} + +static inline void +inff_bus_interrupt_disable(struct inff_bus *bus) +{ + if (!bus->ops->interrupt_disable) + return; + + bus->ops->interrupt_disable(bus->dev); +} + +static inline int +inff_proto_query_cpcmd(struct inff_pub *drvr, int ifidx, + uint cmd, void *buf, uint len, + int *cperr) +{ + return drvr->proto->query_cpcmd(drvr, ifidx, cmd, buf, len, cperr); +} + +static inline int +inff_proto_set_cpcmd(struct inff_pub *drvr, int ifidx, + uint cmd, void *buf, uint len, int *cperr) +{ + return drvr->proto->set_cpcmd(drvr, ifidx, cmd, buf, len, cperr); +} + +static inline int +inff_proto_query_fwcmd(struct inff_pub *drvr, int ifidx, + uint cmd, void *buf, uint len, + int *fwerr) +{ + return drvr->proto->query_fwcmd(drvr, ifidx, cmd, buf, len, fwerr); +} + +static inline int +inff_proto_set_fwcmd(struct inff_pub *drvr, int ifidx, + uint cmd, void *buf, uint len, int *fwerr) +{ + return drvr->proto->set_fwcmd(drvr, ifidx, cmd, buf, len, fwerr); +} + +static inline int +inff_proto_hdrpull(struct inff_pub *drvr, bool do_fws, + struct sk_buff *skb, + struct inff_if **ifp) +{ + struct inff_if *tmp = NULL; + + /* assure protocol is always called with + * non-null initialized pointer. + */ + if (ifp) + *ifp = NULL; + else + ifp = &tmp; + return drvr->proto->hdrpull(drvr, do_fws, skb, ifp); +} + +static inline int +inff_proto_tx_queue_data(struct inff_pub *drvr, int ifidx, + struct sk_buff *skb) +{ + return drvr->proto->tx_queue_data(drvr, ifidx, skb); +} + +static inline int +inff_proto_txdata(struct inff_pub *drvr, int ifidx, + u8 offset, struct sk_buff *skb) +{ + return drvr->proto->txdata(drvr, ifidx, offset, skb); +} + +static inline void +inff_proto_configure_addr_mode(struct inff_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode) +{ + drvr->proto->configure_addr_mode(drvr, ifidx, addr_mode); +} + +static inline void +inff_proto_delete_peer(struct inff_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + drvr->proto->delete_peer(drvr, ifidx, peer); +} + +static inline bool inff_proto_is_reorder_skb(struct sk_buff *skb) +{ + struct inff_skb_reorder_data *rd; + + rd = (struct inff_skb_reorder_data *)skb->cb; + return !!rd->reorder; +} + +static inline void +inff_proto_rxreorder(struct inff_if *ifp, struct sk_buff *skb, bool inirq) +{ + ifp->drvr->proto->rxreorder(ifp, skb, inirq); +} + +static inline void +inff_proto_add_if(struct inff_pub *drvr, struct inff_if *ifp) +{ + if (!drvr->proto->add_if) + return; + drvr->proto->add_if(ifp); +} + +static inline void +inff_proto_del_if(struct inff_pub *drvr, struct inff_if *ifp) +{ + if (!drvr->proto->del_if) + return; + drvr->proto->del_if(ifp); +} + +static inline void +inff_proto_reset_if(struct inff_pub *drvr, struct inff_if *ifp) +{ + if (!drvr->proto->reset_if) + return; + drvr->proto->reset_if(ifp); +} + +static inline void +inff_proto_cleanup_if(struct inff_pub *drvr, struct inff_if *ifp) +{ + if (!drvr->proto->cleanup_if) + return; + drvr->proto->cleanup_if(ifp); +} + +static inline int +inff_proto_init_done(struct inff_pub *drvr) +{ + if (!drvr->proto->init_done) + return 0; + return drvr->proto->init_done(drvr); +} + +void inff_bus_proto_debugfs_create(struct inff_pub *drvr); +void inff_bus_change_state(struct inff_bus *bus, enum inff_bus_state state); +void inff_bus_add_txhdrlen(struct device *dev, uint len); +void inff_bus_wlan_regon_gpio_toggle(u8 gpio_on); +int inff_bus_proto_attach(struct inff_pub *drvr); +void inff_bus_proto_detach(struct inff_pub *drvr); + +#endif /* INFF_BUS_PROTO_H */ -- 2.43.0