diff options
author | David S. Miller <davem@davemloft.net> | 2015-08-24 17:06:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-08-24 17:06:37 -0400 |
commit | 56fff0a01fa056502a28d67cb5a2714d64780415 (patch) | |
tree | f9f59b7da4c673d3f7cdbc5322e0e21c122fddfb | |
parent | 4a89ba04ecc6377696e4e26c1abc1cb5764decb9 (diff) | |
parent | 786eec27cbb1713caf3b33538a877a21e779d3bc (diff) |
Merge branch 'fjes'
Taku Izumi says:
====================
FUJITSU Extended Socket network device driver
This patchsets adds FUJITSU Extended Socket network device driver.
Extended Socket network device is a shared memory based high-speed
network interface between Extended Partitions of PRIMEQUEST 2000 E2
series.
You can get some information about Extended Partition and Extended
Socket by referring the following manual.
http://globalsp.ts.fujitsu.com/dmsp/Publications/public/CA92344-0537.pdf
3.2.1 Extended Partitioning
3.2.2 Extended Socke
v2.2 -> v3:
- Fix up according to David's comment (No functional change)
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/fjes/Makefile | 30 | ||||
-rw-r--r-- | drivers/net/fjes/fjes.h | 77 | ||||
-rw-r--r-- | drivers/net/fjes/fjes_ethtool.c | 137 | ||||
-rw-r--r-- | drivers/net/fjes/fjes_hw.c | 1125 | ||||
-rw-r--r-- | drivers/net/fjes/fjes_hw.h | 334 | ||||
-rw-r--r-- | drivers/net/fjes/fjes_main.c | 1383 | ||||
-rw-r--r-- | drivers/net/fjes/fjes_regs.h | 142 |
9 files changed, 3237 insertions, 0 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f50373645ab4..770483b31d62 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig | |||
@@ -413,6 +413,13 @@ config VMXNET3 | |||
413 | To compile this driver as a module, choose M here: the | 413 | To compile this driver as a module, choose M here: the |
414 | module will be called vmxnet3. | 414 | module will be called vmxnet3. |
415 | 415 | ||
416 | config FUJITSU_ES | ||
417 | tristate "FUJITSU Extended Socket Network Device driver" | ||
418 | depends on ACPI | ||
419 | help | ||
420 | This driver provides support for Extended Socket network device | ||
421 | on Extended Partitioning of FUJITSU PRIMEQUEST 2000 E2 series. | ||
422 | |||
416 | source "drivers/net/hyperv/Kconfig" | 423 | source "drivers/net/hyperv/Kconfig" |
417 | 424 | ||
418 | endif # NETDEVICES | 425 | endif # NETDEVICES |
diff --git a/drivers/net/Makefile b/drivers/net/Makefile index ca16dd689b36..900b0c5320bb 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile | |||
@@ -68,3 +68,5 @@ obj-$(CONFIG_USB_NET_DRIVERS) += usb/ | |||
68 | 68 | ||
69 | obj-$(CONFIG_HYPERV_NET) += hyperv/ | 69 | obj-$(CONFIG_HYPERV_NET) += hyperv/ |
70 | obj-$(CONFIG_NTB_NETDEV) += ntb_netdev.o | 70 | obj-$(CONFIG_NTB_NETDEV) += ntb_netdev.o |
71 | |||
72 | obj-$(CONFIG_FUJITSU_ES) += fjes/ | ||
diff --git a/drivers/net/fjes/Makefile b/drivers/net/fjes/Makefile new file mode 100644 index 000000000000..523e3d7cf7aa --- /dev/null +++ b/drivers/net/fjes/Makefile | |||
@@ -0,0 +1,30 @@ | |||
1 | ################################################################################ | ||
2 | # | ||
3 | # FUJITSU Extended Socket Network Device driver | ||
4 | # Copyright (c) 2015 FUJITSU LIMITED | ||
5 | # | ||
6 | # This program is free software; you can redistribute it and/or modify it | ||
7 | # under the terms and conditions of the GNU General Public License, | ||
8 | # version 2, as published by the Free Software Foundation. | ||
9 | # | ||
10 | # This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | # more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License along with | ||
16 | # this program; if not, see <http://www.gnu.org/licenses/>. | ||
17 | # | ||
18 | # The full GNU General Public License is included in this distribution in | ||
19 | # the file called "COPYING". | ||
20 | # | ||
21 | ################################################################################ | ||
22 | |||
23 | |||
24 | # | ||
25 | # Makefile for the FUJITSU Extended Socket network device driver | ||
26 | # | ||
27 | |||
28 | obj-$(CONFIG_FUJITSU_ES) += fjes.o | ||
29 | |||
30 | fjes-objs := fjes_main.o fjes_hw.o fjes_ethtool.o | ||
diff --git a/drivers/net/fjes/fjes.h b/drivers/net/fjes/fjes.h new file mode 100644 index 000000000000..a592fe21c698 --- /dev/null +++ b/drivers/net/fjes/fjes.h | |||
@@ -0,0 +1,77 @@ | |||
1 | /* | ||
2 | * FUJITSU Extended Socket Network Device driver | ||
3 | * Copyright (c) 2015 FUJITSU LIMITED | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, see <http://www.gnu.org/licenses/>. | ||
16 | * | ||
17 | * The full GNU General Public License is included in this distribution in | ||
18 | * the file called "COPYING". | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef FJES_H_ | ||
23 | #define FJES_H_ | ||
24 | |||
25 | #include <linux/acpi.h> | ||
26 | |||
27 | #include "fjes_hw.h" | ||
28 | |||
29 | #define FJES_ACPI_SYMBOL "Extended Socket" | ||
30 | #define FJES_MAX_QUEUES 1 | ||
31 | #define FJES_TX_RETRY_INTERVAL (20 * HZ) | ||
32 | #define FJES_TX_RETRY_TIMEOUT (100) | ||
33 | #define FJES_TX_TX_STALL_TIMEOUT (FJES_TX_RETRY_INTERVAL / 2) | ||
34 | #define FJES_OPEN_ZONE_UPDATE_WAIT (300) /* msec */ | ||
35 | #define FJES_IRQ_WATCH_DELAY (HZ) | ||
36 | |||
37 | /* board specific private data structure */ | ||
38 | struct fjes_adapter { | ||
39 | struct net_device *netdev; | ||
40 | struct platform_device *plat_dev; | ||
41 | |||
42 | struct napi_struct napi; | ||
43 | struct rtnl_link_stats64 stats64; | ||
44 | |||
45 | unsigned int tx_retry_count; | ||
46 | unsigned long tx_start_jiffies; | ||
47 | unsigned long rx_last_jiffies; | ||
48 | bool unset_rx_last; | ||
49 | |||
50 | struct work_struct force_close_task; | ||
51 | bool force_reset; | ||
52 | bool open_guard; | ||
53 | |||
54 | bool irq_registered; | ||
55 | |||
56 | struct workqueue_struct *txrx_wq; | ||
57 | struct workqueue_struct *control_wq; | ||
58 | |||
59 | struct work_struct tx_stall_task; | ||
60 | struct work_struct raise_intr_rxdata_task; | ||
61 | |||
62 | struct work_struct unshare_watch_task; | ||
63 | unsigned long unshare_watch_bitmask; | ||
64 | |||
65 | struct delayed_work interrupt_watch_task; | ||
66 | bool interrupt_watch_enable; | ||
67 | |||
68 | struct fjes_hw hw; | ||
69 | }; | ||
70 | |||
71 | extern char fjes_driver_name[]; | ||
72 | extern char fjes_driver_version[]; | ||
73 | extern const u32 fjes_support_mtu[]; | ||
74 | |||
75 | void fjes_set_ethtool_ops(struct net_device *); | ||
76 | |||
77 | #endif /* FJES_H_ */ | ||
diff --git a/drivers/net/fjes/fjes_ethtool.c b/drivers/net/fjes/fjes_ethtool.c new file mode 100644 index 000000000000..0119dd199276 --- /dev/null +++ b/drivers/net/fjes/fjes_ethtool.c | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * FUJITSU Extended Socket Network Device driver | ||
3 | * Copyright (c) 2015 FUJITSU LIMITED | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, see <http://www.gnu.org/licenses/>. | ||
16 | * | ||
17 | * The full GNU General Public License is included in this distribution in | ||
18 | * the file called "COPYING". | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | /* ethtool support for fjes */ | ||
23 | |||
24 | #include <linux/vmalloc.h> | ||
25 | #include <linux/netdevice.h> | ||
26 | #include <linux/ethtool.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | |||
29 | #include "fjes.h" | ||
30 | |||
31 | struct fjes_stats { | ||
32 | char stat_string[ETH_GSTRING_LEN]; | ||
33 | int sizeof_stat; | ||
34 | int stat_offset; | ||
35 | }; | ||
36 | |||
37 | #define FJES_STAT(name, stat) { \ | ||
38 | .stat_string = name, \ | ||
39 | .sizeof_stat = FIELD_SIZEOF(struct fjes_adapter, stat), \ | ||
40 | .stat_offset = offsetof(struct fjes_adapter, stat) \ | ||
41 | } | ||
42 | |||
43 | static const struct fjes_stats fjes_gstrings_stats[] = { | ||
44 | FJES_STAT("rx_packets", stats64.rx_packets), | ||
45 | FJES_STAT("tx_packets", stats64.tx_packets), | ||
46 | FJES_STAT("rx_bytes", stats64.rx_bytes), | ||
47 | FJES_STAT("tx_bytes", stats64.rx_bytes), | ||
48 | FJES_STAT("rx_dropped", stats64.rx_dropped), | ||
49 | FJES_STAT("tx_dropped", stats64.tx_dropped), | ||
50 | }; | ||
51 | |||
52 | static void fjes_get_ethtool_stats(struct net_device *netdev, | ||
53 | struct ethtool_stats *stats, u64 *data) | ||
54 | { | ||
55 | struct fjes_adapter *adapter = netdev_priv(netdev); | ||
56 | char *p; | ||
57 | int i; | ||
58 | |||
59 | for (i = 0; i < ARRAY_SIZE(fjes_gstrings_stats); i++) { | ||
60 | p = (char *)adapter + fjes_gstrings_stats[i].stat_offset; | ||
61 | data[i] = (fjes_gstrings_stats[i].sizeof_stat == sizeof(u64)) | ||
62 | ? *(u64 *)p : *(u32 *)p; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | static void fjes_get_strings(struct net_device *netdev, | ||
67 | u32 stringset, u8 *data) | ||
68 | { | ||
69 | u8 *p = data; | ||
70 | int i; | ||
71 | |||
72 | switch (stringset) { | ||
73 | case ETH_SS_STATS: | ||
74 | for (i = 0; i < ARRAY_SIZE(fjes_gstrings_stats); i++) { | ||
75 | memcpy(p, fjes_gstrings_stats[i].stat_string, | ||
76 | ETH_GSTRING_LEN); | ||
77 | p += ETH_GSTRING_LEN; | ||
78 | } | ||
79 | break; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | static int fjes_get_sset_count(struct net_device *netdev, int sset) | ||
84 | { | ||
85 | switch (sset) { | ||
86 | case ETH_SS_STATS: | ||
87 | return ARRAY_SIZE(fjes_gstrings_stats); | ||
88 | default: | ||
89 | return -EOPNOTSUPP; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | static void fjes_get_drvinfo(struct net_device *netdev, | ||
94 | struct ethtool_drvinfo *drvinfo) | ||
95 | { | ||
96 | struct fjes_adapter *adapter = netdev_priv(netdev); | ||
97 | struct platform_device *plat_dev; | ||
98 | |||
99 | plat_dev = adapter->plat_dev; | ||
100 | |||
101 | strlcpy(drvinfo->driver, fjes_driver_name, sizeof(drvinfo->driver)); | ||
102 | strlcpy(drvinfo->version, fjes_driver_version, | ||
103 | sizeof(drvinfo->version)); | ||
104 | |||
105 | strlcpy(drvinfo->fw_version, "none", sizeof(drvinfo->fw_version)); | ||
106 | snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info), | ||
107 | "platform:%s", plat_dev->name); | ||
108 | drvinfo->regdump_len = 0; | ||
109 | drvinfo->eedump_len = 0; | ||
110 | } | ||
111 | |||
112 | static int fjes_get_settings(struct net_device *netdev, | ||
113 | struct ethtool_cmd *ecmd) | ||
114 | { | ||
115 | ecmd->supported = 0; | ||
116 | ecmd->advertising = 0; | ||
117 | ecmd->duplex = DUPLEX_FULL; | ||
118 | ecmd->autoneg = AUTONEG_DISABLE; | ||
119 | ecmd->transceiver = XCVR_DUMMY1; | ||
120 | ecmd->port = PORT_NONE; | ||
121 | ethtool_cmd_speed_set(ecmd, 20000); /* 20Gb/s */ | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static const struct ethtool_ops fjes_ethtool_ops = { | ||
127 | .get_settings = fjes_get_settings, | ||
128 | .get_drvinfo = fjes_get_drvinfo, | ||
129 | .get_ethtool_stats = fjes_get_ethtool_stats, | ||
130 | .get_strings = fjes_get_strings, | ||
131 | .get_sset_count = fjes_get_sset_count, | ||
132 | }; | ||
133 | |||
134 | void fjes_set_ethtool_ops(struct net_device *netdev) | ||
135 | { | ||
136 | netdev->ethtool_ops = &fjes_ethtool_ops; | ||
137 | } | ||
diff --git a/drivers/net/fjes/fjes_hw.c b/drivers/net/fjes/fjes_hw.c new file mode 100644 index 000000000000..b5f4a78da828 --- /dev/null +++ b/drivers/net/fjes/fjes_hw.c | |||
@@ -0,0 +1,1125 @@ | |||
1 | /* | ||
2 | * FUJITSU Extended Socket Network Device driver | ||
3 | * Copyright (c) 2015 FUJITSU LIMITED | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, see <http://www.gnu.org/licenses/>. | ||
16 | * | ||
17 | * The full GNU General Public License is included in this distribution in | ||
18 | * the file called "COPYING". | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "fjes_hw.h" | ||
23 | #include "fjes.h" | ||
24 | |||
25 | static void fjes_hw_update_zone_task(struct work_struct *); | ||
26 | static void fjes_hw_epstop_task(struct work_struct *); | ||
27 | |||
28 | /* supported MTU list */ | ||
29 | const u32 fjes_support_mtu[] = { | ||
30 | FJES_MTU_DEFINE(8 * 1024), | ||
31 | FJES_MTU_DEFINE(16 * 1024), | ||
32 | FJES_MTU_DEFINE(32 * 1024), | ||
33 | FJES_MTU_DEFINE(64 * 1024), | ||
34 | 0 | ||
35 | }; | ||
36 | |||
37 | u32 fjes_hw_rd32(struct fjes_hw *hw, u32 reg) | ||
38 | { | ||
39 | u8 *base = hw->base; | ||
40 | u32 value = 0; | ||
41 | |||
42 | value = readl(&base[reg]); | ||
43 | |||
44 | return value; | ||
45 | } | ||
46 | |||
47 | static u8 *fjes_hw_iomap(struct fjes_hw *hw) | ||
48 | { | ||
49 | u8 *base; | ||
50 | |||
51 | if (!request_mem_region(hw->hw_res.start, hw->hw_res.size, | ||
52 | fjes_driver_name)) { | ||
53 | pr_err("request_mem_region failed\n"); | ||
54 | return NULL; | ||
55 | } | ||
56 | |||
57 | base = (u8 *)ioremap_nocache(hw->hw_res.start, hw->hw_res.size); | ||
58 | |||
59 | return base; | ||
60 | } | ||
61 | |||
62 | static void fjes_hw_iounmap(struct fjes_hw *hw) | ||
63 | { | ||
64 | iounmap(hw->base); | ||
65 | release_mem_region(hw->hw_res.start, hw->hw_res.size); | ||
66 | } | ||
67 | |||
68 | int fjes_hw_reset(struct fjes_hw *hw) | ||
69 | { | ||
70 | union REG_DCTL dctl; | ||
71 | int timeout; | ||
72 | |||
73 | dctl.reg = 0; | ||
74 | dctl.bits.reset = 1; | ||
75 | wr32(XSCT_DCTL, dctl.reg); | ||
76 | |||
77 | timeout = FJES_DEVICE_RESET_TIMEOUT * 1000; | ||
78 | dctl.reg = rd32(XSCT_DCTL); | ||
79 | while ((dctl.bits.reset == 1) && (timeout > 0)) { | ||
80 | msleep(1000); | ||
81 | dctl.reg = rd32(XSCT_DCTL); | ||
82 | timeout -= 1000; | ||
83 | } | ||
84 | |||
85 | return timeout > 0 ? 0 : -EIO; | ||
86 | } | ||
87 | |||
88 | static int fjes_hw_get_max_epid(struct fjes_hw *hw) | ||
89 | { | ||
90 | union REG_MAX_EP info; | ||
91 | |||
92 | info.reg = rd32(XSCT_MAX_EP); | ||
93 | |||
94 | return info.bits.maxep; | ||
95 | } | ||
96 | |||
97 | static int fjes_hw_get_my_epid(struct fjes_hw *hw) | ||
98 | { | ||
99 | union REG_OWNER_EPID info; | ||
100 | |||
101 | info.reg = rd32(XSCT_OWNER_EPID); | ||
102 | |||
103 | return info.bits.epid; | ||
104 | } | ||
105 | |||
106 | static int fjes_hw_alloc_shared_status_region(struct fjes_hw *hw) | ||
107 | { | ||
108 | size_t size; | ||
109 | |||
110 | size = sizeof(struct fjes_device_shared_info) + | ||
111 | (sizeof(u8) * hw->max_epid); | ||
112 | hw->hw_info.share = kzalloc(size, GFP_KERNEL); | ||
113 | if (!hw->hw_info.share) | ||
114 | return -ENOMEM; | ||
115 | |||
116 | hw->hw_info.share->epnum = hw->max_epid; | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static void fjes_hw_free_shared_status_region(struct fjes_hw *hw) | ||
122 | { | ||
123 | kfree(hw->hw_info.share); | ||
124 | hw->hw_info.share = NULL; | ||
125 | } | ||
126 | |||
127 | static int fjes_hw_alloc_epbuf(struct epbuf_handler *epbh) | ||
128 | { | ||
129 | void *mem; | ||
130 | |||
131 | mem = vzalloc(EP_BUFFER_SIZE); | ||
132 | if (!mem) | ||
133 | return -ENOMEM; | ||
134 | |||
135 | epbh->buffer = mem; | ||
136 | epbh->size = EP_BUFFER_SIZE; | ||
137 | |||
138 | epbh->info = (union ep_buffer_info *)mem; | ||
139 | epbh->ring = (u8 *)(mem + sizeof(union ep_buffer_info)); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static void fjes_hw_free_epbuf(struct epbuf_handler *epbh) | ||
145 | { | ||
146 | if (epbh->buffer) | ||
147 | vfree(epbh->buffer); | ||
148 | |||
149 | epbh->buffer = NULL; | ||
150 | epbh->size = 0; | ||
151 | |||
152 | epbh->info = NULL; | ||
153 | epbh->ring = NULL; | ||
154 | } | ||
155 | |||
156 | void fjes_hw_setup_epbuf(struct epbuf_handler *epbh, u8 *mac_addr, u32 mtu) | ||
157 | { | ||
158 | union ep_buffer_info *info = epbh->info; | ||
159 | u16 vlan_id[EP_BUFFER_SUPPORT_VLAN_MAX]; | ||
160 | int i; | ||
161 | |||
162 | for (i = 0; i < EP_BUFFER_SUPPORT_VLAN_MAX; i++) | ||
163 | vlan_id[i] = info->v1i.vlan_id[i]; | ||
164 | |||
165 | memset(info, 0, sizeof(union ep_buffer_info)); | ||
166 | |||
167 | info->v1i.version = 0; /* version 0 */ | ||
168 | |||
169 | for (i = 0; i < ETH_ALEN; i++) | ||
170 | info->v1i.mac_addr[i] = mac_addr[i]; | ||
171 | |||
172 | info->v1i.head = 0; | ||
173 | info->v1i.tail = 1; | ||
174 | |||
175 | info->v1i.info_size = sizeof(union ep_buffer_info); | ||
176 | info->v1i.buffer_size = epbh->size - info->v1i.info_size; | ||
177 | |||
178 | info->v1i.frame_max = FJES_MTU_TO_FRAME_SIZE(mtu); | ||
179 | info->v1i.count_max = | ||
180 | EP_RING_NUM(info->v1i.buffer_size, info->v1i.frame_max); | ||
181 | |||
182 | for (i = 0; i < EP_BUFFER_SUPPORT_VLAN_MAX; i++) | ||
183 | info->v1i.vlan_id[i] = vlan_id[i]; | ||
184 | } | ||
185 | |||
186 | void | ||
187 | fjes_hw_init_command_registers(struct fjes_hw *hw, | ||
188 | struct fjes_device_command_param *param) | ||
189 | { | ||
190 | /* Request Buffer length */ | ||
191 | wr32(XSCT_REQBL, (__le32)(param->req_len)); | ||
192 | /* Response Buffer Length */ | ||
193 | wr32(XSCT_RESPBL, (__le32)(param->res_len)); | ||
194 | |||
195 | /* Request Buffer Address */ | ||
196 | wr32(XSCT_REQBAL, | ||
197 | (__le32)(param->req_start & GENMASK_ULL(31, 0))); | ||
198 | wr32(XSCT_REQBAH, | ||
199 | (__le32)((param->req_start & GENMASK_ULL(63, 32)) >> 32)); | ||
200 | |||
201 | /* Response Buffer Address */ | ||
202 | wr32(XSCT_RESPBAL, | ||
203 | (__le32)(param->res_start & GENMASK_ULL(31, 0))); | ||
204 | wr32(XSCT_RESPBAH, | ||
205 | (__le32)((param->res_start & GENMASK_ULL(63, 32)) >> 32)); | ||
206 | |||
207 | /* Share status address */ | ||
208 | wr32(XSCT_SHSTSAL, | ||
209 | (__le32)(param->share_start & GENMASK_ULL(31, 0))); | ||
210 | wr32(XSCT_SHSTSAH, | ||
211 | (__le32)((param->share_start & GENMASK_ULL(63, 32)) >> 32)); | ||
212 | } | ||
213 | |||
214 | static int fjes_hw_setup(struct fjes_hw *hw) | ||
215 | { | ||
216 | u8 mac[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
217 | struct fjes_device_command_param param; | ||
218 | struct ep_share_mem_info *buf_pair; | ||
219 | size_t mem_size; | ||
220 | int result; | ||
221 | int epidx; | ||
222 | void *buf; | ||
223 | |||
224 | hw->hw_info.max_epid = &hw->max_epid; | ||
225 | hw->hw_info.my_epid = &hw->my_epid; | ||
226 | |||
227 | buf = kcalloc(hw->max_epid, sizeof(struct ep_share_mem_info), | ||
228 | GFP_KERNEL); | ||
229 | if (!buf) | ||
230 | return -ENOMEM; | ||
231 | |||
232 | hw->ep_shm_info = (struct ep_share_mem_info *)buf; | ||
233 | |||
234 | mem_size = FJES_DEV_REQ_BUF_SIZE(hw->max_epid); | ||
235 | hw->hw_info.req_buf = kzalloc(mem_size, GFP_KERNEL); | ||
236 | if (!(hw->hw_info.req_buf)) | ||
237 | return -ENOMEM; | ||
238 | |||
239 | hw->hw_info.req_buf_size = mem_size; | ||
240 | |||
241 | mem_size = FJES_DEV_RES_BUF_SIZE(hw->max_epid); | ||
242 | hw->hw_info.res_buf = kzalloc(mem_size, GFP_KERNEL); | ||
243 | if (!(hw->hw_info.res_buf)) | ||
244 | return -ENOMEM; | ||
245 | |||
246 | hw->hw_info.res_buf_size = mem_size; | ||
247 | |||
248 | result = fjes_hw_alloc_shared_status_region(hw); | ||
249 | if (result) | ||
250 | return result; | ||
251 | |||
252 | hw->hw_info.buffer_share_bit = 0; | ||
253 | hw->hw_info.buffer_unshare_reserve_bit = 0; | ||
254 | |||
255 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
256 | if (epidx != hw->my_epid) { | ||
257 | buf_pair = &hw->ep_shm_info[epidx]; | ||
258 | |||
259 | result = fjes_hw_alloc_epbuf(&buf_pair->tx); | ||
260 | if (result) | ||
261 | return result; | ||
262 | |||
263 | result = fjes_hw_alloc_epbuf(&buf_pair->rx); | ||
264 | if (result) | ||
265 | return result; | ||
266 | |||
267 | fjes_hw_setup_epbuf(&buf_pair->tx, mac, | ||
268 | fjes_support_mtu[0]); | ||
269 | fjes_hw_setup_epbuf(&buf_pair->rx, mac, | ||
270 | fjes_support_mtu[0]); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | memset(¶m, 0, sizeof(param)); | ||
275 | |||
276 | param.req_len = hw->hw_info.req_buf_size; | ||
277 | param.req_start = __pa(hw->hw_info.req_buf); | ||
278 | param.res_len = hw->hw_info.res_buf_size; | ||
279 | param.res_start = __pa(hw->hw_info.res_buf); | ||
280 | |||
281 | param.share_start = __pa(hw->hw_info.share->ep_status); | ||
282 | |||
283 | fjes_hw_init_command_registers(hw, ¶m); | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static void fjes_hw_cleanup(struct fjes_hw *hw) | ||
289 | { | ||
290 | int epidx; | ||
291 | |||
292 | if (!hw->ep_shm_info) | ||
293 | return; | ||
294 | |||
295 | fjes_hw_free_shared_status_region(hw); | ||
296 | |||
297 | kfree(hw->hw_info.req_buf); | ||
298 | hw->hw_info.req_buf = NULL; | ||
299 | |||
300 | kfree(hw->hw_info.res_buf); | ||
301 | hw->hw_info.res_buf = NULL; | ||
302 | |||
303 | for (epidx = 0; epidx < hw->max_epid ; epidx++) { | ||
304 | if (epidx == hw->my_epid) | ||
305 | continue; | ||
306 | fjes_hw_free_epbuf(&hw->ep_shm_info[epidx].tx); | ||
307 | fjes_hw_free_epbuf(&hw->ep_shm_info[epidx].rx); | ||
308 | } | ||
309 | |||
310 | kfree(hw->ep_shm_info); | ||
311 | hw->ep_shm_info = NULL; | ||
312 | } | ||
313 | |||
314 | int fjes_hw_init(struct fjes_hw *hw) | ||
315 | { | ||
316 | int ret; | ||
317 | |||
318 | hw->base = fjes_hw_iomap(hw); | ||
319 | if (!hw->base) | ||
320 | return -EIO; | ||
321 | |||
322 | ret = fjes_hw_reset(hw); | ||
323 | if (ret) | ||
324 | return ret; | ||
325 | |||
326 | fjes_hw_set_irqmask(hw, REG_ICTL_MASK_ALL, true); | ||
327 | |||
328 | INIT_WORK(&hw->update_zone_task, fjes_hw_update_zone_task); | ||
329 | INIT_WORK(&hw->epstop_task, fjes_hw_epstop_task); | ||
330 | |||
331 | mutex_init(&hw->hw_info.lock); | ||
332 | |||
333 | hw->max_epid = fjes_hw_get_max_epid(hw); | ||
334 | hw->my_epid = fjes_hw_get_my_epid(hw); | ||
335 | |||
336 | if ((hw->max_epid == 0) || (hw->my_epid >= hw->max_epid)) | ||
337 | return -ENXIO; | ||
338 | |||
339 | ret = fjes_hw_setup(hw); | ||
340 | |||
341 | return ret; | ||
342 | } | ||
343 | |||
344 | void fjes_hw_exit(struct fjes_hw *hw) | ||
345 | { | ||
346 | int ret; | ||
347 | |||
348 | if (hw->base) { | ||
349 | ret = fjes_hw_reset(hw); | ||
350 | if (ret) | ||
351 | pr_err("%s: reset error", __func__); | ||
352 | |||
353 | fjes_hw_iounmap(hw); | ||
354 | hw->base = NULL; | ||
355 | } | ||
356 | |||
357 | fjes_hw_cleanup(hw); | ||
358 | |||
359 | cancel_work_sync(&hw->update_zone_task); | ||
360 | cancel_work_sync(&hw->epstop_task); | ||
361 | } | ||
362 | |||
363 | static enum fjes_dev_command_response_e | ||
364 | fjes_hw_issue_request_command(struct fjes_hw *hw, | ||
365 | enum fjes_dev_command_request_type type) | ||
366 | { | ||
367 | enum fjes_dev_command_response_e ret = FJES_CMD_STATUS_UNKNOWN; | ||
368 | union REG_CR cr; | ||
369 | union REG_CS cs; | ||
370 | int timeout; | ||
371 | |||
372 | cr.reg = 0; | ||
373 | cr.bits.req_start = 1; | ||
374 | cr.bits.req_code = type; | ||
375 | wr32(XSCT_CR, cr.reg); | ||
376 | cr.reg = rd32(XSCT_CR); | ||
377 | |||
378 | if (cr.bits.error == 0) { | ||
379 | timeout = FJES_COMMAND_REQ_TIMEOUT * 1000; | ||
380 | cs.reg = rd32(XSCT_CS); | ||
381 | |||
382 | while ((cs.bits.complete != 1) && timeout > 0) { | ||
383 | msleep(1000); | ||
384 | cs.reg = rd32(XSCT_CS); | ||
385 | timeout -= 1000; | ||
386 | } | ||
387 | |||
388 | if (cs.bits.complete == 1) | ||
389 | ret = FJES_CMD_STATUS_NORMAL; | ||
390 | else if (timeout <= 0) | ||
391 | ret = FJES_CMD_STATUS_TIMEOUT; | ||
392 | |||
393 | } else { | ||
394 | switch (cr.bits.err_info) { | ||
395 | case FJES_CMD_REQ_ERR_INFO_PARAM: | ||
396 | ret = FJES_CMD_STATUS_ERROR_PARAM; | ||
397 | break; | ||
398 | case FJES_CMD_REQ_ERR_INFO_STATUS: | ||
399 | ret = FJES_CMD_STATUS_ERROR_STATUS; | ||
400 | break; | ||
401 | default: | ||
402 | ret = FJES_CMD_STATUS_UNKNOWN; | ||
403 | break; | ||
404 | } | ||
405 | } | ||
406 | |||
407 | return ret; | ||
408 | } | ||
409 | |||
410 | int fjes_hw_request_info(struct fjes_hw *hw) | ||
411 | { | ||
412 | union fjes_device_command_req *req_buf = hw->hw_info.req_buf; | ||
413 | union fjes_device_command_res *res_buf = hw->hw_info.res_buf; | ||
414 | enum fjes_dev_command_response_e ret; | ||
415 | int result; | ||
416 | |||
417 | memset(req_buf, 0, hw->hw_info.req_buf_size); | ||
418 | memset(res_buf, 0, hw->hw_info.res_buf_size); | ||
419 | |||
420 | req_buf->info.length = FJES_DEV_COMMAND_INFO_REQ_LEN; | ||
421 | |||
422 | res_buf->info.length = 0; | ||
423 | res_buf->info.code = 0; | ||
424 | |||
425 | ret = fjes_hw_issue_request_command(hw, FJES_CMD_REQ_INFO); | ||
426 | |||
427 | result = 0; | ||
428 | |||
429 | if (FJES_DEV_COMMAND_INFO_RES_LEN((*hw->hw_info.max_epid)) != | ||
430 | res_buf->info.length) { | ||
431 | result = -ENOMSG; | ||
432 | } else if (ret == FJES_CMD_STATUS_NORMAL) { | ||
433 | switch (res_buf->info.code) { | ||
434 | case FJES_CMD_REQ_RES_CODE_NORMAL: | ||
435 | result = 0; | ||
436 | break; | ||
437 | default: | ||
438 | result = -EPERM; | ||
439 | break; | ||
440 | } | ||
441 | } else { | ||
442 | switch (ret) { | ||
443 | case FJES_CMD_STATUS_UNKNOWN: | ||
444 | result = -EPERM; | ||
445 | break; | ||
446 | case FJES_CMD_STATUS_TIMEOUT: | ||
447 | result = -EBUSY; | ||
448 | break; | ||
449 | case FJES_CMD_STATUS_ERROR_PARAM: | ||
450 | result = -EPERM; | ||
451 | break; | ||
452 | case FJES_CMD_STATUS_ERROR_STATUS: | ||
453 | result = -EPERM; | ||
454 | break; | ||
455 | default: | ||
456 | result = -EPERM; | ||
457 | break; | ||
458 | } | ||
459 | } | ||
460 | |||
461 | return result; | ||
462 | } | ||
463 | |||
464 | int fjes_hw_register_buff_addr(struct fjes_hw *hw, int dest_epid, | ||
465 | struct ep_share_mem_info *buf_pair) | ||
466 | { | ||
467 | union fjes_device_command_req *req_buf = hw->hw_info.req_buf; | ||
468 | union fjes_device_command_res *res_buf = hw->hw_info.res_buf; | ||
469 | enum fjes_dev_command_response_e ret; | ||
470 | int page_count; | ||
471 | int timeout; | ||
472 | int i, idx; | ||
473 | void *addr; | ||
474 | int result; | ||
475 | |||
476 | if (test_bit(dest_epid, &hw->hw_info.buffer_share_bit)) | ||
477 | return 0; | ||
478 | |||
479 | memset(req_buf, 0, hw->hw_info.req_buf_size); | ||
480 | memset(res_buf, 0, hw->hw_info.res_buf_size); | ||
481 | |||
482 | req_buf->share_buffer.length = FJES_DEV_COMMAND_SHARE_BUFFER_REQ_LEN( | ||
483 | buf_pair->tx.size, | ||
484 | buf_pair->rx.size); | ||
485 | req_buf->share_buffer.epid = dest_epid; | ||
486 | |||
487 | idx = 0; | ||
488 | req_buf->share_buffer.buffer[idx++] = buf_pair->tx.size; | ||
489 | page_count = buf_pair->tx.size / EP_BUFFER_INFO_SIZE; | ||
490 | for (i = 0; i < page_count; i++) { | ||
491 | addr = ((u8 *)(buf_pair->tx.buffer)) + | ||
492 | (i * EP_BUFFER_INFO_SIZE); | ||
493 | req_buf->share_buffer.buffer[idx++] = | ||
494 | (__le64)(page_to_phys(vmalloc_to_page(addr)) + | ||
495 | offset_in_page(addr)); | ||
496 | } | ||
497 | |||
498 | req_buf->share_buffer.buffer[idx++] = buf_pair->rx.size; | ||
499 | page_count = buf_pair->rx.size / EP_BUFFER_INFO_SIZE; | ||
500 | for (i = 0; i < page_count; i++) { | ||
501 | addr = ((u8 *)(buf_pair->rx.buffer)) + | ||
502 | (i * EP_BUFFER_INFO_SIZE); | ||
503 | req_buf->share_buffer.buffer[idx++] = | ||
504 | (__le64)(page_to_phys(vmalloc_to_page(addr)) + | ||
505 | offset_in_page(addr)); | ||
506 | } | ||
507 | |||
508 | res_buf->share_buffer.length = 0; | ||
509 | res_buf->share_buffer.code = 0; | ||
510 | |||
511 | ret = fjes_hw_issue_request_command(hw, FJES_CMD_REQ_SHARE_BUFFER); | ||
512 | |||
513 | timeout = FJES_COMMAND_REQ_BUFF_TIMEOUT * 1000; | ||
514 | while ((ret == FJES_CMD_STATUS_NORMAL) && | ||
515 | (res_buf->share_buffer.length == | ||
516 | FJES_DEV_COMMAND_SHARE_BUFFER_RES_LEN) && | ||
517 | (res_buf->share_buffer.code == FJES_CMD_REQ_RES_CODE_BUSY) && | ||
518 | (timeout > 0)) { | ||
519 | msleep(200 + hw->my_epid * 20); | ||
520 | timeout -= (200 + hw->my_epid * 20); | ||
521 | |||
522 | res_buf->share_buffer.length = 0; | ||
523 | res_buf->share_buffer.code = 0; | ||
524 | |||
525 | ret = fjes_hw_issue_request_command( | ||
526 | hw, FJES_CMD_REQ_SHARE_BUFFER); | ||
527 | } | ||
528 | |||
529 | result = 0; | ||
530 | |||
531 | if (res_buf->share_buffer.length != | ||
532 | FJES_DEV_COMMAND_SHARE_BUFFER_RES_LEN) | ||
533 | result = -ENOMSG; | ||
534 | else if (ret == FJES_CMD_STATUS_NORMAL) { | ||
535 | switch (res_buf->share_buffer.code) { | ||
536 | case FJES_CMD_REQ_RES_CODE_NORMAL: | ||
537 | result = 0; | ||
538 | set_bit(dest_epid, &hw->hw_info.buffer_share_bit); | ||
539 | break; | ||
540 | case FJES_CMD_REQ_RES_CODE_BUSY: | ||
541 | result = -EBUSY; | ||
542 | break; | ||
543 | default: | ||
544 | result = -EPERM; | ||
545 | break; | ||
546 | } | ||
547 | } else { | ||
548 | switch (ret) { | ||
549 | case FJES_CMD_STATUS_UNKNOWN: | ||
550 | result = -EPERM; | ||
551 | break; | ||
552 | case FJES_CMD_STATUS_TIMEOUT: | ||
553 | result = -EBUSY; | ||
554 | break; | ||
555 | case FJES_CMD_STATUS_ERROR_PARAM: | ||
556 | case FJES_CMD_STATUS_ERROR_STATUS: | ||
557 | default: | ||
558 | result = -EPERM; | ||
559 | break; | ||
560 | } | ||
561 | } | ||
562 | |||
563 | return result; | ||
564 | } | ||
565 | |||
566 | int fjes_hw_unregister_buff_addr(struct fjes_hw *hw, int dest_epid) | ||
567 | { | ||
568 | union fjes_device_command_req *req_buf = hw->hw_info.req_buf; | ||
569 | union fjes_device_command_res *res_buf = hw->hw_info.res_buf; | ||
570 | struct fjes_device_shared_info *share = hw->hw_info.share; | ||
571 | enum fjes_dev_command_response_e ret; | ||
572 | int timeout; | ||
573 | int result; | ||
574 | |||
575 | if (!hw->base) | ||
576 | return -EPERM; | ||
577 | |||
578 | if (!req_buf || !res_buf || !share) | ||
579 | return -EPERM; | ||
580 | |||
581 | if (!test_bit(dest_epid, &hw->hw_info.buffer_share_bit)) | ||
582 | return 0; | ||
583 | |||
584 | memset(req_buf, 0, hw->hw_info.req_buf_size); | ||
585 | memset(res_buf, 0, hw->hw_info.res_buf_size); | ||
586 | |||
587 | req_buf->unshare_buffer.length = | ||
588 | FJES_DEV_COMMAND_UNSHARE_BUFFER_REQ_LEN; | ||
589 | req_buf->unshare_buffer.epid = dest_epid; | ||
590 | |||
591 | res_buf->unshare_buffer.length = 0; | ||
592 | res_buf->unshare_buffer.code = 0; | ||
593 | |||
594 | ret = fjes_hw_issue_request_command(hw, FJES_CMD_REQ_UNSHARE_BUFFER); | ||
595 | |||
596 | timeout = FJES_COMMAND_REQ_BUFF_TIMEOUT * 1000; | ||
597 | while ((ret == FJES_CMD_STATUS_NORMAL) && | ||
598 | (res_buf->unshare_buffer.length == | ||
599 | FJES_DEV_COMMAND_UNSHARE_BUFFER_RES_LEN) && | ||
600 | (res_buf->unshare_buffer.code == | ||
601 | FJES_CMD_REQ_RES_CODE_BUSY) && | ||
602 | (timeout > 0)) { | ||
603 | msleep(200 + hw->my_epid * 20); | ||
604 | timeout -= (200 + hw->my_epid * 20); | ||
605 | |||
606 | res_buf->unshare_buffer.length = 0; | ||
607 | res_buf->unshare_buffer.code = 0; | ||
608 | |||
609 | ret = | ||
610 | fjes_hw_issue_request_command(hw, FJES_CMD_REQ_UNSHARE_BUFFER); | ||
611 | } | ||
612 | |||
613 | result = 0; | ||
614 | |||
615 | if (res_buf->unshare_buffer.length != | ||
616 | FJES_DEV_COMMAND_UNSHARE_BUFFER_RES_LEN) { | ||
617 | result = -ENOMSG; | ||
618 | } else if (ret == FJES_CMD_STATUS_NORMAL) { | ||
619 | switch (res_buf->unshare_buffer.code) { | ||
620 | case FJES_CMD_REQ_RES_CODE_NORMAL: | ||
621 | result = 0; | ||
622 | clear_bit(dest_epid, &hw->hw_info.buffer_share_bit); | ||
623 | break; | ||
624 | case FJES_CMD_REQ_RES_CODE_BUSY: | ||
625 | result = -EBUSY; | ||
626 | break; | ||
627 | default: | ||
628 | result = -EPERM; | ||
629 | break; | ||
630 | } | ||
631 | } else { | ||
632 | switch (ret) { | ||
633 | case FJES_CMD_STATUS_UNKNOWN: | ||
634 | result = -EPERM; | ||
635 | break; | ||
636 | case FJES_CMD_STATUS_TIMEOUT: | ||
637 | result = -EBUSY; | ||
638 | break; | ||
639 | case FJES_CMD_STATUS_ERROR_PARAM: | ||
640 | case FJES_CMD_STATUS_ERROR_STATUS: | ||
641 | default: | ||
642 | result = -EPERM; | ||
643 | break; | ||
644 | } | ||
645 | } | ||
646 | |||
647 | return result; | ||
648 | } | ||
649 | |||
650 | int fjes_hw_raise_interrupt(struct fjes_hw *hw, int dest_epid, | ||
651 | enum REG_ICTL_MASK mask) | ||
652 | { | ||
653 | u32 ig = mask | dest_epid; | ||
654 | |||
655 | wr32(XSCT_IG, cpu_to_le32(ig)); | ||
656 | |||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | u32 fjes_hw_capture_interrupt_status(struct fjes_hw *hw) | ||
661 | { | ||
662 | u32 cur_is; | ||
663 | |||
664 | cur_is = rd32(XSCT_IS); | ||
665 | |||
666 | return cur_is; | ||
667 | } | ||
668 | |||
669 | void fjes_hw_set_irqmask(struct fjes_hw *hw, | ||
670 | enum REG_ICTL_MASK intr_mask, bool mask) | ||
671 | { | ||
672 | if (mask) | ||
673 | wr32(XSCT_IMS, intr_mask); | ||
674 | else | ||
675 | wr32(XSCT_IMC, intr_mask); | ||
676 | } | ||
677 | |||
678 | bool fjes_hw_epid_is_same_zone(struct fjes_hw *hw, int epid) | ||
679 | { | ||
680 | if (epid >= hw->max_epid) | ||
681 | return false; | ||
682 | |||
683 | if ((hw->ep_shm_info[epid].es_status != | ||
684 | FJES_ZONING_STATUS_ENABLE) || | ||
685 | (hw->ep_shm_info[hw->my_epid].zone == | ||
686 | FJES_ZONING_ZONE_TYPE_NONE)) | ||
687 | return false; | ||
688 | else | ||
689 | return (hw->ep_shm_info[epid].zone == | ||
690 | hw->ep_shm_info[hw->my_epid].zone); | ||
691 | } | ||
692 | |||
693 | int fjes_hw_epid_is_shared(struct fjes_device_shared_info *share, | ||
694 | int dest_epid) | ||
695 | { | ||
696 | int value = false; | ||
697 | |||
698 | if (dest_epid < share->epnum) | ||
699 | value = share->ep_status[dest_epid]; | ||
700 | |||
701 | return value; | ||
702 | } | ||
703 | |||
704 | static bool fjes_hw_epid_is_stop_requested(struct fjes_hw *hw, int src_epid) | ||
705 | { | ||
706 | return test_bit(src_epid, &hw->txrx_stop_req_bit); | ||
707 | } | ||
708 | |||
709 | static bool fjes_hw_epid_is_stop_process_done(struct fjes_hw *hw, int src_epid) | ||
710 | { | ||
711 | return (hw->ep_shm_info[src_epid].tx.info->v1i.rx_status & | ||
712 | FJES_RX_STOP_REQ_DONE); | ||
713 | } | ||
714 | |||
715 | enum ep_partner_status | ||
716 | fjes_hw_get_partner_ep_status(struct fjes_hw *hw, int epid) | ||
717 | { | ||
718 | enum ep_partner_status status; | ||
719 | |||
720 | if (fjes_hw_epid_is_shared(hw->hw_info.share, epid)) { | ||
721 | if (fjes_hw_epid_is_stop_requested(hw, epid)) { | ||
722 | status = EP_PARTNER_WAITING; | ||
723 | } else { | ||
724 | if (fjes_hw_epid_is_stop_process_done(hw, epid)) | ||
725 | status = EP_PARTNER_COMPLETE; | ||
726 | else | ||
727 | status = EP_PARTNER_SHARED; | ||
728 | } | ||
729 | } else { | ||
730 | status = EP_PARTNER_UNSHARE; | ||
731 | } | ||
732 | |||
733 | return status; | ||
734 | } | ||
735 | |||
736 | void fjes_hw_raise_epstop(struct fjes_hw *hw) | ||
737 | { | ||
738 | enum ep_partner_status status; | ||
739 | int epidx; | ||
740 | |||
741 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
742 | if (epidx == hw->my_epid) | ||
743 | continue; | ||
744 | |||
745 | status = fjes_hw_get_partner_ep_status(hw, epidx); | ||
746 | switch (status) { | ||
747 | case EP_PARTNER_SHARED: | ||
748 | fjes_hw_raise_interrupt(hw, epidx, | ||
749 | REG_ICTL_MASK_TXRX_STOP_REQ); | ||
750 | break; | ||
751 | default: | ||
752 | break; | ||
753 | } | ||
754 | |||
755 | set_bit(epidx, &hw->hw_info.buffer_unshare_reserve_bit); | ||
756 | set_bit(epidx, &hw->txrx_stop_req_bit); | ||
757 | |||
758 | hw->ep_shm_info[epidx].tx.info->v1i.rx_status |= | ||
759 | FJES_RX_STOP_REQ_REQUEST; | ||
760 | } | ||
761 | } | ||
762 | |||
763 | int fjes_hw_wait_epstop(struct fjes_hw *hw) | ||
764 | { | ||
765 | enum ep_partner_status status; | ||
766 | union ep_buffer_info *info; | ||
767 | int wait_time = 0; | ||
768 | int epidx; | ||
769 | |||
770 | while (hw->hw_info.buffer_unshare_reserve_bit && | ||
771 | (wait_time < FJES_COMMAND_EPSTOP_WAIT_TIMEOUT * 1000)) { | ||
772 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
773 | if (epidx == hw->my_epid) | ||
774 | continue; | ||
775 | status = fjes_hw_epid_is_shared(hw->hw_info.share, | ||
776 | epidx); | ||
777 | info = hw->ep_shm_info[epidx].rx.info; | ||
778 | if ((!status || | ||
779 | (info->v1i.rx_status & | ||
780 | FJES_RX_STOP_REQ_DONE)) && | ||
781 | test_bit(epidx, | ||
782 | &hw->hw_info.buffer_unshare_reserve_bit)) { | ||
783 | clear_bit(epidx, | ||
784 | &hw->hw_info.buffer_unshare_reserve_bit); | ||
785 | } | ||
786 | } | ||
787 | |||
788 | msleep(100); | ||
789 | wait_time += 100; | ||
790 | } | ||
791 | |||
792 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
793 | if (epidx == hw->my_epid) | ||
794 | continue; | ||
795 | if (test_bit(epidx, &hw->hw_info.buffer_unshare_reserve_bit)) | ||
796 | clear_bit(epidx, | ||
797 | &hw->hw_info.buffer_unshare_reserve_bit); | ||
798 | } | ||
799 | |||
800 | return (wait_time < FJES_COMMAND_EPSTOP_WAIT_TIMEOUT * 1000) | ||
801 | ? 0 : -EBUSY; | ||
802 | } | ||
803 | |||
804 | bool fjes_hw_check_epbuf_version(struct epbuf_handler *epbh, u32 version) | ||
805 | { | ||
806 | union ep_buffer_info *info = epbh->info; | ||
807 | |||
808 | return (info->common.version == version); | ||
809 | } | ||
810 | |||
811 | bool fjes_hw_check_mtu(struct epbuf_handler *epbh, u32 mtu) | ||
812 | { | ||
813 | union ep_buffer_info *info = epbh->info; | ||
814 | |||
815 | return (info->v1i.frame_max == FJES_MTU_TO_FRAME_SIZE(mtu)); | ||
816 | } | ||
817 | |||
818 | bool fjes_hw_check_vlan_id(struct epbuf_handler *epbh, u16 vlan_id) | ||
819 | { | ||
820 | union ep_buffer_info *info = epbh->info; | ||
821 | bool ret = false; | ||
822 | int i; | ||
823 | |||
824 | if (vlan_id == 0) { | ||
825 | ret = true; | ||
826 | } else { | ||
827 | for (i = 0; i < EP_BUFFER_SUPPORT_VLAN_MAX; i++) { | ||
828 | if (vlan_id == info->v1i.vlan_id[i]) { | ||
829 | ret = true; | ||
830 | break; | ||
831 | } | ||
832 | } | ||
833 | } | ||
834 | return ret; | ||
835 | } | ||
836 | |||
837 | bool fjes_hw_set_vlan_id(struct epbuf_handler *epbh, u16 vlan_id) | ||
838 | { | ||
839 | union ep_buffer_info *info = epbh->info; | ||
840 | int i; | ||
841 | |||
842 | for (i = 0; i < EP_BUFFER_SUPPORT_VLAN_MAX; i++) { | ||
843 | if (info->v1i.vlan_id[i] == 0) { | ||
844 | info->v1i.vlan_id[i] = vlan_id; | ||
845 | return true; | ||
846 | } | ||
847 | } | ||
848 | return false; | ||
849 | } | ||
850 | |||
851 | void fjes_hw_del_vlan_id(struct epbuf_handler *epbh, u16 vlan_id) | ||
852 | { | ||
853 | union ep_buffer_info *info = epbh->info; | ||
854 | int i; | ||
855 | |||
856 | if (0 != vlan_id) { | ||
857 | for (i = 0; i < EP_BUFFER_SUPPORT_VLAN_MAX; i++) { | ||
858 | if (vlan_id == info->v1i.vlan_id[i]) | ||
859 | info->v1i.vlan_id[i] = 0; | ||
860 | } | ||
861 | } | ||
862 | } | ||
863 | |||
864 | bool fjes_hw_epbuf_rx_is_empty(struct epbuf_handler *epbh) | ||
865 | { | ||
866 | union ep_buffer_info *info = epbh->info; | ||
867 | |||
868 | if (info->v1i.count_max == 0) | ||
869 | return true; | ||
870 | |||
871 | return EP_RING_EMPTY(info->v1i.head, info->v1i.tail, | ||
872 | info->v1i.count_max); | ||
873 | } | ||
874 | |||
875 | void *fjes_hw_epbuf_rx_curpkt_get_addr(struct epbuf_handler *epbh, | ||
876 | size_t *psize) | ||
877 | { | ||
878 | union ep_buffer_info *info = epbh->info; | ||
879 | struct esmem_frame *ring_frame; | ||
880 | void *frame; | ||
881 | |||
882 | ring_frame = (struct esmem_frame *)&(epbh->ring[EP_RING_INDEX | ||
883 | (info->v1i.head, | ||
884 | info->v1i.count_max) * | ||
885 | info->v1i.frame_max]); | ||
886 | |||
887 | *psize = (size_t)ring_frame->frame_size; | ||
888 | |||
889 | frame = ring_frame->frame_data; | ||
890 | |||
891 | return frame; | ||
892 | } | ||
893 | |||
894 | void fjes_hw_epbuf_rx_curpkt_drop(struct epbuf_handler *epbh) | ||
895 | { | ||
896 | union ep_buffer_info *info = epbh->info; | ||
897 | |||
898 | if (fjes_hw_epbuf_rx_is_empty(epbh)) | ||
899 | return; | ||
900 | |||
901 | EP_RING_INDEX_INC(epbh->info->v1i.head, info->v1i.count_max); | ||
902 | } | ||
903 | |||
904 | int fjes_hw_epbuf_tx_pkt_send(struct epbuf_handler *epbh, | ||
905 | void *frame, size_t size) | ||
906 | { | ||
907 | union ep_buffer_info *info = epbh->info; | ||
908 | struct esmem_frame *ring_frame; | ||
909 | |||
910 | if (EP_RING_FULL(info->v1i.head, info->v1i.tail, info->v1i.count_max)) | ||
911 | return -ENOBUFS; | ||
912 | |||
913 | ring_frame = (struct esmem_frame *)&(epbh->ring[EP_RING_INDEX | ||
914 | (info->v1i.tail - 1, | ||
915 | info->v1i.count_max) * | ||
916 | info->v1i.frame_max]); | ||
917 | |||
918 | ring_frame->frame_size = size; | ||
919 | memcpy((void *)(ring_frame->frame_data), (void *)frame, size); | ||
920 | |||
921 | EP_RING_INDEX_INC(epbh->info->v1i.tail, info->v1i.count_max); | ||
922 | |||
923 | return 0; | ||
924 | } | ||
925 | |||
926 | static void fjes_hw_update_zone_task(struct work_struct *work) | ||
927 | { | ||
928 | struct fjes_hw *hw = container_of(work, | ||
929 | struct fjes_hw, update_zone_task); | ||
930 | |||
931 | struct my_s {u8 es_status; u8 zone; } *info; | ||
932 | union fjes_device_command_res *res_buf; | ||
933 | enum ep_partner_status pstatus; | ||
934 | |||
935 | struct fjes_adapter *adapter; | ||
936 | struct net_device *netdev; | ||
937 | |||
938 | ulong unshare_bit = 0; | ||
939 | ulong share_bit = 0; | ||
940 | ulong irq_bit = 0; | ||
941 | |||
942 | int epidx; | ||
943 | int ret; | ||
944 | |||
945 | adapter = (struct fjes_adapter *)hw->back; | ||
946 | netdev = adapter->netdev; | ||
947 | res_buf = hw->hw_info.res_buf; | ||
948 | info = (struct my_s *)&res_buf->info.info; | ||
949 | |||
950 | mutex_lock(&hw->hw_info.lock); | ||
951 | |||
952 | ret = fjes_hw_request_info(hw); | ||
953 | switch (ret) { | ||
954 | case -ENOMSG: | ||
955 | case -EBUSY: | ||
956 | default: | ||
957 | if (!work_pending(&adapter->force_close_task)) { | ||
958 | adapter->force_reset = true; | ||
959 | schedule_work(&adapter->force_close_task); | ||
960 | } | ||
961 | break; | ||
962 | |||
963 | case 0: | ||
964 | |||
965 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
966 | if (epidx == hw->my_epid) { | ||
967 | hw->ep_shm_info[epidx].es_status = | ||
968 | info[epidx].es_status; | ||
969 | hw->ep_shm_info[epidx].zone = | ||
970 | info[epidx].zone; | ||
971 | continue; | ||
972 | } | ||
973 | |||
974 | pstatus = fjes_hw_get_partner_ep_status(hw, epidx); | ||
975 | switch (pstatus) { | ||
976 | case EP_PARTNER_UNSHARE: | ||
977 | default: | ||
978 | if ((info[epidx].zone != | ||
979 | FJES_ZONING_ZONE_TYPE_NONE) && | ||
980 | (info[epidx].es_status == | ||
981 | FJES_ZONING_STATUS_ENABLE) && | ||
982 | (info[epidx].zone == | ||
983 | info[hw->my_epid].zone)) | ||
984 | set_bit(epidx, &share_bit); | ||
985 | else | ||
986 | set_bit(epidx, &unshare_bit); | ||
987 | break; | ||
988 | |||
989 | case EP_PARTNER_COMPLETE: | ||
990 | case EP_PARTNER_WAITING: | ||
991 | if ((info[epidx].zone == | ||
992 | FJES_ZONING_ZONE_TYPE_NONE) || | ||
993 | (info[epidx].es_status != | ||
994 | FJES_ZONING_STATUS_ENABLE) || | ||
995 | (info[epidx].zone != | ||
996 | info[hw->my_epid].zone)) { | ||
997 | set_bit(epidx, | ||
998 | &adapter->unshare_watch_bitmask); | ||
999 | set_bit(epidx, | ||
1000 | &hw->hw_info.buffer_unshare_reserve_bit); | ||
1001 | } | ||
1002 | break; | ||
1003 | |||
1004 | case EP_PARTNER_SHARED: | ||
1005 | if ((info[epidx].zone == | ||
1006 | FJES_ZONING_ZONE_TYPE_NONE) || | ||
1007 | (info[epidx].es_status != | ||
1008 | FJES_ZONING_STATUS_ENABLE) || | ||
1009 | (info[epidx].zone != | ||
1010 | info[hw->my_epid].zone)) | ||
1011 | set_bit(epidx, &irq_bit); | ||
1012 | break; | ||
1013 | } | ||
1014 | } | ||
1015 | |||
1016 | hw->ep_shm_info[epidx].es_status = info[epidx].es_status; | ||
1017 | hw->ep_shm_info[epidx].zone = info[epidx].zone; | ||
1018 | |||
1019 | break; | ||
1020 | } | ||
1021 | |||
1022 | mutex_unlock(&hw->hw_info.lock); | ||
1023 | |||
1024 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
1025 | if (epidx == hw->my_epid) | ||
1026 | continue; | ||
1027 | |||
1028 | if (test_bit(epidx, &share_bit)) { | ||
1029 | fjes_hw_setup_epbuf(&hw->ep_shm_info[epidx].tx, | ||
1030 | netdev->dev_addr, netdev->mtu); | ||
1031 | |||
1032 | mutex_lock(&hw->hw_info.lock); | ||
1033 | |||
1034 | ret = fjes_hw_register_buff_addr( | ||
1035 | hw, epidx, &hw->ep_shm_info[epidx]); | ||
1036 | |||
1037 | switch (ret) { | ||
1038 | case 0: | ||
1039 | break; | ||
1040 | case -ENOMSG: | ||
1041 | case -EBUSY: | ||
1042 | default: | ||
1043 | if (!work_pending(&adapter->force_close_task)) { | ||
1044 | adapter->force_reset = true; | ||
1045 | schedule_work( | ||
1046 | &adapter->force_close_task); | ||
1047 | } | ||
1048 | break; | ||
1049 | } | ||
1050 | mutex_unlock(&hw->hw_info.lock); | ||
1051 | } | ||
1052 | |||
1053 | if (test_bit(epidx, &unshare_bit)) { | ||
1054 | mutex_lock(&hw->hw_info.lock); | ||
1055 | |||
1056 | ret = fjes_hw_unregister_buff_addr(hw, epidx); | ||
1057 | |||
1058 | switch (ret) { | ||
1059 | case 0: | ||
1060 | break; | ||
1061 | case -ENOMSG: | ||
1062 | case -EBUSY: | ||
1063 | default: | ||
1064 | if (!work_pending(&adapter->force_close_task)) { | ||
1065 | adapter->force_reset = true; | ||
1066 | schedule_work( | ||
1067 | &adapter->force_close_task); | ||
1068 | } | ||
1069 | break; | ||
1070 | } | ||
1071 | |||
1072 | mutex_unlock(&hw->hw_info.lock); | ||
1073 | |||
1074 | if (ret == 0) | ||
1075 | fjes_hw_setup_epbuf( | ||
1076 | &hw->ep_shm_info[epidx].tx, | ||
1077 | netdev->dev_addr, netdev->mtu); | ||
1078 | } | ||
1079 | |||
1080 | if (test_bit(epidx, &irq_bit)) { | ||
1081 | fjes_hw_raise_interrupt(hw, epidx, | ||
1082 | REG_ICTL_MASK_TXRX_STOP_REQ); | ||
1083 | |||
1084 | set_bit(epidx, &hw->txrx_stop_req_bit); | ||
1085 | hw->ep_shm_info[epidx].tx. | ||
1086 | info->v1i.rx_status |= | ||
1087 | FJES_RX_STOP_REQ_REQUEST; | ||
1088 | set_bit(epidx, &hw->hw_info.buffer_unshare_reserve_bit); | ||
1089 | } | ||
1090 | } | ||
1091 | |||
1092 | if (irq_bit || adapter->unshare_watch_bitmask) { | ||
1093 | if (!work_pending(&adapter->unshare_watch_task)) | ||
1094 | queue_work(adapter->control_wq, | ||
1095 | &adapter->unshare_watch_task); | ||
1096 | } | ||
1097 | } | ||
1098 | |||
1099 | static void fjes_hw_epstop_task(struct work_struct *work) | ||
1100 | { | ||
1101 | struct fjes_hw *hw = container_of(work, struct fjes_hw, epstop_task); | ||
1102 | struct fjes_adapter *adapter = (struct fjes_adapter *)hw->back; | ||
1103 | |||
1104 | ulong remain_bit; | ||
1105 | int epid_bit; | ||
1106 | |||
1107 | while ((remain_bit = hw->epstop_req_bit)) { | ||
1108 | for (epid_bit = 0; remain_bit; remain_bit >>= 1, epid_bit++) { | ||
1109 | if (remain_bit & 1) { | ||
1110 | hw->ep_shm_info[epid_bit]. | ||
1111 | tx.info->v1i.rx_status |= | ||
1112 | FJES_RX_STOP_REQ_DONE; | ||
1113 | |||
1114 | clear_bit(epid_bit, &hw->epstop_req_bit); | ||
1115 | set_bit(epid_bit, | ||
1116 | &adapter->unshare_watch_bitmask); | ||
1117 | |||
1118 | if (!work_pending(&adapter->unshare_watch_task)) | ||
1119 | queue_work( | ||
1120 | adapter->control_wq, | ||
1121 | &adapter->unshare_watch_task); | ||
1122 | } | ||
1123 | } | ||
1124 | } | ||
1125 | } | ||
diff --git a/drivers/net/fjes/fjes_hw.h b/drivers/net/fjes/fjes_hw.h new file mode 100644 index 000000000000..6d57b89a0ee8 --- /dev/null +++ b/drivers/net/fjes/fjes_hw.h | |||
@@ -0,0 +1,334 @@ | |||
1 | /* | ||
2 | * FUJITSU Extended Socket Network Device driver | ||
3 | * Copyright (c) 2015 FUJITSU LIMITED | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, see <http://www.gnu.org/licenses/>. | ||
16 | * | ||
17 | * The full GNU General Public License is included in this distribution in | ||
18 | * the file called "COPYING". | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef FJES_HW_H_ | ||
23 | #define FJES_HW_H_ | ||
24 | |||
25 | #include <linux/netdevice.h> | ||
26 | #include <linux/if_vlan.h> | ||
27 | #include <linux/vmalloc.h> | ||
28 | |||
29 | #include "fjes_regs.h" | ||
30 | |||
31 | struct fjes_hw; | ||
32 | |||
33 | #define EP_BUFFER_SUPPORT_VLAN_MAX 4 | ||
34 | #define EP_BUFFER_INFO_SIZE 4096 | ||
35 | |||
36 | #define FJES_DEVICE_RESET_TIMEOUT ((17 + 1) * 3) /* sec */ | ||
37 | #define FJES_COMMAND_REQ_TIMEOUT (5 + 1) /* sec */ | ||
38 | #define FJES_COMMAND_REQ_BUFF_TIMEOUT (8 * 3) /* sec */ | ||
39 | #define FJES_COMMAND_EPSTOP_WAIT_TIMEOUT (1) /* sec */ | ||
40 | |||
41 | #define FJES_CMD_REQ_ERR_INFO_PARAM (0x0001) | ||
42 | #define FJES_CMD_REQ_ERR_INFO_STATUS (0x0002) | ||
43 | |||
44 | #define FJES_CMD_REQ_RES_CODE_NORMAL (0) | ||
45 | #define FJES_CMD_REQ_RES_CODE_BUSY (1) | ||
46 | |||
47 | #define FJES_ZONING_STATUS_DISABLE (0x00) | ||
48 | #define FJES_ZONING_STATUS_ENABLE (0x01) | ||
49 | #define FJES_ZONING_STATUS_INVALID (0xFF) | ||
50 | |||
51 | #define FJES_ZONING_ZONE_TYPE_NONE (0xFF) | ||
52 | |||
53 | #define FJES_TX_DELAY_SEND_NONE (0) | ||
54 | #define FJES_TX_DELAY_SEND_PENDING (1) | ||
55 | |||
56 | #define FJES_RX_STOP_REQ_NONE (0x0) | ||
57 | #define FJES_RX_STOP_REQ_DONE (0x1) | ||
58 | #define FJES_RX_STOP_REQ_REQUEST (0x2) | ||
59 | #define FJES_RX_POLL_WORK (0x4) | ||
60 | |||
61 | #define EP_BUFFER_SIZE \ | ||
62 | (((sizeof(union ep_buffer_info) + (128 * (64 * 1024))) \ | ||
63 | / EP_BUFFER_INFO_SIZE) * EP_BUFFER_INFO_SIZE) | ||
64 | |||
65 | #define EP_RING_NUM(buffer_size, frame_size) \ | ||
66 | (u32)((buffer_size) / (frame_size)) | ||
67 | #define EP_RING_INDEX(_num, _max) (((_num) + (_max)) % (_max)) | ||
68 | #define EP_RING_INDEX_INC(_num, _max) \ | ||
69 | ((_num) = EP_RING_INDEX((_num) + 1, (_max))) | ||
70 | #define EP_RING_FULL(_head, _tail, _max) \ | ||
71 | (0 == EP_RING_INDEX(((_tail) - (_head)), (_max))) | ||
72 | #define EP_RING_EMPTY(_head, _tail, _max) \ | ||
73 | (1 == EP_RING_INDEX(((_tail) - (_head)), (_max))) | ||
74 | |||
75 | #define FJES_MTU_TO_BUFFER_SIZE(mtu) \ | ||
76 | (ETH_HLEN + VLAN_HLEN + (mtu) + ETH_FCS_LEN) | ||
77 | #define FJES_MTU_TO_FRAME_SIZE(mtu) \ | ||
78 | (sizeof(struct esmem_frame) + FJES_MTU_TO_BUFFER_SIZE(mtu)) | ||
79 | #define FJES_MTU_DEFINE(size) \ | ||
80 | ((size) - sizeof(struct esmem_frame) - \ | ||
81 | (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)) | ||
82 | |||
83 | #define FJES_DEV_COMMAND_INFO_REQ_LEN (4) | ||
84 | #define FJES_DEV_COMMAND_INFO_RES_LEN(epnum) (8 + 2 * (epnum)) | ||
85 | #define FJES_DEV_COMMAND_SHARE_BUFFER_REQ_LEN(txb, rxb) \ | ||
86 | (24 + (8 * ((txb) / EP_BUFFER_INFO_SIZE + (rxb) / EP_BUFFER_INFO_SIZE))) | ||
87 | #define FJES_DEV_COMMAND_SHARE_BUFFER_RES_LEN (8) | ||
88 | #define FJES_DEV_COMMAND_UNSHARE_BUFFER_REQ_LEN (8) | ||
89 | #define FJES_DEV_COMMAND_UNSHARE_BUFFER_RES_LEN (8) | ||
90 | |||
91 | #define FJES_DEV_REQ_BUF_SIZE(maxep) \ | ||
92 | FJES_DEV_COMMAND_SHARE_BUFFER_REQ_LEN(EP_BUFFER_SIZE, EP_BUFFER_SIZE) | ||
93 | #define FJES_DEV_RES_BUF_SIZE(maxep) \ | ||
94 | FJES_DEV_COMMAND_INFO_RES_LEN(maxep) | ||
95 | |||
96 | /* Frame & MTU */ | ||
97 | struct esmem_frame { | ||
98 | __le32 frame_size; | ||
99 | u8 frame_data[]; | ||
100 | }; | ||
101 | |||
102 | /* EP partner status */ | ||
103 | enum ep_partner_status { | ||
104 | EP_PARTNER_UNSHARE, | ||
105 | EP_PARTNER_SHARED, | ||
106 | EP_PARTNER_WAITING, | ||
107 | EP_PARTNER_COMPLETE, | ||
108 | EP_PARTNER_STATUS_MAX, | ||
109 | }; | ||
110 | |||
111 | /* shared status region */ | ||
112 | struct fjes_device_shared_info { | ||
113 | int epnum; | ||
114 | u8 ep_status[]; | ||
115 | }; | ||
116 | |||
117 | /* structures for command control request data*/ | ||
118 | union fjes_device_command_req { | ||
119 | struct { | ||
120 | __le32 length; | ||
121 | } info; | ||
122 | struct { | ||
123 | __le32 length; | ||
124 | __le32 epid; | ||
125 | __le64 buffer[]; | ||
126 | } share_buffer; | ||
127 | struct { | ||
128 | __le32 length; | ||
129 | __le32 epid; | ||
130 | } unshare_buffer; | ||
131 | struct { | ||
132 | __le32 length; | ||
133 | __le32 mode; | ||
134 | __le64 buffer_len; | ||
135 | __le64 buffer[]; | ||
136 | } start_trace; | ||
137 | struct { | ||
138 | __le32 length; | ||
139 | } stop_trace; | ||
140 | }; | ||
141 | |||
142 | /* structures for command control response data */ | ||
143 | union fjes_device_command_res { | ||
144 | struct { | ||
145 | __le32 length; | ||
146 | __le32 code; | ||
147 | struct { | ||
148 | u8 es_status; | ||
149 | u8 zone; | ||
150 | } info[]; | ||
151 | } info; | ||
152 | struct { | ||
153 | __le32 length; | ||
154 | __le32 code; | ||
155 | } share_buffer; | ||
156 | struct { | ||
157 | __le32 length; | ||
158 | __le32 code; | ||
159 | } unshare_buffer; | ||
160 | struct { | ||
161 | __le32 length; | ||
162 | __le32 code; | ||
163 | } start_trace; | ||
164 | struct { | ||
165 | __le32 length; | ||
166 | __le32 code; | ||
167 | } stop_trace; | ||
168 | }; | ||
169 | |||
170 | /* request command type */ | ||
171 | enum fjes_dev_command_request_type { | ||
172 | FJES_CMD_REQ_INFO = 0x0001, | ||
173 | FJES_CMD_REQ_SHARE_BUFFER = 0x0002, | ||
174 | FJES_CMD_REQ_UNSHARE_BUFFER = 0x0004, | ||
175 | }; | ||
176 | |||
177 | /* parameter for command control */ | ||
178 | struct fjes_device_command_param { | ||
179 | u32 req_len; | ||
180 | phys_addr_t req_start; | ||
181 | u32 res_len; | ||
182 | phys_addr_t res_start; | ||
183 | phys_addr_t share_start; | ||
184 | }; | ||
185 | |||
186 | /* error code for command control */ | ||
187 | enum fjes_dev_command_response_e { | ||
188 | FJES_CMD_STATUS_UNKNOWN, | ||
189 | FJES_CMD_STATUS_NORMAL, | ||
190 | FJES_CMD_STATUS_TIMEOUT, | ||
191 | FJES_CMD_STATUS_ERROR_PARAM, | ||
192 | FJES_CMD_STATUS_ERROR_STATUS, | ||
193 | }; | ||
194 | |||
195 | /* EP buffer information */ | ||
196 | union ep_buffer_info { | ||
197 | u8 raw[EP_BUFFER_INFO_SIZE]; | ||
198 | |||
199 | struct _ep_buffer_info_common_t { | ||
200 | u32 version; | ||
201 | } common; | ||
202 | |||
203 | struct _ep_buffer_info_v1_t { | ||
204 | u32 version; | ||
205 | u32 info_size; | ||
206 | |||
207 | u32 buffer_size; | ||
208 | u16 count_max; | ||
209 | |||
210 | u16 _rsv_1; | ||
211 | |||
212 | u32 frame_max; | ||
213 | u8 mac_addr[ETH_ALEN]; | ||
214 | |||
215 | u16 _rsv_2; | ||
216 | u32 _rsv_3; | ||
217 | |||
218 | u16 tx_status; | ||
219 | u16 rx_status; | ||
220 | |||
221 | u32 head; | ||
222 | u32 tail; | ||
223 | |||
224 | u16 vlan_id[EP_BUFFER_SUPPORT_VLAN_MAX]; | ||
225 | |||
226 | } v1i; | ||
227 | |||
228 | }; | ||
229 | |||
230 | /* buffer pair for Extended Partition */ | ||
231 | struct ep_share_mem_info { | ||
232 | struct epbuf_handler { | ||
233 | void *buffer; | ||
234 | size_t size; | ||
235 | union ep_buffer_info *info; | ||
236 | u8 *ring; | ||
237 | } tx, rx; | ||
238 | |||
239 | struct rtnl_link_stats64 net_stats; | ||
240 | |||
241 | u16 tx_status_work; | ||
242 | |||
243 | u8 es_status; | ||
244 | u8 zone; | ||
245 | }; | ||
246 | |||
247 | struct es_device_trace { | ||
248 | u32 record_num; | ||
249 | u32 current_record; | ||
250 | u32 status_flag; | ||
251 | u32 _rsv; | ||
252 | |||
253 | struct { | ||
254 | u16 epid; | ||
255 | u16 dir_offset; | ||
256 | u32 data; | ||
257 | u64 tsc; | ||
258 | } record[]; | ||
259 | }; | ||
260 | |||
261 | struct fjes_hw_info { | ||
262 | struct fjes_device_shared_info *share; | ||
263 | union fjes_device_command_req *req_buf; | ||
264 | u64 req_buf_size; | ||
265 | union fjes_device_command_res *res_buf; | ||
266 | u64 res_buf_size; | ||
267 | |||
268 | int *my_epid; | ||
269 | int *max_epid; | ||
270 | |||
271 | struct es_device_trace *trace; | ||
272 | u64 trace_size; | ||
273 | |||
274 | struct mutex lock; /* buffer lock*/ | ||
275 | |||
276 | unsigned long buffer_share_bit; | ||
277 | unsigned long buffer_unshare_reserve_bit; | ||
278 | }; | ||
279 | |||
280 | struct fjes_hw { | ||
281 | void *back; | ||
282 | |||
283 | unsigned long txrx_stop_req_bit; | ||
284 | unsigned long epstop_req_bit; | ||
285 | struct work_struct update_zone_task; | ||
286 | struct work_struct epstop_task; | ||
287 | |||
288 | int my_epid; | ||
289 | int max_epid; | ||
290 | |||
291 | struct ep_share_mem_info *ep_shm_info; | ||
292 | |||
293 | struct fjes_hw_resource { | ||
294 | u64 start; | ||
295 | u64 size; | ||
296 | int irq; | ||
297 | } hw_res; | ||
298 | |||
299 | u8 *base; | ||
300 | |||
301 | struct fjes_hw_info hw_info; | ||
302 | }; | ||
303 | |||
304 | int fjes_hw_init(struct fjes_hw *); | ||
305 | void fjes_hw_exit(struct fjes_hw *); | ||
306 | int fjes_hw_reset(struct fjes_hw *); | ||
307 | int fjes_hw_request_info(struct fjes_hw *); | ||
308 | int fjes_hw_register_buff_addr(struct fjes_hw *, int, | ||
309 | struct ep_share_mem_info *); | ||
310 | int fjes_hw_unregister_buff_addr(struct fjes_hw *, int); | ||
311 | void fjes_hw_init_command_registers(struct fjes_hw *, | ||
312 | struct fjes_device_command_param *); | ||
313 | void fjes_hw_setup_epbuf(struct epbuf_handler *, u8 *, u32); | ||
314 | int fjes_hw_raise_interrupt(struct fjes_hw *, int, enum REG_ICTL_MASK); | ||
315 | void fjes_hw_set_irqmask(struct fjes_hw *, enum REG_ICTL_MASK, bool); | ||
316 | u32 fjes_hw_capture_interrupt_status(struct fjes_hw *); | ||
317 | void fjes_hw_raise_epstop(struct fjes_hw *); | ||
318 | int fjes_hw_wait_epstop(struct fjes_hw *); | ||
319 | enum ep_partner_status | ||
320 | fjes_hw_get_partner_ep_status(struct fjes_hw *, int); | ||
321 | |||
322 | bool fjes_hw_epid_is_same_zone(struct fjes_hw *, int); | ||
323 | int fjes_hw_epid_is_shared(struct fjes_device_shared_info *, int); | ||
324 | bool fjes_hw_check_epbuf_version(struct epbuf_handler *, u32); | ||
325 | bool fjes_hw_check_mtu(struct epbuf_handler *, u32); | ||
326 | bool fjes_hw_check_vlan_id(struct epbuf_handler *, u16); | ||
327 | bool fjes_hw_set_vlan_id(struct epbuf_handler *, u16); | ||
328 | void fjes_hw_del_vlan_id(struct epbuf_handler *, u16); | ||
329 | bool fjes_hw_epbuf_rx_is_empty(struct epbuf_handler *); | ||
330 | void *fjes_hw_epbuf_rx_curpkt_get_addr(struct epbuf_handler *, size_t *); | ||
331 | void fjes_hw_epbuf_rx_curpkt_drop(struct epbuf_handler *); | ||
332 | int fjes_hw_epbuf_tx_pkt_send(struct epbuf_handler *, void *, size_t); | ||
333 | |||
334 | #endif /* FJES_HW_H_ */ | ||
diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c new file mode 100644 index 000000000000..0ddb54fe3d91 --- /dev/null +++ b/drivers/net/fjes/fjes_main.c | |||
@@ -0,0 +1,1383 @@ | |||
1 | /* | ||
2 | * FUJITSU Extended Socket Network Device driver | ||
3 | * Copyright (c) 2015 FUJITSU LIMITED | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, see <http://www.gnu.org/licenses/>. | ||
16 | * | ||
17 | * The full GNU General Public License is included in this distribution in | ||
18 | * the file called "COPYING". | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/nls.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/netdevice.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | |||
29 | #include "fjes.h" | ||
30 | |||
31 | #define MAJ 1 | ||
32 | #define MIN 0 | ||
33 | #define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) | ||
34 | #define DRV_NAME "fjes" | ||
35 | char fjes_driver_name[] = DRV_NAME; | ||
36 | char fjes_driver_version[] = DRV_VERSION; | ||
37 | static const char fjes_driver_string[] = | ||
38 | "FUJITSU Extended Socket Network Device Driver"; | ||
39 | static const char fjes_copyright[] = | ||
40 | "Copyright (c) 2015 FUJITSU LIMITED"; | ||
41 | |||
42 | MODULE_AUTHOR("Taku Izumi <izumi.taku@jp.fujitsu.com>"); | ||
43 | MODULE_DESCRIPTION("FUJITSU Extended Socket Network Device Driver"); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | MODULE_VERSION(DRV_VERSION); | ||
46 | |||
47 | static int fjes_request_irq(struct fjes_adapter *); | ||
48 | static void fjes_free_irq(struct fjes_adapter *); | ||
49 | |||
50 | static int fjes_open(struct net_device *); | ||
51 | static int fjes_close(struct net_device *); | ||
52 | static int fjes_setup_resources(struct fjes_adapter *); | ||
53 | static void fjes_free_resources(struct fjes_adapter *); | ||
54 | static netdev_tx_t fjes_xmit_frame(struct sk_buff *, struct net_device *); | ||
55 | static void fjes_raise_intr_rxdata_task(struct work_struct *); | ||
56 | static void fjes_tx_stall_task(struct work_struct *); | ||
57 | static void fjes_force_close_task(struct work_struct *); | ||
58 | static irqreturn_t fjes_intr(int, void*); | ||
59 | static struct rtnl_link_stats64 * | ||
60 | fjes_get_stats64(struct net_device *, struct rtnl_link_stats64 *); | ||
61 | static int fjes_change_mtu(struct net_device *, int); | ||
62 | static int fjes_vlan_rx_add_vid(struct net_device *, __be16 proto, u16); | ||
63 | static int fjes_vlan_rx_kill_vid(struct net_device *, __be16 proto, u16); | ||
64 | static void fjes_tx_retry(struct net_device *); | ||
65 | |||
66 | static int fjes_acpi_add(struct acpi_device *); | ||
67 | static int fjes_acpi_remove(struct acpi_device *); | ||
68 | static acpi_status fjes_get_acpi_resource(struct acpi_resource *, void*); | ||
69 | |||
70 | static int fjes_probe(struct platform_device *); | ||
71 | static int fjes_remove(struct platform_device *); | ||
72 | |||
73 | static int fjes_sw_init(struct fjes_adapter *); | ||
74 | static void fjes_netdev_setup(struct net_device *); | ||
75 | static void fjes_irq_watch_task(struct work_struct *); | ||
76 | static void fjes_watch_unshare_task(struct work_struct *); | ||
77 | static void fjes_rx_irq(struct fjes_adapter *, int); | ||
78 | static int fjes_poll(struct napi_struct *, int); | ||
79 | |||
80 | static const struct acpi_device_id fjes_acpi_ids[] = { | ||
81 | {"PNP0C02", 0}, | ||
82 | {"", 0}, | ||
83 | }; | ||
84 | MODULE_DEVICE_TABLE(acpi, fjes_acpi_ids); | ||
85 | |||
86 | static struct acpi_driver fjes_acpi_driver = { | ||
87 | .name = DRV_NAME, | ||
88 | .class = DRV_NAME, | ||
89 | .owner = THIS_MODULE, | ||
90 | .ids = fjes_acpi_ids, | ||
91 | .ops = { | ||
92 | .add = fjes_acpi_add, | ||
93 | .remove = fjes_acpi_remove, | ||
94 | }, | ||
95 | }; | ||
96 | |||
97 | static struct platform_driver fjes_driver = { | ||
98 | .driver = { | ||
99 | .name = DRV_NAME, | ||
100 | .owner = THIS_MODULE, | ||
101 | }, | ||
102 | .probe = fjes_probe, | ||
103 | .remove = fjes_remove, | ||
104 | }; | ||
105 | |||
106 | static struct resource fjes_resource[] = { | ||
107 | { | ||
108 | .flags = IORESOURCE_MEM, | ||
109 | .start = 0, | ||
110 | .end = 0, | ||
111 | }, | ||
112 | { | ||
113 | .flags = IORESOURCE_IRQ, | ||
114 | .start = 0, | ||
115 | .end = 0, | ||
116 | }, | ||
117 | }; | ||
118 | |||
119 | static int fjes_acpi_add(struct acpi_device *device) | ||
120 | { | ||
121 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; | ||
122 | char str_buf[sizeof(FJES_ACPI_SYMBOL) + 1]; | ||
123 | struct platform_device *plat_dev; | ||
124 | union acpi_object *str; | ||
125 | acpi_status status; | ||
126 | int result; | ||
127 | |||
128 | status = acpi_evaluate_object(device->handle, "_STR", NULL, &buffer); | ||
129 | if (ACPI_FAILURE(status)) | ||
130 | return -ENODEV; | ||
131 | |||
132 | str = buffer.pointer; | ||
133 | result = utf16s_to_utf8s((wchar_t *)str->string.pointer, | ||
134 | str->string.length, UTF16_LITTLE_ENDIAN, | ||
135 | str_buf, sizeof(str_buf) - 1); | ||
136 | str_buf[result] = 0; | ||
137 | |||
138 | if (strncmp(FJES_ACPI_SYMBOL, str_buf, strlen(FJES_ACPI_SYMBOL)) != 0) { | ||
139 | kfree(buffer.pointer); | ||
140 | return -ENODEV; | ||
141 | } | ||
142 | kfree(buffer.pointer); | ||
143 | |||
144 | status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, | ||
145 | fjes_get_acpi_resource, fjes_resource); | ||
146 | if (ACPI_FAILURE(status)) | ||
147 | return -ENODEV; | ||
148 | |||
149 | /* create platform_device */ | ||
150 | plat_dev = platform_device_register_simple(DRV_NAME, 0, fjes_resource, | ||
151 | ARRAY_SIZE(fjes_resource)); | ||
152 | device->driver_data = plat_dev; | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static int fjes_acpi_remove(struct acpi_device *device) | ||
158 | { | ||
159 | struct platform_device *plat_dev; | ||
160 | |||
161 | plat_dev = (struct platform_device *)acpi_driver_data(device); | ||
162 | platform_device_unregister(plat_dev); | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static acpi_status | ||
168 | fjes_get_acpi_resource(struct acpi_resource *acpi_res, void *data) | ||
169 | { | ||
170 | struct acpi_resource_address32 *addr; | ||
171 | struct acpi_resource_irq *irq; | ||
172 | struct resource *res = data; | ||
173 | |||
174 | switch (acpi_res->type) { | ||
175 | case ACPI_RESOURCE_TYPE_ADDRESS32: | ||
176 | addr = &acpi_res->data.address32; | ||
177 | res[0].start = addr->address.minimum; | ||
178 | res[0].end = addr->address.minimum + | ||
179 | addr->address.address_length - 1; | ||
180 | break; | ||
181 | |||
182 | case ACPI_RESOURCE_TYPE_IRQ: | ||
183 | irq = &acpi_res->data.irq; | ||
184 | if (irq->interrupt_count != 1) | ||
185 | return AE_ERROR; | ||
186 | res[1].start = irq->interrupts[0]; | ||
187 | res[1].end = irq->interrupts[0]; | ||
188 | break; | ||
189 | |||
190 | default: | ||
191 | break; | ||
192 | } | ||
193 | |||
194 | return AE_OK; | ||
195 | } | ||
196 | |||
197 | static int fjes_request_irq(struct fjes_adapter *adapter) | ||
198 | { | ||
199 | struct net_device *netdev = adapter->netdev; | ||
200 | int result = -1; | ||
201 | |||
202 | adapter->interrupt_watch_enable = true; | ||
203 | if (!delayed_work_pending(&adapter->interrupt_watch_task)) { | ||
204 | queue_delayed_work(adapter->control_wq, | ||
205 | &adapter->interrupt_watch_task, | ||
206 | FJES_IRQ_WATCH_DELAY); | ||
207 | } | ||
208 | |||
209 | if (!adapter->irq_registered) { | ||
210 | result = request_irq(adapter->hw.hw_res.irq, fjes_intr, | ||
211 | IRQF_SHARED, netdev->name, adapter); | ||
212 | if (result) | ||
213 | adapter->irq_registered = false; | ||
214 | else | ||
215 | adapter->irq_registered = true; | ||
216 | } | ||
217 | |||
218 | return result; | ||
219 | } | ||
220 | |||
221 | static void fjes_free_irq(struct fjes_adapter *adapter) | ||
222 | { | ||
223 | struct fjes_hw *hw = &adapter->hw; | ||
224 | |||
225 | adapter->interrupt_watch_enable = false; | ||
226 | cancel_delayed_work_sync(&adapter->interrupt_watch_task); | ||
227 | |||
228 | fjes_hw_set_irqmask(hw, REG_ICTL_MASK_ALL, true); | ||
229 | |||
230 | if (adapter->irq_registered) { | ||
231 | free_irq(adapter->hw.hw_res.irq, adapter); | ||
232 | adapter->irq_registered = false; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | static const struct net_device_ops fjes_netdev_ops = { | ||
237 | .ndo_open = fjes_open, | ||
238 | .ndo_stop = fjes_close, | ||
239 | .ndo_start_xmit = fjes_xmit_frame, | ||
240 | .ndo_get_stats64 = fjes_get_stats64, | ||
241 | .ndo_change_mtu = fjes_change_mtu, | ||
242 | .ndo_tx_timeout = fjes_tx_retry, | ||
243 | .ndo_vlan_rx_add_vid = fjes_vlan_rx_add_vid, | ||
244 | .ndo_vlan_rx_kill_vid = fjes_vlan_rx_kill_vid, | ||
245 | }; | ||
246 | |||
247 | /* fjes_open - Called when a network interface is made active */ | ||
248 | static int fjes_open(struct net_device *netdev) | ||
249 | { | ||
250 | struct fjes_adapter *adapter = netdev_priv(netdev); | ||
251 | struct fjes_hw *hw = &adapter->hw; | ||
252 | int result; | ||
253 | |||
254 | if (adapter->open_guard) | ||
255 | return -ENXIO; | ||
256 | |||
257 | result = fjes_setup_resources(adapter); | ||
258 | if (result) | ||
259 | goto err_setup_res; | ||
260 | |||
261 | hw->txrx_stop_req_bit = 0; | ||
262 | hw->epstop_req_bit = 0; | ||
263 | |||
264 | napi_enable(&adapter->napi); | ||
265 | |||
266 | fjes_hw_capture_interrupt_status(hw); | ||
267 | |||
268 | result = fjes_request_irq(adapter); | ||
269 | if (result) | ||
270 | goto err_req_irq; | ||
271 | |||
272 | fjes_hw_set_irqmask(hw, REG_ICTL_MASK_ALL, false); | ||
273 | |||
274 | netif_tx_start_all_queues(netdev); | ||
275 | netif_carrier_on(netdev); | ||
276 | |||
277 | return 0; | ||
278 | |||
279 | err_req_irq: | ||
280 | fjes_free_irq(adapter); | ||
281 | napi_disable(&adapter->napi); | ||
282 | |||
283 | err_setup_res: | ||
284 | fjes_free_resources(adapter); | ||
285 | return result; | ||
286 | } | ||
287 | |||
288 | /* fjes_close - Disables a network interface */ | ||
289 | static int fjes_close(struct net_device *netdev) | ||
290 | { | ||
291 | struct fjes_adapter *adapter = netdev_priv(netdev); | ||
292 | struct fjes_hw *hw = &adapter->hw; | ||
293 | int epidx; | ||
294 | |||
295 | netif_tx_stop_all_queues(netdev); | ||
296 | netif_carrier_off(netdev); | ||
297 | |||
298 | fjes_hw_raise_epstop(hw); | ||
299 | |||
300 | napi_disable(&adapter->napi); | ||
301 | |||
302 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
303 | if (epidx == hw->my_epid) | ||
304 | continue; | ||
305 | |||
306 | adapter->hw.ep_shm_info[epidx].tx.info->v1i.rx_status &= | ||
307 | ~FJES_RX_POLL_WORK; | ||
308 | } | ||
309 | |||
310 | fjes_free_irq(adapter); | ||
311 | |||
312 | cancel_delayed_work_sync(&adapter->interrupt_watch_task); | ||
313 | cancel_work_sync(&adapter->unshare_watch_task); | ||
314 | adapter->unshare_watch_bitmask = 0; | ||
315 | cancel_work_sync(&adapter->raise_intr_rxdata_task); | ||
316 | cancel_work_sync(&adapter->tx_stall_task); | ||
317 | |||
318 | cancel_work_sync(&hw->update_zone_task); | ||
319 | cancel_work_sync(&hw->epstop_task); | ||
320 | |||
321 | fjes_hw_wait_epstop(hw); | ||
322 | |||
323 | fjes_free_resources(adapter); | ||
324 | |||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static int fjes_setup_resources(struct fjes_adapter *adapter) | ||
329 | { | ||
330 | struct net_device *netdev = adapter->netdev; | ||
331 | struct ep_share_mem_info *buf_pair; | ||
332 | struct fjes_hw *hw = &adapter->hw; | ||
333 | int result; | ||
334 | int epidx; | ||
335 | |||
336 | mutex_lock(&hw->hw_info.lock); | ||
337 | result = fjes_hw_request_info(hw); | ||
338 | switch (result) { | ||
339 | case 0: | ||
340 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
341 | hw->ep_shm_info[epidx].es_status = | ||
342 | hw->hw_info.res_buf->info.info[epidx].es_status; | ||
343 | hw->ep_shm_info[epidx].zone = | ||
344 | hw->hw_info.res_buf->info.info[epidx].zone; | ||
345 | } | ||
346 | break; | ||
347 | default: | ||
348 | case -ENOMSG: | ||
349 | case -EBUSY: | ||
350 | adapter->force_reset = true; | ||
351 | |||
352 | mutex_unlock(&hw->hw_info.lock); | ||
353 | return result; | ||
354 | } | ||
355 | mutex_unlock(&hw->hw_info.lock); | ||
356 | |||
357 | for (epidx = 0; epidx < (hw->max_epid); epidx++) { | ||
358 | if ((epidx != hw->my_epid) && | ||
359 | (hw->ep_shm_info[epidx].es_status == | ||
360 | FJES_ZONING_STATUS_ENABLE)) { | ||
361 | fjes_hw_raise_interrupt(hw, epidx, | ||
362 | REG_ICTL_MASK_INFO_UPDATE); | ||
363 | } | ||
364 | } | ||
365 | |||
366 | msleep(FJES_OPEN_ZONE_UPDATE_WAIT * hw->max_epid); | ||
367 | |||
368 | for (epidx = 0; epidx < (hw->max_epid); epidx++) { | ||
369 | if (epidx == hw->my_epid) | ||
370 | continue; | ||
371 | |||
372 | buf_pair = &hw->ep_shm_info[epidx]; | ||
373 | |||
374 | fjes_hw_setup_epbuf(&buf_pair->tx, netdev->dev_addr, | ||
375 | netdev->mtu); | ||
376 | |||
377 | if (fjes_hw_epid_is_same_zone(hw, epidx)) { | ||
378 | mutex_lock(&hw->hw_info.lock); | ||
379 | result = | ||
380 | fjes_hw_register_buff_addr(hw, epidx, buf_pair); | ||
381 | mutex_unlock(&hw->hw_info.lock); | ||
382 | |||
383 | switch (result) { | ||
384 | case 0: | ||
385 | break; | ||
386 | case -ENOMSG: | ||
387 | case -EBUSY: | ||
388 | default: | ||
389 | adapter->force_reset = true; | ||
390 | return result; | ||
391 | } | ||
392 | } | ||
393 | } | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | static void fjes_free_resources(struct fjes_adapter *adapter) | ||
399 | { | ||
400 | struct net_device *netdev = adapter->netdev; | ||
401 | struct fjes_device_command_param param; | ||
402 | struct ep_share_mem_info *buf_pair; | ||
403 | struct fjes_hw *hw = &adapter->hw; | ||
404 | bool reset_flag = false; | ||
405 | int result; | ||
406 | int epidx; | ||
407 | |||
408 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
409 | if (epidx == hw->my_epid) | ||
410 | continue; | ||
411 | |||
412 | mutex_lock(&hw->hw_info.lock); | ||
413 | result = fjes_hw_unregister_buff_addr(hw, epidx); | ||
414 | mutex_unlock(&hw->hw_info.lock); | ||
415 | |||
416 | if (result) | ||
417 | reset_flag = true; | ||
418 | |||
419 | buf_pair = &hw->ep_shm_info[epidx]; | ||
420 | |||
421 | fjes_hw_setup_epbuf(&buf_pair->tx, | ||
422 | netdev->dev_addr, netdev->mtu); | ||
423 | |||
424 | clear_bit(epidx, &hw->txrx_stop_req_bit); | ||
425 | } | ||
426 | |||
427 | if (reset_flag || adapter->force_reset) { | ||
428 | result = fjes_hw_reset(hw); | ||
429 | |||
430 | adapter->force_reset = false; | ||
431 | |||
432 | if (result) | ||
433 | adapter->open_guard = true; | ||
434 | |||
435 | hw->hw_info.buffer_share_bit = 0; | ||
436 | |||
437 | memset((void *)¶m, 0, sizeof(param)); | ||
438 | |||
439 | param.req_len = hw->hw_info.req_buf_size; | ||
440 | param.req_start = __pa(hw->hw_info.req_buf); | ||
441 | param.res_len = hw->hw_info.res_buf_size; | ||
442 | param.res_start = __pa(hw->hw_info.res_buf); | ||
443 | param.share_start = __pa(hw->hw_info.share->ep_status); | ||
444 | |||
445 | fjes_hw_init_command_registers(hw, ¶m); | ||
446 | } | ||
447 | } | ||
448 | |||
449 | static void fjes_tx_stall_task(struct work_struct *work) | ||
450 | { | ||
451 | struct fjes_adapter *adapter = container_of(work, | ||
452 | struct fjes_adapter, tx_stall_task); | ||
453 | struct net_device *netdev = adapter->netdev; | ||
454 | struct fjes_hw *hw = &adapter->hw; | ||
455 | int all_queue_available, sendable; | ||
456 | enum ep_partner_status pstatus; | ||
457 | int max_epid, my_epid, epid; | ||
458 | union ep_buffer_info *info; | ||
459 | int i; | ||
460 | |||
461 | if (((long)jiffies - | ||
462 | (long)(netdev->trans_start)) > FJES_TX_TX_STALL_TIMEOUT) { | ||
463 | netif_wake_queue(netdev); | ||
464 | return; | ||
465 | } | ||
466 | |||
467 | my_epid = hw->my_epid; | ||
468 | max_epid = hw->max_epid; | ||
469 | |||
470 | for (i = 0; i < 5; i++) { | ||
471 | all_queue_available = 1; | ||
472 | |||
473 | for (epid = 0; epid < max_epid; epid++) { | ||
474 | if (my_epid == epid) | ||
475 | continue; | ||
476 | |||
477 | pstatus = fjes_hw_get_partner_ep_status(hw, epid); | ||
478 | sendable = (pstatus == EP_PARTNER_SHARED); | ||
479 | if (!sendable) | ||
480 | continue; | ||
481 | |||
482 | info = adapter->hw.ep_shm_info[epid].tx.info; | ||
483 | |||
484 | if (EP_RING_FULL(info->v1i.head, info->v1i.tail, | ||
485 | info->v1i.count_max)) { | ||
486 | all_queue_available = 0; | ||
487 | break; | ||
488 | } | ||
489 | } | ||
490 | |||
491 | if (all_queue_available) { | ||
492 | netif_wake_queue(netdev); | ||
493 | return; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | usleep_range(50, 100); | ||
498 | |||
499 | queue_work(adapter->txrx_wq, &adapter->tx_stall_task); | ||
500 | } | ||
501 | |||
502 | static void fjes_force_close_task(struct work_struct *work) | ||
503 | { | ||
504 | struct fjes_adapter *adapter = container_of(work, | ||
505 | struct fjes_adapter, force_close_task); | ||
506 | struct net_device *netdev = adapter->netdev; | ||
507 | |||
508 | rtnl_lock(); | ||
509 | dev_close(netdev); | ||
510 | rtnl_unlock(); | ||
511 | } | ||
512 | |||
513 | static void fjes_raise_intr_rxdata_task(struct work_struct *work) | ||
514 | { | ||
515 | struct fjes_adapter *adapter = container_of(work, | ||
516 | struct fjes_adapter, raise_intr_rxdata_task); | ||
517 | struct fjes_hw *hw = &adapter->hw; | ||
518 | enum ep_partner_status pstatus; | ||
519 | int max_epid, my_epid, epid; | ||
520 | |||
521 | my_epid = hw->my_epid; | ||
522 | max_epid = hw->max_epid; | ||
523 | |||
524 | for (epid = 0; epid < max_epid; epid++) | ||
525 | hw->ep_shm_info[epid].tx_status_work = 0; | ||
526 | |||
527 | for (epid = 0; epid < max_epid; epid++) { | ||
528 | if (epid == my_epid) | ||
529 | continue; | ||
530 | |||
531 | pstatus = fjes_hw_get_partner_ep_status(hw, epid); | ||
532 | if (pstatus == EP_PARTNER_SHARED) { | ||
533 | hw->ep_shm_info[epid].tx_status_work = | ||
534 | hw->ep_shm_info[epid].tx.info->v1i.tx_status; | ||
535 | |||
536 | if (hw->ep_shm_info[epid].tx_status_work == | ||
537 | FJES_TX_DELAY_SEND_PENDING) { | ||
538 | hw->ep_shm_info[epid].tx.info->v1i.tx_status = | ||
539 | FJES_TX_DELAY_SEND_NONE; | ||
540 | } | ||
541 | } | ||
542 | } | ||
543 | |||
544 | for (epid = 0; epid < max_epid; epid++) { | ||
545 | if (epid == my_epid) | ||
546 | continue; | ||
547 | |||
548 | pstatus = fjes_hw_get_partner_ep_status(hw, epid); | ||
549 | if ((hw->ep_shm_info[epid].tx_status_work == | ||
550 | FJES_TX_DELAY_SEND_PENDING) && | ||
551 | (pstatus == EP_PARTNER_SHARED) && | ||
552 | !(hw->ep_shm_info[epid].rx.info->v1i.rx_status)) { | ||
553 | fjes_hw_raise_interrupt(hw, epid, | ||
554 | REG_ICTL_MASK_RX_DATA); | ||
555 | } | ||
556 | } | ||
557 | |||
558 | usleep_range(500, 1000); | ||
559 | } | ||
560 | |||
561 | static int fjes_tx_send(struct fjes_adapter *adapter, int dest, | ||
562 | void *data, size_t len) | ||
563 | { | ||
564 | int retval; | ||
565 | |||
566 | retval = fjes_hw_epbuf_tx_pkt_send(&adapter->hw.ep_shm_info[dest].tx, | ||
567 | data, len); | ||
568 | if (retval) | ||
569 | return retval; | ||
570 | |||
571 | adapter->hw.ep_shm_info[dest].tx.info->v1i.tx_status = | ||
572 | FJES_TX_DELAY_SEND_PENDING; | ||
573 | if (!work_pending(&adapter->raise_intr_rxdata_task)) | ||
574 | queue_work(adapter->txrx_wq, | ||
575 | &adapter->raise_intr_rxdata_task); | ||
576 | |||
577 | retval = 0; | ||
578 | return retval; | ||
579 | } | ||
580 | |||
581 | static netdev_tx_t | ||
582 | fjes_xmit_frame(struct sk_buff *skb, struct net_device *netdev) | ||
583 | { | ||
584 | struct fjes_adapter *adapter = netdev_priv(netdev); | ||
585 | struct fjes_hw *hw = &adapter->hw; | ||
586 | |||
587 | int max_epid, my_epid, dest_epid; | ||
588 | enum ep_partner_status pstatus; | ||
589 | struct netdev_queue *cur_queue; | ||
590 | char shortpkt[VLAN_ETH_HLEN]; | ||
591 | bool is_multi, vlan; | ||
592 | struct ethhdr *eth; | ||
593 | u16 queue_no = 0; | ||
594 | u16 vlan_id = 0; | ||
595 | netdev_tx_t ret; | ||
596 | char *data; | ||
597 | int len; | ||
598 | |||
599 | ret = NETDEV_TX_OK; | ||
600 | is_multi = false; | ||
601 | cur_queue = netdev_get_tx_queue(netdev, queue_no); | ||
602 | |||
603 | eth = (struct ethhdr *)skb->data; | ||
604 | my_epid = hw->my_epid; | ||
605 | |||
606 | vlan = (vlan_get_tag(skb, &vlan_id) == 0) ? true : false; | ||
607 | |||
608 | data = skb->data; | ||
609 | len = skb->len; | ||
610 | |||
611 | if (is_multicast_ether_addr(eth->h_dest)) { | ||
612 | dest_epid = 0; | ||
613 | max_epid = hw->max_epid; | ||
614 | is_multi = true; | ||
615 | } else if (is_local_ether_addr(eth->h_dest)) { | ||
616 | dest_epid = eth->h_dest[ETH_ALEN - 1]; | ||
617 | max_epid = dest_epid + 1; | ||
618 | |||
619 | if ((eth->h_dest[0] == 0x02) && | ||
620 | (0x00 == (eth->h_dest[1] | eth->h_dest[2] | | ||
621 | eth->h_dest[3] | eth->h_dest[4])) && | ||
622 | (dest_epid < hw->max_epid)) { | ||
623 | ; | ||
624 | } else { | ||
625 | dest_epid = 0; | ||
626 | max_epid = 0; | ||
627 | ret = NETDEV_TX_OK; | ||
628 | |||
629 | adapter->stats64.tx_packets += 1; | ||
630 | hw->ep_shm_info[my_epid].net_stats.tx_packets += 1; | ||
631 | adapter->stats64.tx_bytes += len; | ||
632 | hw->ep_shm_info[my_epid].net_stats.tx_bytes += len; | ||
633 | } | ||
634 | } else { | ||
635 | dest_epid = 0; | ||
636 | max_epid = 0; | ||
637 | ret = NETDEV_TX_OK; | ||
638 | |||
639 | adapter->stats64.tx_packets += 1; | ||
640 | hw->ep_shm_info[my_epid].net_stats.tx_packets += 1; | ||
641 | adapter->stats64.tx_bytes += len; | ||
642 | hw->ep_shm_info[my_epid].net_stats.tx_bytes += len; | ||
643 | } | ||
644 | |||
645 | for (; dest_epid < max_epid; dest_epid++) { | ||
646 | if (my_epid == dest_epid) | ||
647 | continue; | ||
648 | |||
649 | pstatus = fjes_hw_get_partner_ep_status(hw, dest_epid); | ||
650 | if (pstatus != EP_PARTNER_SHARED) { | ||
651 | ret = NETDEV_TX_OK; | ||
652 | } else if (!fjes_hw_check_epbuf_version( | ||
653 | &adapter->hw.ep_shm_info[dest_epid].rx, 0)) { | ||
654 | /* version is NOT 0 */ | ||
655 | adapter->stats64.tx_carrier_errors += 1; | ||
656 | hw->ep_shm_info[my_epid].net_stats | ||
657 | .tx_carrier_errors += 1; | ||
658 | |||
659 | ret = NETDEV_TX_OK; | ||
660 | } else if (!fjes_hw_check_mtu( | ||
661 | &adapter->hw.ep_shm_info[dest_epid].rx, | ||
662 | netdev->mtu)) { | ||
663 | adapter->stats64.tx_dropped += 1; | ||
664 | hw->ep_shm_info[my_epid].net_stats.tx_dropped += 1; | ||
665 | adapter->stats64.tx_errors += 1; | ||
666 | hw->ep_shm_info[my_epid].net_stats.tx_errors += 1; | ||
667 | |||
668 | ret = NETDEV_TX_OK; | ||
669 | } else if (vlan && | ||
670 | !fjes_hw_check_vlan_id( | ||
671 | &adapter->hw.ep_shm_info[dest_epid].rx, | ||
672 | vlan_id)) { | ||
673 | ret = NETDEV_TX_OK; | ||
674 | } else { | ||
675 | if (len < VLAN_ETH_HLEN) { | ||
676 | memset(shortpkt, 0, VLAN_ETH_HLEN); | ||
677 | memcpy(shortpkt, skb->data, skb->len); | ||
678 | len = VLAN_ETH_HLEN; | ||
679 | data = shortpkt; | ||
680 | } | ||
681 | |||
682 | if (adapter->tx_retry_count == 0) { | ||
683 | adapter->tx_start_jiffies = jiffies; | ||
684 | adapter->tx_retry_count = 1; | ||
685 | } else { | ||
686 | adapter->tx_retry_count++; | ||
687 | } | ||
688 | |||
689 | if (fjes_tx_send(adapter, dest_epid, data, len)) { | ||
690 | if (is_multi) { | ||
691 | ret = NETDEV_TX_OK; | ||
692 | } else if ( | ||
693 | ((long)jiffies - | ||
694 | (long)adapter->tx_start_jiffies) >= | ||
695 | FJES_TX_RETRY_TIMEOUT) { | ||
696 | adapter->stats64.tx_fifo_errors += 1; | ||
697 | hw->ep_shm_info[my_epid].net_stats | ||
698 | .tx_fifo_errors += 1; | ||
699 | adapter->stats64.tx_errors += 1; | ||
700 | hw->ep_shm_info[my_epid].net_stats | ||
701 | .tx_errors += 1; | ||
702 | |||
703 | ret = NETDEV_TX_OK; | ||
704 | } else { | ||
705 | netdev->trans_start = jiffies; | ||
706 | netif_tx_stop_queue(cur_queue); | ||
707 | |||
708 | if (!work_pending(&adapter->tx_stall_task)) | ||
709 | queue_work(adapter->txrx_wq, | ||
710 | &adapter->tx_stall_task); | ||
711 | |||
712 | ret = NETDEV_TX_BUSY; | ||
713 | } | ||
714 | } else { | ||
715 | if (!is_multi) { | ||
716 | adapter->stats64.tx_packets += 1; | ||
717 | hw->ep_shm_info[my_epid].net_stats | ||
718 | .tx_packets += 1; | ||
719 | adapter->stats64.tx_bytes += len; | ||
720 | hw->ep_shm_info[my_epid].net_stats | ||
721 | .tx_bytes += len; | ||
722 | } | ||
723 | |||
724 | adapter->tx_retry_count = 0; | ||
725 | ret = NETDEV_TX_OK; | ||
726 | } | ||
727 | } | ||
728 | } | ||
729 | |||
730 | if (ret == NETDEV_TX_OK) { | ||
731 | dev_kfree_skb(skb); | ||
732 | if (is_multi) { | ||
733 | adapter->stats64.tx_packets += 1; | ||
734 | hw->ep_shm_info[my_epid].net_stats.tx_packets += 1; | ||
735 | adapter->stats64.tx_bytes += 1; | ||
736 | hw->ep_shm_info[my_epid].net_stats.tx_bytes += len; | ||
737 | } | ||
738 | } | ||
739 | |||
740 | return ret; | ||
741 | } | ||
742 | |||
743 | static void fjes_tx_retry(struct net_device *netdev) | ||
744 | { | ||
745 | struct netdev_queue *queue = netdev_get_tx_queue(netdev, 0); | ||
746 | |||
747 | netif_tx_wake_queue(queue); | ||
748 | } | ||
749 | |||
750 | static struct rtnl_link_stats64 * | ||
751 | fjes_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) | ||
752 | { | ||
753 | struct fjes_adapter *adapter = netdev_priv(netdev); | ||
754 | |||
755 | memcpy(stats, &adapter->stats64, sizeof(struct rtnl_link_stats64)); | ||
756 | |||
757 | return stats; | ||
758 | } | ||
759 | |||
760 | static int fjes_change_mtu(struct net_device *netdev, int new_mtu) | ||
761 | { | ||
762 | bool running = netif_running(netdev); | ||
763 | int ret = 0; | ||
764 | int idx; | ||
765 | |||
766 | for (idx = 0; fjes_support_mtu[idx] != 0; idx++) { | ||
767 | if (new_mtu <= fjes_support_mtu[idx]) { | ||
768 | new_mtu = fjes_support_mtu[idx]; | ||
769 | if (new_mtu == netdev->mtu) | ||
770 | return 0; | ||
771 | |||
772 | if (running) | ||
773 | fjes_close(netdev); | ||
774 | |||
775 | netdev->mtu = new_mtu; | ||
776 | |||
777 | if (running) | ||
778 | ret = fjes_open(netdev); | ||
779 | |||
780 | return ret; | ||
781 | } | ||
782 | } | ||
783 | |||
784 | return -EINVAL; | ||
785 | } | ||
786 | |||
787 | static int fjes_vlan_rx_add_vid(struct net_device *netdev, | ||
788 | __be16 proto, u16 vid) | ||
789 | { | ||
790 | struct fjes_adapter *adapter = netdev_priv(netdev); | ||
791 | bool ret = true; | ||
792 | int epid; | ||
793 | |||
794 | for (epid = 0; epid < adapter->hw.max_epid; epid++) { | ||
795 | if (epid == adapter->hw.my_epid) | ||
796 | continue; | ||
797 | |||
798 | if (!fjes_hw_check_vlan_id( | ||
799 | &adapter->hw.ep_shm_info[epid].tx, vid)) | ||
800 | ret = fjes_hw_set_vlan_id( | ||
801 | &adapter->hw.ep_shm_info[epid].tx, vid); | ||
802 | } | ||
803 | |||
804 | return ret ? 0 : -ENOSPC; | ||
805 | } | ||
806 | |||
807 | static int fjes_vlan_rx_kill_vid(struct net_device *netdev, | ||
808 | __be16 proto, u16 vid) | ||
809 | { | ||
810 | struct fjes_adapter *adapter = netdev_priv(netdev); | ||
811 | int epid; | ||
812 | |||
813 | for (epid = 0; epid < adapter->hw.max_epid; epid++) { | ||
814 | if (epid == adapter->hw.my_epid) | ||
815 | continue; | ||
816 | |||
817 | fjes_hw_del_vlan_id(&adapter->hw.ep_shm_info[epid].tx, vid); | ||
818 | } | ||
819 | |||
820 | return 0; | ||
821 | } | ||
822 | |||
823 | static void fjes_txrx_stop_req_irq(struct fjes_adapter *adapter, | ||
824 | int src_epid) | ||
825 | { | ||
826 | struct fjes_hw *hw = &adapter->hw; | ||
827 | enum ep_partner_status status; | ||
828 | |||
829 | status = fjes_hw_get_partner_ep_status(hw, src_epid); | ||
830 | switch (status) { | ||
831 | case EP_PARTNER_UNSHARE: | ||
832 | case EP_PARTNER_COMPLETE: | ||
833 | default: | ||
834 | break; | ||
835 | case EP_PARTNER_WAITING: | ||
836 | if (src_epid < hw->my_epid) { | ||
837 | hw->ep_shm_info[src_epid].tx.info->v1i.rx_status |= | ||
838 | FJES_RX_STOP_REQ_DONE; | ||
839 | |||
840 | clear_bit(src_epid, &hw->txrx_stop_req_bit); | ||
841 | set_bit(src_epid, &adapter->unshare_watch_bitmask); | ||
842 | |||
843 | if (!work_pending(&adapter->unshare_watch_task)) | ||
844 | queue_work(adapter->control_wq, | ||
845 | &adapter->unshare_watch_task); | ||
846 | } | ||
847 | break; | ||
848 | case EP_PARTNER_SHARED: | ||
849 | if (hw->ep_shm_info[src_epid].rx.info->v1i.rx_status & | ||
850 | FJES_RX_STOP_REQ_REQUEST) { | ||
851 | set_bit(src_epid, &hw->epstop_req_bit); | ||
852 | if (!work_pending(&hw->epstop_task)) | ||
853 | queue_work(adapter->control_wq, | ||
854 | &hw->epstop_task); | ||
855 | } | ||
856 | break; | ||
857 | } | ||
858 | } | ||
859 | |||
860 | static void fjes_stop_req_irq(struct fjes_adapter *adapter, int src_epid) | ||
861 | { | ||
862 | struct fjes_hw *hw = &adapter->hw; | ||
863 | enum ep_partner_status status; | ||
864 | |||
865 | set_bit(src_epid, &hw->hw_info.buffer_unshare_reserve_bit); | ||
866 | |||
867 | status = fjes_hw_get_partner_ep_status(hw, src_epid); | ||
868 | switch (status) { | ||
869 | case EP_PARTNER_WAITING: | ||
870 | hw->ep_shm_info[src_epid].tx.info->v1i.rx_status |= | ||
871 | FJES_RX_STOP_REQ_DONE; | ||
872 | clear_bit(src_epid, &hw->txrx_stop_req_bit); | ||
873 | /* fall through */ | ||
874 | case EP_PARTNER_UNSHARE: | ||
875 | case EP_PARTNER_COMPLETE: | ||
876 | default: | ||
877 | set_bit(src_epid, &adapter->unshare_watch_bitmask); | ||
878 | if (!work_pending(&adapter->unshare_watch_task)) | ||
879 | queue_work(adapter->control_wq, | ||
880 | &adapter->unshare_watch_task); | ||
881 | break; | ||
882 | case EP_PARTNER_SHARED: | ||
883 | set_bit(src_epid, &hw->epstop_req_bit); | ||
884 | |||
885 | if (!work_pending(&hw->epstop_task)) | ||
886 | queue_work(adapter->control_wq, &hw->epstop_task); | ||
887 | break; | ||
888 | } | ||
889 | } | ||
890 | |||
891 | static void fjes_update_zone_irq(struct fjes_adapter *adapter, | ||
892 | int src_epid) | ||
893 | { | ||
894 | struct fjes_hw *hw = &adapter->hw; | ||
895 | |||
896 | if (!work_pending(&hw->update_zone_task)) | ||
897 | queue_work(adapter->control_wq, &hw->update_zone_task); | ||
898 | } | ||
899 | |||
900 | static irqreturn_t fjes_intr(int irq, void *data) | ||
901 | { | ||
902 | struct fjes_adapter *adapter = data; | ||
903 | struct fjes_hw *hw = &adapter->hw; | ||
904 | irqreturn_t ret; | ||
905 | u32 icr; | ||
906 | |||
907 | icr = fjes_hw_capture_interrupt_status(hw); | ||
908 | |||
909 | if (icr & REG_IS_MASK_IS_ASSERT) { | ||
910 | if (icr & REG_ICTL_MASK_RX_DATA) | ||
911 | fjes_rx_irq(adapter, icr & REG_IS_MASK_EPID); | ||
912 | |||
913 | if (icr & REG_ICTL_MASK_DEV_STOP_REQ) | ||
914 | fjes_stop_req_irq(adapter, icr & REG_IS_MASK_EPID); | ||
915 | |||
916 | if (icr & REG_ICTL_MASK_TXRX_STOP_REQ) | ||
917 | fjes_txrx_stop_req_irq(adapter, icr & REG_IS_MASK_EPID); | ||
918 | |||
919 | if (icr & REG_ICTL_MASK_TXRX_STOP_DONE) | ||
920 | fjes_hw_set_irqmask(hw, | ||
921 | REG_ICTL_MASK_TXRX_STOP_DONE, true); | ||
922 | |||
923 | if (icr & REG_ICTL_MASK_INFO_UPDATE) | ||
924 | fjes_update_zone_irq(adapter, icr & REG_IS_MASK_EPID); | ||
925 | |||
926 | ret = IRQ_HANDLED; | ||
927 | } else { | ||
928 | ret = IRQ_NONE; | ||
929 | } | ||
930 | |||
931 | return ret; | ||
932 | } | ||
933 | |||
934 | static int fjes_rxframe_search_exist(struct fjes_adapter *adapter, | ||
935 | int start_epid) | ||
936 | { | ||
937 | struct fjes_hw *hw = &adapter->hw; | ||
938 | enum ep_partner_status pstatus; | ||
939 | int max_epid, cur_epid; | ||
940 | int i; | ||
941 | |||
942 | max_epid = hw->max_epid; | ||
943 | start_epid = (start_epid + 1 + max_epid) % max_epid; | ||
944 | |||
945 | for (i = 0; i < max_epid; i++) { | ||
946 | cur_epid = (start_epid + i) % max_epid; | ||
947 | if (cur_epid == hw->my_epid) | ||
948 | continue; | ||
949 | |||
950 | pstatus = fjes_hw_get_partner_ep_status(hw, cur_epid); | ||
951 | if (pstatus == EP_PARTNER_SHARED) { | ||
952 | if (!fjes_hw_epbuf_rx_is_empty( | ||
953 | &hw->ep_shm_info[cur_epid].rx)) | ||
954 | return cur_epid; | ||
955 | } | ||
956 | } | ||
957 | return -1; | ||
958 | } | ||
959 | |||
960 | static void *fjes_rxframe_get(struct fjes_adapter *adapter, size_t *psize, | ||
961 | int *cur_epid) | ||
962 | { | ||
963 | void *frame; | ||
964 | |||
965 | *cur_epid = fjes_rxframe_search_exist(adapter, *cur_epid); | ||
966 | if (*cur_epid < 0) | ||
967 | return NULL; | ||
968 | |||
969 | frame = | ||
970 | fjes_hw_epbuf_rx_curpkt_get_addr( | ||
971 | &adapter->hw.ep_shm_info[*cur_epid].rx, psize); | ||
972 | |||
973 | return frame; | ||
974 | } | ||
975 | |||
976 | static void fjes_rxframe_release(struct fjes_adapter *adapter, int cur_epid) | ||
977 | { | ||
978 | fjes_hw_epbuf_rx_curpkt_drop(&adapter->hw.ep_shm_info[cur_epid].rx); | ||
979 | } | ||
980 | |||
981 | static void fjes_rx_irq(struct fjes_adapter *adapter, int src_epid) | ||
982 | { | ||
983 | struct fjes_hw *hw = &adapter->hw; | ||
984 | |||
985 | fjes_hw_set_irqmask(hw, REG_ICTL_MASK_RX_DATA, true); | ||
986 | |||
987 | adapter->unset_rx_last = true; | ||
988 | napi_schedule(&adapter->napi); | ||
989 | } | ||
990 | |||
991 | static int fjes_poll(struct napi_struct *napi, int budget) | ||
992 | { | ||
993 | struct fjes_adapter *adapter = | ||
994 | container_of(napi, struct fjes_adapter, napi); | ||
995 | struct net_device *netdev = napi->dev; | ||
996 | struct fjes_hw *hw = &adapter->hw; | ||
997 | struct sk_buff *skb; | ||
998 | int work_done = 0; | ||
999 | int cur_epid = 0; | ||
1000 | int epidx; | ||
1001 | size_t frame_len; | ||
1002 | void *frame; | ||
1003 | |||
1004 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
1005 | if (epidx == hw->my_epid) | ||
1006 | continue; | ||
1007 | |||
1008 | adapter->hw.ep_shm_info[epidx].tx.info->v1i.rx_status |= | ||
1009 | FJES_RX_POLL_WORK; | ||
1010 | } | ||
1011 | |||
1012 | while (work_done < budget) { | ||
1013 | prefetch(&adapter->hw); | ||
1014 | frame = fjes_rxframe_get(adapter, &frame_len, &cur_epid); | ||
1015 | |||
1016 | if (frame) { | ||
1017 | skb = napi_alloc_skb(napi, frame_len); | ||
1018 | if (!skb) { | ||
1019 | adapter->stats64.rx_dropped += 1; | ||
1020 | hw->ep_shm_info[cur_epid].net_stats | ||
1021 | .rx_dropped += 1; | ||
1022 | adapter->stats64.rx_errors += 1; | ||
1023 | hw->ep_shm_info[cur_epid].net_stats | ||
1024 | .rx_errors += 1; | ||
1025 | } else { | ||
1026 | memcpy(skb_put(skb, frame_len), | ||
1027 | frame, frame_len); | ||
1028 | skb->protocol = eth_type_trans(skb, netdev); | ||
1029 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
1030 | |||
1031 | netif_receive_skb(skb); | ||
1032 | |||
1033 | work_done++; | ||
1034 | |||
1035 | adapter->stats64.rx_packets += 1; | ||
1036 | hw->ep_shm_info[cur_epid].net_stats | ||
1037 | .rx_packets += 1; | ||
1038 | adapter->stats64.rx_bytes += frame_len; | ||
1039 | hw->ep_shm_info[cur_epid].net_stats | ||
1040 | .rx_bytes += frame_len; | ||
1041 | |||
1042 | if (is_multicast_ether_addr( | ||
1043 | ((struct ethhdr *)frame)->h_dest)) { | ||
1044 | adapter->stats64.multicast += 1; | ||
1045 | hw->ep_shm_info[cur_epid].net_stats | ||
1046 | .multicast += 1; | ||
1047 | } | ||
1048 | } | ||
1049 | |||
1050 | fjes_rxframe_release(adapter, cur_epid); | ||
1051 | adapter->unset_rx_last = true; | ||
1052 | } else { | ||
1053 | break; | ||
1054 | } | ||
1055 | } | ||
1056 | |||
1057 | if (work_done < budget) { | ||
1058 | napi_complete(napi); | ||
1059 | |||
1060 | if (adapter->unset_rx_last) { | ||
1061 | adapter->rx_last_jiffies = jiffies; | ||
1062 | adapter->unset_rx_last = false; | ||
1063 | } | ||
1064 | |||
1065 | if (((long)jiffies - (long)adapter->rx_last_jiffies) < 3) { | ||
1066 | napi_reschedule(napi); | ||
1067 | } else { | ||
1068 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
1069 | if (epidx == hw->my_epid) | ||
1070 | continue; | ||
1071 | adapter->hw.ep_shm_info[epidx] | ||
1072 | .tx.info->v1i.rx_status &= | ||
1073 | ~FJES_RX_POLL_WORK; | ||
1074 | } | ||
1075 | |||
1076 | fjes_hw_set_irqmask(hw, REG_ICTL_MASK_RX_DATA, false); | ||
1077 | } | ||
1078 | } | ||
1079 | |||
1080 | return work_done; | ||
1081 | } | ||
1082 | |||
1083 | /* fjes_probe - Device Initialization Routine */ | ||
1084 | static int fjes_probe(struct platform_device *plat_dev) | ||
1085 | { | ||
1086 | struct fjes_adapter *adapter; | ||
1087 | struct net_device *netdev; | ||
1088 | struct resource *res; | ||
1089 | struct fjes_hw *hw; | ||
1090 | int err; | ||
1091 | |||
1092 | err = -ENOMEM; | ||
1093 | netdev = alloc_netdev_mq(sizeof(struct fjes_adapter), "es%d", | ||
1094 | NET_NAME_UNKNOWN, fjes_netdev_setup, | ||
1095 | FJES_MAX_QUEUES); | ||
1096 | |||
1097 | if (!netdev) | ||
1098 | goto err_out; | ||
1099 | |||
1100 | SET_NETDEV_DEV(netdev, &plat_dev->dev); | ||
1101 | |||
1102 | dev_set_drvdata(&plat_dev->dev, netdev); | ||
1103 | adapter = netdev_priv(netdev); | ||
1104 | adapter->netdev = netdev; | ||
1105 | adapter->plat_dev = plat_dev; | ||
1106 | hw = &adapter->hw; | ||
1107 | hw->back = adapter; | ||
1108 | |||
1109 | /* setup the private structure */ | ||
1110 | err = fjes_sw_init(adapter); | ||
1111 | if (err) | ||
1112 | goto err_free_netdev; | ||
1113 | |||
1114 | INIT_WORK(&adapter->force_close_task, fjes_force_close_task); | ||
1115 | adapter->force_reset = false; | ||
1116 | adapter->open_guard = false; | ||
1117 | |||
1118 | adapter->txrx_wq = create_workqueue(DRV_NAME "/txrx"); | ||
1119 | adapter->control_wq = create_workqueue(DRV_NAME "/control"); | ||
1120 | |||
1121 | INIT_WORK(&adapter->tx_stall_task, fjes_tx_stall_task); | ||
1122 | INIT_WORK(&adapter->raise_intr_rxdata_task, | ||
1123 | fjes_raise_intr_rxdata_task); | ||
1124 | INIT_WORK(&adapter->unshare_watch_task, fjes_watch_unshare_task); | ||
1125 | adapter->unshare_watch_bitmask = 0; | ||
1126 | |||
1127 | INIT_DELAYED_WORK(&adapter->interrupt_watch_task, fjes_irq_watch_task); | ||
1128 | adapter->interrupt_watch_enable = false; | ||
1129 | |||
1130 | res = platform_get_resource(plat_dev, IORESOURCE_MEM, 0); | ||
1131 | hw->hw_res.start = res->start; | ||
1132 | hw->hw_res.size = res->end - res->start + 1; | ||
1133 | hw->hw_res.irq = platform_get_irq(plat_dev, 0); | ||
1134 | err = fjes_hw_init(&adapter->hw); | ||
1135 | if (err) | ||
1136 | goto err_free_netdev; | ||
1137 | |||
1138 | /* setup MAC address (02:00:00:00:00:[epid])*/ | ||
1139 | netdev->dev_addr[0] = 2; | ||
1140 | netdev->dev_addr[1] = 0; | ||
1141 | netdev->dev_addr[2] = 0; | ||
1142 | netdev->dev_addr[3] = 0; | ||
1143 | netdev->dev_addr[4] = 0; | ||
1144 | netdev->dev_addr[5] = hw->my_epid; /* EPID */ | ||
1145 | |||
1146 | err = register_netdev(netdev); | ||
1147 | if (err) | ||
1148 | goto err_hw_exit; | ||
1149 | |||
1150 | netif_carrier_off(netdev); | ||
1151 | |||
1152 | return 0; | ||
1153 | |||
1154 | err_hw_exit: | ||
1155 | fjes_hw_exit(&adapter->hw); | ||
1156 | err_free_netdev: | ||
1157 | free_netdev(netdev); | ||
1158 | err_out: | ||
1159 | return err; | ||
1160 | } | ||
1161 | |||
1162 | /* fjes_remove - Device Removal Routine */ | ||
1163 | static int fjes_remove(struct platform_device *plat_dev) | ||
1164 | { | ||
1165 | struct net_device *netdev = dev_get_drvdata(&plat_dev->dev); | ||
1166 | struct fjes_adapter *adapter = netdev_priv(netdev); | ||
1167 | struct fjes_hw *hw = &adapter->hw; | ||
1168 | |||
1169 | cancel_delayed_work_sync(&adapter->interrupt_watch_task); | ||
1170 | cancel_work_sync(&adapter->unshare_watch_task); | ||
1171 | cancel_work_sync(&adapter->raise_intr_rxdata_task); | ||
1172 | cancel_work_sync(&adapter->tx_stall_task); | ||
1173 | if (adapter->control_wq) | ||
1174 | destroy_workqueue(adapter->control_wq); | ||
1175 | if (adapter->txrx_wq) | ||
1176 | destroy_workqueue(adapter->txrx_wq); | ||
1177 | |||
1178 | unregister_netdev(netdev); | ||
1179 | |||
1180 | fjes_hw_exit(hw); | ||
1181 | |||
1182 | netif_napi_del(&adapter->napi); | ||
1183 | |||
1184 | free_netdev(netdev); | ||
1185 | |||
1186 | return 0; | ||
1187 | } | ||
1188 | |||
1189 | static int fjes_sw_init(struct fjes_adapter *adapter) | ||
1190 | { | ||
1191 | struct net_device *netdev = adapter->netdev; | ||
1192 | |||
1193 | netif_napi_add(netdev, &adapter->napi, fjes_poll, 64); | ||
1194 | |||
1195 | return 0; | ||
1196 | } | ||
1197 | |||
1198 | /* fjes_netdev_setup - netdevice initialization routine */ | ||
1199 | static void fjes_netdev_setup(struct net_device *netdev) | ||
1200 | { | ||
1201 | ether_setup(netdev); | ||
1202 | |||
1203 | netdev->watchdog_timeo = FJES_TX_RETRY_INTERVAL; | ||
1204 | netdev->netdev_ops = &fjes_netdev_ops; | ||
1205 | fjes_set_ethtool_ops(netdev); | ||
1206 | netdev->mtu = fjes_support_mtu[0]; | ||
1207 | netdev->flags |= IFF_BROADCAST; | ||
1208 | netdev->features |= NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_FILTER; | ||
1209 | } | ||
1210 | |||
1211 | static void fjes_irq_watch_task(struct work_struct *work) | ||
1212 | { | ||
1213 | struct fjes_adapter *adapter = container_of(to_delayed_work(work), | ||
1214 | struct fjes_adapter, interrupt_watch_task); | ||
1215 | |||
1216 | local_irq_disable(); | ||
1217 | fjes_intr(adapter->hw.hw_res.irq, adapter); | ||
1218 | local_irq_enable(); | ||
1219 | |||
1220 | if (fjes_rxframe_search_exist(adapter, 0) >= 0) | ||
1221 | napi_schedule(&adapter->napi); | ||
1222 | |||
1223 | if (adapter->interrupt_watch_enable) { | ||
1224 | if (!delayed_work_pending(&adapter->interrupt_watch_task)) | ||
1225 | queue_delayed_work(adapter->control_wq, | ||
1226 | &adapter->interrupt_watch_task, | ||
1227 | FJES_IRQ_WATCH_DELAY); | ||
1228 | } | ||
1229 | } | ||
1230 | |||
1231 | static void fjes_watch_unshare_task(struct work_struct *work) | ||
1232 | { | ||
1233 | struct fjes_adapter *adapter = | ||
1234 | container_of(work, struct fjes_adapter, unshare_watch_task); | ||
1235 | |||
1236 | struct net_device *netdev = adapter->netdev; | ||
1237 | struct fjes_hw *hw = &adapter->hw; | ||
1238 | |||
1239 | int unshare_watch, unshare_reserve; | ||
1240 | int max_epid, my_epid, epidx; | ||
1241 | int stop_req, stop_req_done; | ||
1242 | ulong unshare_watch_bitmask; | ||
1243 | int wait_time = 0; | ||
1244 | int is_shared; | ||
1245 | int ret; | ||
1246 | |||
1247 | my_epid = hw->my_epid; | ||
1248 | max_epid = hw->max_epid; | ||
1249 | |||
1250 | unshare_watch_bitmask = adapter->unshare_watch_bitmask; | ||
1251 | adapter->unshare_watch_bitmask = 0; | ||
1252 | |||
1253 | while ((unshare_watch_bitmask || hw->txrx_stop_req_bit) && | ||
1254 | (wait_time < 3000)) { | ||
1255 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
1256 | if (epidx == hw->my_epid) | ||
1257 | continue; | ||
1258 | |||
1259 | is_shared = fjes_hw_epid_is_shared(hw->hw_info.share, | ||
1260 | epidx); | ||
1261 | |||
1262 | stop_req = test_bit(epidx, &hw->txrx_stop_req_bit); | ||
1263 | |||
1264 | stop_req_done = hw->ep_shm_info[epidx].rx.info->v1i.rx_status & | ||
1265 | FJES_RX_STOP_REQ_DONE; | ||
1266 | |||
1267 | unshare_watch = test_bit(epidx, &unshare_watch_bitmask); | ||
1268 | |||
1269 | unshare_reserve = test_bit(epidx, | ||
1270 | &hw->hw_info.buffer_unshare_reserve_bit); | ||
1271 | |||
1272 | if ((!stop_req || | ||
1273 | (is_shared && (!is_shared || !stop_req_done))) && | ||
1274 | (is_shared || !unshare_watch || !unshare_reserve)) | ||
1275 | continue; | ||
1276 | |||
1277 | mutex_lock(&hw->hw_info.lock); | ||
1278 | ret = fjes_hw_unregister_buff_addr(hw, epidx); | ||
1279 | switch (ret) { | ||
1280 | case 0: | ||
1281 | break; | ||
1282 | case -ENOMSG: | ||
1283 | case -EBUSY: | ||
1284 | default: | ||
1285 | if (!work_pending( | ||
1286 | &adapter->force_close_task)) { | ||
1287 | adapter->force_reset = true; | ||
1288 | schedule_work( | ||
1289 | &adapter->force_close_task); | ||
1290 | } | ||
1291 | break; | ||
1292 | } | ||
1293 | mutex_unlock(&hw->hw_info.lock); | ||
1294 | |||
1295 | fjes_hw_setup_epbuf(&hw->ep_shm_info[epidx].tx, | ||
1296 | netdev->dev_addr, netdev->mtu); | ||
1297 | |||
1298 | clear_bit(epidx, &hw->txrx_stop_req_bit); | ||
1299 | clear_bit(epidx, &unshare_watch_bitmask); | ||
1300 | clear_bit(epidx, | ||
1301 | &hw->hw_info.buffer_unshare_reserve_bit); | ||
1302 | } | ||
1303 | |||
1304 | msleep(100); | ||
1305 | wait_time += 100; | ||
1306 | } | ||
1307 | |||
1308 | if (hw->hw_info.buffer_unshare_reserve_bit) { | ||
1309 | for (epidx = 0; epidx < hw->max_epid; epidx++) { | ||
1310 | if (epidx == hw->my_epid) | ||
1311 | continue; | ||
1312 | |||
1313 | if (test_bit(epidx, | ||
1314 | &hw->hw_info.buffer_unshare_reserve_bit)) { | ||
1315 | mutex_lock(&hw->hw_info.lock); | ||
1316 | |||
1317 | ret = fjes_hw_unregister_buff_addr(hw, epidx); | ||
1318 | switch (ret) { | ||
1319 | case 0: | ||
1320 | break; | ||
1321 | case -ENOMSG: | ||
1322 | case -EBUSY: | ||
1323 | default: | ||
1324 | if (!work_pending( | ||
1325 | &adapter->force_close_task)) { | ||
1326 | adapter->force_reset = true; | ||
1327 | schedule_work( | ||
1328 | &adapter->force_close_task); | ||
1329 | } | ||
1330 | break; | ||
1331 | } | ||
1332 | mutex_unlock(&hw->hw_info.lock); | ||
1333 | |||
1334 | fjes_hw_setup_epbuf( | ||
1335 | &hw->ep_shm_info[epidx].tx, | ||
1336 | netdev->dev_addr, netdev->mtu); | ||
1337 | |||
1338 | clear_bit(epidx, &hw->txrx_stop_req_bit); | ||
1339 | clear_bit(epidx, &unshare_watch_bitmask); | ||
1340 | clear_bit(epidx, &hw->hw_info.buffer_unshare_reserve_bit); | ||
1341 | } | ||
1342 | |||
1343 | if (test_bit(epidx, &unshare_watch_bitmask)) { | ||
1344 | hw->ep_shm_info[epidx].tx.info->v1i.rx_status &= | ||
1345 | ~FJES_RX_STOP_REQ_DONE; | ||
1346 | } | ||
1347 | } | ||
1348 | } | ||
1349 | } | ||
1350 | |||
1351 | /* fjes_init_module - Driver Registration Routine */ | ||
1352 | static int __init fjes_init_module(void) | ||
1353 | { | ||
1354 | int result; | ||
1355 | |||
1356 | pr_info("%s - version %s - %s\n", | ||
1357 | fjes_driver_string, fjes_driver_version, fjes_copyright); | ||
1358 | |||
1359 | result = platform_driver_register(&fjes_driver); | ||
1360 | if (result < 0) | ||
1361 | return result; | ||
1362 | |||
1363 | result = acpi_bus_register_driver(&fjes_acpi_driver); | ||
1364 | if (result < 0) | ||
1365 | goto fail_acpi_driver; | ||
1366 | |||
1367 | return 0; | ||
1368 | |||
1369 | fail_acpi_driver: | ||
1370 | platform_driver_unregister(&fjes_driver); | ||
1371 | return result; | ||
1372 | } | ||
1373 | |||
1374 | module_init(fjes_init_module); | ||
1375 | |||
1376 | /* fjes_exit_module - Driver Exit Cleanup Routine */ | ||
1377 | static void __exit fjes_exit_module(void) | ||
1378 | { | ||
1379 | acpi_bus_unregister_driver(&fjes_acpi_driver); | ||
1380 | platform_driver_unregister(&fjes_driver); | ||
1381 | } | ||
1382 | |||
1383 | module_exit(fjes_exit_module); | ||
diff --git a/drivers/net/fjes/fjes_regs.h b/drivers/net/fjes/fjes_regs.h new file mode 100644 index 000000000000..029c924dc175 --- /dev/null +++ b/drivers/net/fjes/fjes_regs.h | |||
@@ -0,0 +1,142 @@ | |||
1 | /* | ||
2 | * FUJITSU Extended Socket Network Device driver | ||
3 | * Copyright (c) 2015 FUJITSU LIMITED | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, see <http://www.gnu.org/licenses/>. | ||
16 | * | ||
17 | * The full GNU General Public License is included in this distribution in | ||
18 | * the file called "COPYING". | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef FJES_REGS_H_ | ||
23 | #define FJES_REGS_H_ | ||
24 | |||
25 | #include <linux/bitops.h> | ||
26 | |||
27 | #define XSCT_DEVICE_REGISTER_SIZE 0x1000 | ||
28 | |||
29 | /* register offset */ | ||
30 | /* Information registers */ | ||
31 | #define XSCT_OWNER_EPID 0x0000 /* Owner EPID */ | ||
32 | #define XSCT_MAX_EP 0x0004 /* Maximum EP */ | ||
33 | |||
34 | /* Device Control registers */ | ||
35 | #define XSCT_DCTL 0x0010 /* Device Control */ | ||
36 | |||
37 | /* Command Control registers */ | ||
38 | #define XSCT_CR 0x0020 /* Command request */ | ||
39 | #define XSCT_CS 0x0024 /* Command status */ | ||
40 | #define XSCT_SHSTSAL 0x0028 /* Share status address Low */ | ||
41 | #define XSCT_SHSTSAH 0x002C /* Share status address High */ | ||
42 | |||
43 | #define XSCT_REQBL 0x0034 /* Request Buffer length */ | ||
44 | #define XSCT_REQBAL 0x0038 /* Request Buffer Address Low */ | ||
45 | #define XSCT_REQBAH 0x003C /* Request Buffer Address High */ | ||
46 | |||
47 | #define XSCT_RESPBL 0x0044 /* Response Buffer Length */ | ||
48 | #define XSCT_RESPBAL 0x0048 /* Response Buffer Address Low */ | ||
49 | #define XSCT_RESPBAH 0x004C /* Response Buffer Address High */ | ||
50 | |||
51 | /* Interrupt Control registers */ | ||
52 | #define XSCT_IS 0x0080 /* Interrupt status */ | ||
53 | #define XSCT_IMS 0x0084 /* Interrupt mask set */ | ||
54 | #define XSCT_IMC 0x0088 /* Interrupt mask clear */ | ||
55 | #define XSCT_IG 0x008C /* Interrupt generator */ | ||
56 | #define XSCT_ICTL 0x0090 /* Interrupt control */ | ||
57 | |||
58 | /* register structure */ | ||
59 | /* Information registers */ | ||
60 | union REG_OWNER_EPID { | ||
61 | struct { | ||
62 | __le32 epid:16; | ||
63 | __le32:16; | ||
64 | } bits; | ||
65 | __le32 reg; | ||
66 | }; | ||
67 | |||
68 | union REG_MAX_EP { | ||
69 | struct { | ||
70 | __le32 maxep:16; | ||
71 | __le32:16; | ||
72 | } bits; | ||
73 | __le32 reg; | ||
74 | }; | ||
75 | |||
76 | /* Device Control registers */ | ||
77 | union REG_DCTL { | ||
78 | struct { | ||
79 | __le32 reset:1; | ||
80 | __le32 rsv0:15; | ||
81 | __le32 rsv1:16; | ||
82 | } bits; | ||
83 | __le32 reg; | ||
84 | }; | ||
85 | |||
86 | /* Command Control registers */ | ||
87 | union REG_CR { | ||
88 | struct { | ||
89 | __le32 req_code:16; | ||
90 | __le32 err_info:14; | ||
91 | __le32 error:1; | ||
92 | __le32 req_start:1; | ||
93 | } bits; | ||
94 | __le32 reg; | ||
95 | }; | ||
96 | |||
97 | union REG_CS { | ||
98 | struct { | ||
99 | __le32 req_code:16; | ||
100 | __le32 rsv0:14; | ||
101 | __le32 busy:1; | ||
102 | __le32 complete:1; | ||
103 | } bits; | ||
104 | __le32 reg; | ||
105 | }; | ||
106 | |||
107 | /* Interrupt Control registers */ | ||
108 | union REG_ICTL { | ||
109 | struct { | ||
110 | __le32 automak:1; | ||
111 | __le32 rsv0:31; | ||
112 | } bits; | ||
113 | __le32 reg; | ||
114 | }; | ||
115 | |||
116 | enum REG_ICTL_MASK { | ||
117 | REG_ICTL_MASK_INFO_UPDATE = 1 << 20, | ||
118 | REG_ICTL_MASK_DEV_STOP_REQ = 1 << 19, | ||
119 | REG_ICTL_MASK_TXRX_STOP_REQ = 1 << 18, | ||
120 | REG_ICTL_MASK_TXRX_STOP_DONE = 1 << 17, | ||
121 | REG_ICTL_MASK_RX_DATA = 1 << 16, | ||
122 | REG_ICTL_MASK_ALL = GENMASK(20, 16), | ||
123 | }; | ||
124 | |||
125 | enum REG_IS_MASK { | ||
126 | REG_IS_MASK_IS_ASSERT = 1 << 31, | ||
127 | REG_IS_MASK_EPID = GENMASK(15, 0), | ||
128 | }; | ||
129 | |||
130 | struct fjes_hw; | ||
131 | |||
132 | u32 fjes_hw_rd32(struct fjes_hw *hw, u32 reg); | ||
133 | |||
134 | #define wr32(reg, val) \ | ||
135 | do { \ | ||
136 | u8 *base = hw->base; \ | ||
137 | writel((val), &base[(reg)]); \ | ||
138 | } while (0) | ||
139 | |||
140 | #define rd32(reg) (fjes_hw_rd32(hw, reg)) | ||
141 | |||
142 | #endif /* FJES_REGS_H_ */ | ||