From 3b51cec9a38d964b1c9fbd67d44932f2714fd98f Mon Sep 17 00:00:00 2001 From: Andriy Kokhan <43479230+akokhan@users.noreply.github.com> Date: Thu, 7 Nov 2019 04:54:05 +0200 Subject: [PATCH] [barefoot] Added Newport platform support (#3709) [barefoot] Added Newport platform support Signed-off-by: Andriy Kokhan --- .../x86_64-accton_as9516bf_32d-r0/default_sku | 1 + .../installer.conf | 1 + .../newport/buffers.json.j2 | 2 + .../newport/buffers_defaults_t0.j2 | 71 + .../newport/buffers_defaults_t1.j2 | 71 + .../newport/pg_profile_lookup.ini | 20 + .../newport/port_config.ini | 33 + .../newport/qos.json.j2 | 10 + .../newport/sai.profile | 3 + .../newport/switch-tna-sai.conf | 40 + .../x86_64-accton_as9516bf_32d-r0/plugins | 1 + .../pmon_daemon_control.json | 6 + platform/barefoot/one-image.mk | 1 + .../barefoot/platform-modules-bfn-newport.mk | 13 + platform/barefoot/rules.mk | 1 + .../LICENSE | 15 + .../MAINTAINERS | 3 + .../README.md | 2 + .../configs/network/interfaces.d/usb0 | 5 + .../debian/changelog | 5 + .../debian/compat | 1 + .../debian/control | 12 + .../debian/copyright | 15 + .../debian/rules | 35 + .../modules/Makefile | 2 + .../modules/bf_fpga_ioctl.c | 196 +++ .../modules/bf_fpga_ioctl.h | 131 ++ .../modules/bf_fpga_main.c | 1294 +++++++++++++++++ .../modules/bf_fpga_priv.h | 141 ++ .../modules/bf_fpga_sysfs.c | 335 +++++ .../modules/i2c/bf_fpga_i2c.c | 516 +++++++ .../modules/i2c/bf_fpga_i2c.h | 55 + .../modules/i2c/bf_fpga_i2c_ctrl.c | 397 +++++ .../modules/i2c/bf_fpga_i2c_porting.c | 150 ++ .../modules/i2c/bf_fpga_i2c_priv.h | 104 ++ .../modules/i2c/bf_fpga_i2c_priv_porting.h | 93 ++ .../modules/i2c/bf_fpga_i2c_reg.h | 116 ++ .../scripts/bf-sfputil | 10 + .../scripts/eeprom | 10 + .../scripts/fancontrol | 11 + .../scripts/ps_info | 10 + .../scripts/sensors | 12 + .../scripts/test | 1 + 43 files changed, 3951 insertions(+) create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/default_sku create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/installer.conf create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers.json.j2 create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers_defaults_t0.j2 create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers_defaults_t1.j2 create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/pg_profile_lookup.ini create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/port_config.ini create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/qos.json.j2 create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/sai.profile create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/switch-tna-sai.conf create mode 120000 device/barefoot/x86_64-accton_as9516bf_32d-r0/plugins create mode 100644 device/barefoot/x86_64-accton_as9516bf_32d-r0/pmon_daemon_control.json create mode 100644 platform/barefoot/platform-modules-bfn-newport.mk create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/LICENSE create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/MAINTAINERS create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/README.md create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/configs/network/interfaces.d/usb0 create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/debian/changelog create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/debian/compat create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/debian/control create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/debian/copyright create mode 100755 platform/barefoot/sonic-platform-modules-bfn-newport/debian/rules create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/Makefile create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_ioctl.c create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_ioctl.h create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_main.c create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_priv.h create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_sysfs.c create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c.c create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c.h create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_ctrl.c create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_porting.c create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_priv.h create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_priv_porting.h create mode 100644 platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_reg.h create mode 100755 platform/barefoot/sonic-platform-modules-bfn-newport/scripts/bf-sfputil create mode 100755 platform/barefoot/sonic-platform-modules-bfn-newport/scripts/eeprom create mode 100755 platform/barefoot/sonic-platform-modules-bfn-newport/scripts/fancontrol create mode 100755 platform/barefoot/sonic-platform-modules-bfn-newport/scripts/ps_info create mode 100755 platform/barefoot/sonic-platform-modules-bfn-newport/scripts/sensors create mode 100755 platform/barefoot/sonic-platform-modules-bfn-newport/scripts/test diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/default_sku b/device/barefoot/x86_64-accton_as9516bf_32d-r0/default_sku new file mode 100644 index 0000000000..517fa893ae --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/default_sku @@ -0,0 +1 @@ +newport t1 diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/installer.conf b/device/barefoot/x86_64-accton_as9516bf_32d-r0/installer.conf new file mode 100644 index 0000000000..3714ff053b --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/installer.conf @@ -0,0 +1 @@ +CONSOLE_SPEED=57600 diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers.json.j2 b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers.json.j2 new file mode 100644 index 0000000000..1083a6210f --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers.json.j2 @@ -0,0 +1,2 @@ +{%- set default_topo = 't0' %} +{%- include 'buffers_config.j2' %} diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers_defaults_t0.j2 b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers_defaults_t0.j2 new file mode 100644 index 0000000000..199f4ad135 --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers_defaults_t0.j2 @@ -0,0 +1,71 @@ +{% set default_cable = '5m' %} +{% set ingress_lossless_pool_size = '4194304' %} +{% set ingress_lossy_pool_size = '7340032' %} +{% set egress_lossless_pool_size = '16777152' %} +{% set egress_lossy_pool_size = '7340032' %} + +{%- macro generate_port_lists(PORT_ALL) %} + {# Generate list of ports #} + {%- for port_idx in range(0,32) %} + {%- if PORT_ALL.append("Ethernet%d" % (port_idx * 4)) %}{%- endif %} + {%- endfor %} +{%- endmacro %} + +{%- macro generate_buffer_pool_and_profiles() %} + "BUFFER_POOL": { + "ingress_lossless_pool": { + "size": "{{ ingress_lossless_pool_size }}", + "type": "ingress", + "mode": "dynamic", + "xoff": "2867200" + }, + "ingress_lossy_pool": { + "size": "{{ ingress_lossy_pool_size }}", + "type": "ingress", + "mode": "dynamic" + }, + "egress_lossless_pool": { + "size": "{{ egress_lossless_pool_size }}", + "type": "egress", + "mode": "dynamic" + }, + "egress_lossy_pool": { + "size": "{{ egress_lossy_pool_size }}", + "type": "egress", + "mode": "dynamic" + } + }, + "BUFFER_PROFILE": { + "ingress_lossy_profile": { + "pool":"[BUFFER_POOL|ingress_lossy_pool]", + "size":"4096", + "dynamic_th":"3" + }, + "egress_lossless_profile": { + "pool":"[BUFFER_POOL|egress_lossless_pool]", + "size":"0", + "dynamic_th":"7" + }, + "egress_lossy_profile": { + "pool":"[BUFFER_POOL|egress_lossy_pool]", + "size":"4096", + "dynamic_th":"3" + }, + "q_lossy_profile": { + "pool":"[BUFFER_POOL|egress_lossy_pool]", + "size":"4096", + "dynamic_th":"3" + } + }, +{%- endmacro %} + +{%- macro generate_queue_buffers(port_names) %} + "BUFFER_QUEUE": { + "{{ port_names }}|3-4": { + "profile" : "[BUFFER_PROFILE|egress_lossless_profile]" + }, + "{{ port_names }}|0-1": { + "profile" : "[BUFFER_PROFILE|q_lossy_profile]" + } + } +{%- endmacro %} diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers_defaults_t1.j2 b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers_defaults_t1.j2 new file mode 100644 index 0000000000..01f50a4419 --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/buffers_defaults_t1.j2 @@ -0,0 +1,71 @@ +{% set default_cable = '5m' %} +{% set ingress_lossless_pool_size = '2097152' %} +{% set ingress_lossy_pool_size = '5242880' %} +{% set egress_lossless_pool_size = '16777152' %} +{% set egress_lossy_pool_size = '5242880' %} + +{%- macro generate_port_lists(PORT_ALL) %} + {# Generate list of ports #} + {%- for port_idx in range(0,32) %} + {%- if PORT_ALL.append("Ethernet%d" % (port_idx * 4)) %}{%- endif %} + {%- endfor %} +{%- endmacro %} + +{%- macro generate_buffer_pool_and_profiles() %} + "BUFFER_POOL": { + "ingress_lossless_pool": { + "size": "{{ ingress_lossless_pool_size }}", + "type": "ingress", + "mode": "dynamic", + "xoff": "2867200" + }, + "ingress_lossy_pool": { + "size": "{{ ingress_lossy_pool_size }}", + "type": "ingress", + "mode": "dynamic" + }, + "egress_lossless_pool": { + "size": "{{ egress_lossless_pool_size }}", + "type": "egress", + "mode": "dynamic" + }, + "egress_lossy_pool": { + "size": "{{ egress_lossy_pool_size }}", + "type": "egress", + "mode": "dynamic" + } + }, + "BUFFER_PROFILE": { + "ingress_lossy_profile": { + "pool":"[BUFFER_POOL|ingress_lossy_pool]", + "size":"4096", + "dynamic_th":"3" + }, + "egress_lossless_profile": { + "pool":"[BUFFER_POOL|egress_lossless_pool]", + "size":"0", + "dynamic_th":"7" + }, + "egress_lossy_profile": { + "pool":"[BUFFER_POOL|egress_lossy_pool]", + "size":"4096", + "dynamic_th":"3" + }, + "q_lossy_profile": { + "pool":"[BUFFER_POOL|egress_lossy_pool]", + "size":"4096", + "dynamic_th":"3" + } + }, +{%- endmacro %} + +{%- macro generate_queue_buffers(port_names) %} + "BUFFER_QUEUE": { + "{{ port_names }}|3-4": { + "profile" : "[BUFFER_PROFILE|egress_lossless_profile]" + }, + "{{ port_names }}|0-1": { + "profile" : "[BUFFER_PROFILE|q_lossy_profile]" + } + } +{%- endmacro %} diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/pg_profile_lookup.ini b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/pg_profile_lookup.ini new file mode 100644 index 0000000000..602400325b --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/pg_profile_lookup.ini @@ -0,0 +1,20 @@ +# PG lossless profiles. +# speed cable size xon xoff threshold + 10000 5m 34816 18432 16384 7 + 25000 5m 34816 18432 16384 7 + 40000 5m 34816 18432 16384 7 + 50000 5m 34816 18432 16384 7 + 100000 5m 36864 18432 18432 7 + 400000 5m 36864 18432 18432 7 + 10000 40m 36864 18432 18432 7 + 25000 40m 39936 18432 21504 7 + 40000 40m 41984 18432 23552 7 + 50000 40m 41984 18432 23552 7 + 100000 40m 54272 18432 35840 7 + 400000 40m 54272 18432 35840 7 + 10000 300m 49152 18432 30720 7 + 25000 300m 71680 18432 53248 7 + 40000 300m 94208 18432 75776 7 + 50000 300m 94208 18432 75776 7 + 100000 300m 184320 18432 165888 7 + 400000 300m 184320 18432 165888 7 diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/port_config.ini b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/port_config.ini new file mode 100644 index 0000000000..3a3be79dc6 --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/port_config.ini @@ -0,0 +1,33 @@ +# name lanes alias index speed autoneg fec +Ethernet0 0,1,2,3,4,5,6,7 Ethernet0 1 400000 0 rs +Ethernet8 8,9,10,11,12,13,14,15 Ethernet8 2 400000 0 rs +Ethernet16 16,17,18,19,20,21,22,23 Ethernet16 3 400000 0 rs +Ethernet24 24,25,26,27,28,29,30,31 Ethernet24 4 400000 0 rs +Ethernet32 32,33,34,35,36,37,38,39 Ethernet32 5 400000 0 rs +Ethernet40 40,41,42,43,44,45,46,47 Ethernet40 6 400000 0 rs +Ethernet48 48,49,50,51,52,53,54,55 Ethernet48 7 400000 0 rs +Ethernet56 56,57,58,59,60,61,62,63 Ethernet56 8 400000 0 rs +Ethernet64 64,65,66,67,68,69,70,71 Ethernet64 9 400000 0 rs +Ethernet72 72,73,74,75,76,77,78,79 Ethernet72 10 400000 0 rs +Ethernet80 80,81,82,83,84,85,86,87 Ethernet80 11 400000 0 rs +Ethernet88 88,89,90,91,92,93,94,95 Ethernet88 12 400000 0 rs +Ethernet96 96,97,98,99,100,101,102,103 Ethernet96 13 400000 0 rs +Ethernet104 104,105,106,107,108,109,110,111 Ethernet104 14 400000 0 rs +Ethernet112 112,113,114,115,116,117,118,119 Ethernet112 15 400000 0 rs +Ethernet120 120,121,122,123,124,125,126,127 Ethernet120 16 400000 0 rs +Ethernet128 128,129,130,131,132,133,134,135 Ethernet128 17 400000 0 rs +Ethernet136 136,137,138,139,140,141,142,143 Ethernet136 18 400000 0 rs +Ethernet144 144,145,146,147,148,149,150,151 Ethernet144 19 400000 0 rs +Ethernet152 152,153,154,155,156,157,158,159 Ethernet152 20 400000 0 rs +Ethernet160 160,161,162,163,164,165,166,167 Ethernet160 21 400000 0 rs +Ethernet168 168,169,170,171,172,173,174,175 Ethernet168 22 400000 0 rs +Ethernet176 176,177,178,179,180,181,182,183 Ethernet176 23 400000 0 rs +Ethernet184 184,185,186,187,188,189,190,191 Ethernet184 24 400000 0 rs +Ethernet192 192,193,194,195,196,197,198,199 Ethernet192 25 400000 0 rs +Ethernet200 200,201,202,203,204,205,206,207 Ethernet200 26 400000 0 rs +Ethernet208 208,209,210,211,212,213,214,215 Ethernet208 27 400000 0 rs +Ethernet216 216,217,218,219,220,221,222,223 Ethernet216 28 400000 0 rs +Ethernet224 224,225,226,227,228,229,230,231 Ethernet224 29 400000 0 rs +Ethernet232 232,233,234,235,236,237,238,239 Ethernet232 30 400000 0 rs +Ethernet240 240,241,242,243,244,245,246,247 Ethernet240 31 400000 0 rs +Ethernet248 248,249,250,251,252,253,254,255 Ethernet248 32 400000 0 rs diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/qos.json.j2 b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/qos.json.j2 new file mode 100644 index 0000000000..a685277448 --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/qos.json.j2 @@ -0,0 +1,10 @@ +{%- macro generate_tc_to_pg_map() %} + "TC_TO_PRIORITY_GROUP_MAP": { + "AZURE": { + "3": "3", + "4": "4" + } + }, +{%- endmacro %} + +{%- include 'qos_config.j2' %} diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/sai.profile b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/sai.profile new file mode 100644 index 0000000000..037b5c1353 --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/sai.profile @@ -0,0 +1,3 @@ +SAI_KEY_WARM_BOOT_WRITE_FILE=/var/warmboot/sai-warmboot.bin +SAI_KEY_WARM_BOOT_READ_FILE=/var/warmboot/sai-warmboot.bin + diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/switch-tna-sai.conf b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/switch-tna-sai.conf new file mode 100644 index 0000000000..a1ee06da87 --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/newport/switch-tna-sai.conf @@ -0,0 +1,40 @@ +{ + "instance": 0, + "chip_list": [ + { + "id": "asic-0", + "chip_family": "Tofino2", + "instance": 0, + "pcie_sysfs_prefix": "/sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0", + "pcie_domain": 0, + "pcie_bus": 5, + "pcie_fn": 0, + "pcie_dev": 0, + "pcie_int_mode": 1, + "sds_fw_path": "share/tofino_sds_fw/avago/firmware" + } + ], + "p4_devices": [ + { + "device-id": 0, + "agent0": "lib/platform/x86_64-accton_as9516bf_32d-r0/libpltfm_mgr.so", + "p4_programs": [ + { + "p4_pipelines": [ + { + "p4_pipeline_name": "pipe", + "config": "share/switch/pipe/tofino2.bin", + "context": "share/switch/pipe/context.json" + } + ], + "program-name": "switch", + "switchsai": "lib/libswitchsai.so", + "bfrt-config": "share/switch/bf-rt.json", + "model_json_path" : "share/switch/aug_model.json", + "switchapi_port_add": false, + "non_default_port_ppgs": 5 + } + ] + } + ] +} diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/plugins b/device/barefoot/x86_64-accton_as9516bf_32d-r0/plugins new file mode 120000 index 0000000000..a8464d1ec2 --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/plugins @@ -0,0 +1 @@ +../x86_64-accton_wedge100bf_32x-r0/plugins/ \ No newline at end of file diff --git a/device/barefoot/x86_64-accton_as9516bf_32d-r0/pmon_daemon_control.json b/device/barefoot/x86_64-accton_as9516bf_32d-r0/pmon_daemon_control.json new file mode 100644 index 0000000000..3a76f2fdd0 --- /dev/null +++ b/device/barefoot/x86_64-accton_as9516bf_32d-r0/pmon_daemon_control.json @@ -0,0 +1,6 @@ +{ + "skip_ledd": true, + "skip_xcvrd": false, + "skip_psud": false, + "skip_syseepromd": false +} diff --git a/platform/barefoot/one-image.mk b/platform/barefoot/one-image.mk index 36823baa87..0147b62d00 100644 --- a/platform/barefoot/one-image.mk +++ b/platform/barefoot/one-image.mk @@ -7,6 +7,7 @@ $(SONIC_ONE_IMAGE)_INSTALLS += $(BFN_MODULE) $(PYTHON_THRIFT) $(SONIC_ONE_IMAGE)_INSTALLS += $(SYSTEMD_SONIC_GENERATOR) $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(BFN_PLATFORM_MODULE) $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(BFN_MONTARA_PLATFORM_MODULE) +$(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(BFN_NEWPORT_PLATFORM_MODULE) $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(WNC_OSW1800_PLATFORM_MODULE) $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(INGRASYS_S9180_32X_PLATFORM_MODULE) $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(INGRASYS_S9280_64X_PLATFORM_MODULE) diff --git a/platform/barefoot/platform-modules-bfn-newport.mk b/platform/barefoot/platform-modules-bfn-newport.mk new file mode 100644 index 0000000000..ff251ed1dd --- /dev/null +++ b/platform/barefoot/platform-modules-bfn-newport.mk @@ -0,0 +1,13 @@ +# BFN Platform modules + +BFN_NEWPORT_PLATFORM_MODULE_VERSION = 1.0 + +export BFN_NEWPORT_PLATFORM_MODULE_VERSION + +BFN_NEWPORT_PLATFORM_MODULE = sonic-platform-modules-bfn-newport_$(BFN_NEWPORT_PLATFORM_MODULE_VERSION)_amd64.deb +$(BFN_NEWPORT_PLATFORM_MODULE)_SRC_PATH = $(PLATFORM_PATH)/sonic-platform-modules-bfn-newport +$(BFN_NEWPORT_PLATFORM_MODULE)_DEPENDS += $(LINUX_HEADERS) $(LINUX_HEADERS_COMMON) +$(BFN_NEWPORT_PLATFORM_MODULE)_PLATFORM = x86_64-accton_as9516bf_32d-r0 +SONIC_DPKG_DEBS += $(BFN_NEWPORT_PLATFORM_MODULE) + +SONIC_STRETCH_DEBS += $(BFN_NEWPORT_PLATFORM_MODULE) diff --git a/platform/barefoot/rules.mk b/platform/barefoot/rules.mk index ea718d10fc..8a218b3ff1 100644 --- a/platform/barefoot/rules.mk +++ b/platform/barefoot/rules.mk @@ -1,6 +1,7 @@ include $(PLATFORM_PATH)/platform-modules-arista.mk include $(PLATFORM_PATH)/platform-modules-bfn.mk include $(PLATFORM_PATH)/platform-modules-bfn-montara.mk +include $(PLATFORM_PATH)/platform-modules-bfn-newport.mk include $(PLATFORM_PATH)/platform-modules-wnc-osw1800.mk include $(PLATFORM_PATH)/platform-modules-ingrasys.mk include $(PLATFORM_PATH)/bfn-sai.mk diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/LICENSE b/platform/barefoot/sonic-platform-modules-bfn-newport/LICENSE new file mode 100644 index 0000000000..676cdeec72 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/LICENSE @@ -0,0 +1,15 @@ +Copyright (C) 2016 Microsoft, Inc + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/MAINTAINERS b/platform/barefoot/sonic-platform-modules-bfn-newport/MAINTAINERS new file mode 100644 index 0000000000..85aa0cd77d --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/MAINTAINERS @@ -0,0 +1,3 @@ +# This file describes the maintainers for sonic-platform-modules-bfn-newport +# See the SONiC project governance document for more information +Mailinglist = sonicproject@googlegroups.com diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/README.md b/platform/barefoot/sonic-platform-modules-bfn-newport/README.md new file mode 100644 index 0000000000..028adef715 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/README.md @@ -0,0 +1,2 @@ +# sonic-platform-modules-bfn-newport +Device drivers for support of BFN platform for the SONiC project diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/configs/network/interfaces.d/usb0 b/platform/barefoot/sonic-platform-modules-bfn-newport/configs/network/interfaces.d/usb0 new file mode 100644 index 0000000000..f1dd054cc6 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/configs/network/interfaces.d/usb0 @@ -0,0 +1,5 @@ +# BMC interface +auto usb0 +allow-hotplug usb0 +iface usb0 inet6 +up ifconfig usb0 txqueuelen 64 diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/debian/changelog b/platform/barefoot/sonic-platform-modules-bfn-newport/debian/changelog new file mode 100644 index 0000000000..98ecad42ac --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/debian/changelog @@ -0,0 +1,5 @@ +sonic-platform-modules-bfn-newport (1.0) unstable; urgency=low + + * Initial release + + -- Support Mon, 27 Sep 2019 18:00:00 -0800 diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/debian/compat b/platform/barefoot/sonic-platform-modules-bfn-newport/debian/compat new file mode 100644 index 0000000000..45a4fb75db --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/debian/compat @@ -0,0 +1 @@ +8 diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/debian/control b/platform/barefoot/sonic-platform-modules-bfn-newport/debian/control new file mode 100644 index 0000000000..edbc5b890c --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/debian/control @@ -0,0 +1,12 @@ +Source: sonic-platform-modules-bfn-newport +Section: main +Priority: extra +Maintainer: Support +Build-Depends: debhelper (>= 8.0.0), bzip2 +Standards-Version: 3.9.3 + +Package: sonic-platform-modules-bfn-newport +Architecture: amd64 +Depends: linux-image-4.9.0-9-2-amd64 +Description: kernel module for bfn platform fpga and scripts for the devices such as fan, led, sfp + diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/debian/copyright b/platform/barefoot/sonic-platform-modules-bfn-newport/debian/copyright new file mode 100644 index 0000000000..ade42b7aa7 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/debian/copyright @@ -0,0 +1,15 @@ +Provides linux kernel driver for BF PCIe devices + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/debian/rules b/platform/barefoot/sonic-platform-modules-bfn-newport/debian/rules new file mode 100755 index 0000000000..951069a601 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/debian/rules @@ -0,0 +1,35 @@ +#!/usr/bin/make -f + +export INSTALL_MOD_DIR:=extra + +PACKAGE_NAME := sonic-platform-modules-bfn-newport +KVERSION ?= $(shell uname -r) +KERNEL_SRC := /lib/modules/$(KVERSION) +MODULE_SRC := $(shell pwd)/modules +SCRIPT_SRC := $(shell pwd)/scripts +CONFIGS_SRC := $(shell pwd)/configs + +%: + dh $@ + +override_dh_auto_build: + make -C $(KERNEL_SRC)/build M=$(MODULE_SRC) + +override_dh_auto_install: + dh_installdirs -p$(PACKAGE_NAME) $(KERNEL_SRC)/$(INSTALL_MOD_DIR) + cp $(MODULE_SRC)/*.ko debian/$(PACKAGE_NAME)/$(KERNEL_SRC)/$(INSTALL_MOD_DIR) + dh_installdirs -p$(PACKAGE_NAME) usr/local/bin + cp -r $(SCRIPT_SRC)/* debian/$(PACKAGE_NAME)/usr/local/bin + dh_installdirs -p$(PACKAGE_NAME) etc/network/interfaces.d/ + cp -r $(CONFIGS_SRC)/network/interfaces.d/* debian/$(PACKAGE_NAME)/etc/network/interfaces.d/ + +override_dh_usrlocal: + +override_dh_pysupport: + +override_dh_clean: + dh_clean + rm -f $(MODULE_SRC)/*.o $(MODULE_SRC)/*.ko $(MODULE_SRC)/*.mod.c $(MODULE_SRC)/.*.cmd + rm -f $(MODULE_SRC)/Module.markers $(MODULE_SRC)/Module.symvers $(MODULE_SRC)/modules.order + rm -rf $(MODULE_SRC)/.tmp_versions + diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/Makefile b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/Makefile new file mode 100644 index 0000000000..732ffb1f3f --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/Makefile @@ -0,0 +1,2 @@ +obj-m := bf_fpga.o +bf_fpga-y := bf_fpga_main.o bf_fpga_ioctl.o bf_fpga_sysfs.o i2c/bf_fpga_i2c.o i2c/bf_fpga_i2c_ctrl.o i2c/bf_fpga_i2c_porting.o diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_ioctl.c b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_ioctl.c new file mode 100644 index 0000000000..b0792556ca --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_ioctl.c @@ -0,0 +1,196 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "bf_fpga_priv.h" +#include "bf_fpga_ioctl.h" +#include "i2c/bf_fpga_i2c.h" + +int bf_fpga_ioctl(struct bf_pci_dev *bfdev, + unsigned int cmd, + unsigned long arg) { + void *addr = (void __user *)arg; + int ret = -1; + + if (!bfdev || !addr) { + return -EFAULT; + } + + switch (cmd) { + case BF_FPGA_IOCTL_I2C_CTL: { + bf_fpga_i2c_ctl_t i2c_ctl; + bool busy; + + if (copy_from_user(&i2c_ctl, addr, sizeof(bf_fpga_i2c_ctl_t))) { + return -EFAULT; + } + switch (i2c_ctl.control_type) { + case BF_FPGA_I2C_START: + ret = fpga_i2c_start(i2c_ctl.inst_hndl.bus_id); + break; + case BF_FPGA_I2C_STOP: + ret = fpga_i2c_stop(i2c_ctl.inst_hndl.bus_id); + break; + case BF_FPGA_I2C_RESET: + ret = fpga_i2c_reset(i2c_ctl.inst_hndl.bus_id); + break; + case BF_FPGA_I2C_BUSY: + ret = fpga_i2c_is_busy(i2c_ctl.inst_hndl.bus_id, &busy); + if (ret == 0) { + if (copy_to_user(&(((bf_fpga_i2c_ctl_t *)addr)->is_busy), + &busy, + sizeof(busy))) { + return -EFAULT; + } + } + break; + case BF_FPGA_I2C_INST_EN: + ret = fpga_i2c_inst_en( + i2c_ctl.inst_hndl.bus_id, i2c_ctl.inst_hndl.inst_id, true); + break; + case BF_FPGA_I2C_INST_DIS: + ret = fpga_i2c_inst_en( + i2c_ctl.inst_hndl.bus_id, i2c_ctl.inst_hndl.inst_id, false); + break; + case BF_FPGA_I2C_INST_PMT: + break; + case BF_FPGA_I2C_INST_STOP_ON_ERR: + break; + case BF_FPGA_I2C_INST_INT_EN: + break; + default: + break; + } + break; + } + case BF_FPGA_IOCTL_I2C_SET_CLK: { + bf_fpga_i2c_set_clk_t i2c_clk; + if (copy_from_user(&i2c_clk, addr, sizeof(bf_fpga_i2c_set_clk_t))) { + return -EFAULT; + } + ret = fpga_i2c_set_clk(i2c_clk.bus_id, i2c_clk.clock_div); + break; + } + case BF_FPGA_IOCTL_I2C_ONETIME: { + bf_fpga_i2c_t i2c_op; + int i; + + if (copy_from_user(&i2c_op, addr, sizeof(bf_fpga_i2c_t))) { + return -EFAULT; + } + ret = fpga_i2c_oneshot(&i2c_op); + if (ret == 0) { + /* copy read data to user area */ + for (i = 0; i < i2c_op.num_i2c; i++) { + if (i2c_op.i2c_inst[i].rd_cnt) { + if (copy_to_user(&(((bf_fpga_i2c_t *)addr)->i2c_inst[i].rd_buf), + &i2c_op.i2c_inst[i].rd_buf, + i2c_op.i2c_inst[i].rd_cnt)) { + return -EFAULT; + } + } + } + } else { + printk(KERN_ERR + "fpga i2c ioctl oneshot bus %d error %d i2c_addr 0x%hhx:0x%hhx " + "i2c_status 0x%hhx:0x%hhx\n", + i2c_op.inst_hndl.bus_id, + ret, + i2c_op.i2c_inst[0].i2c_addr, + i2c_op.i2c_inst[1].i2c_addr, + i2c_op.i2c_inst[0].status, + i2c_op.i2c_inst[1].status); + } + break; + } + case BF_FPGA_IOCTL_I2C_ADD_PR: { + bf_fpga_i2c_t i2c_op; + if (copy_from_user(&i2c_op, addr, sizeof(bf_fpga_i2c_t))) { + return -EFAULT; + } + ret = fpga_i2c_pr_add(&i2c_op); + if (ret == 0) { + /* copy read data to user area */ + if (copy_to_user(&((bf_fpga_i2c_t *)addr)->inst_hndl.inst_id, + &i2c_op.inst_hndl.inst_id, + sizeof(i2c_op.inst_hndl.inst_id))) { + return -EFAULT; + } + } else { + printk(KERN_ERR "fpga i2c ioctl add-pr error %d on bus %d\n", + ret, + i2c_op.inst_hndl.bus_id); + } + break; + } + case BF_FPGA_IOCTL_I2C_DEL_PR: { + bf_fpga_i2c_t i2c_op; + if (copy_from_user(&i2c_op, addr, sizeof(bf_fpga_i2c_t))) { + return -EFAULT; + } + ret = fpga_i2c_del(&i2c_op); + if (ret != 0) { + printk(KERN_ERR "fpga i2c ioctl del-pr error %d on bus %d\n", + ret, + i2c_op.inst_hndl.bus_id); + } + break; + } + case BF_FPGA_IOCTL_I2C_RD_DATA: { + bf_fpga_i2c_rd_data_t i2c_rd_data; + /* get user supplied offset and rd_cnt */ + if (copy_from_user( + &i2c_rd_data, addr, offsetof(bf_fpga_i2c_rd_data_t, rd_buf))) { + return -EFAULT; + } + ret = fpga_i2c_data_read(i2c_rd_data.inst_hndl.bus_id, + i2c_rd_data.inst_hndl.inst_id, + i2c_rd_data.offset, + i2c_rd_data.rd_cnt, + i2c_rd_data.rd_buf); + if (ret == 0) { + if (copy_to_user(&(((bf_fpga_i2c_rd_data_t *)addr)->rd_buf), + &i2c_rd_data.rd_buf, + i2c_rd_data.rd_cnt)) { + return -EFAULT; + } + } else { + printk(KERN_ERR "fpga i2c ioctl rd-data error %d on bus %d inst %d\n", + ret, + i2c_rd_data.inst_hndl.bus_id, + i2c_rd_data.inst_hndl.inst_id); + } + break; + } + default: + return -EINVAL; + } + return ret; +} diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_ioctl.h b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_ioctl.h new file mode 100644 index 0000000000..4c5eb7bf03 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_ioctl.h @@ -0,0 +1,131 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#ifndef _BF_FPGA_IOCTL_H_ +#define _BF_FPGA_IOCTL_H_ + +#ifdef __KERNEL__ +#include +#else +#include + +#endif /* __KERNEL__ */ + +#define BF_FPGA_IOC_MAGIC 'f' +#define BF_I2C_FPGA_NUM_CTRL 34 +#define BF_FPGA_MAX_I2C_RD_DATA 128 +#define BF_FPGA_MAX_I2C_WR_DATA 129 + +/* i2c control types */ +#define BF_FPGA_I2C_START 1 +#define BF_FPGA_I2C_STOP 2 +#define BF_FPGA_I2C_BUSY 3 +#define BF_FPGA_I2C_INST_EN 4 +#define BF_FPGA_I2C_INST_DIS 5 +#define BF_FPGA_I2C_INST_PMT 6 +#define BF_FPGA_I2C_INST_STOP_ON_ERR 7 +#define BF_FPGA_I2C_INST_INT_EN 8 +#define BF_FPGA_I2C_RESET 9 + +/* i2c operation types */ +#define BF_FPGA_I2C_NOP 0 +#define BF_FPGA_I2C_WRITE \ + 1 /* / ..[max 129 bytes] */ +#define BF_FPGA_I2C_READ \ + 2 /* / ..[max 128 bytes] */ +#define BF_FPGA_I2C_ADDR_READ \ + 3 /* / / \ + */ + +#define FPGA_I2c_ONESHOT_BEGIN_INDEX 0 +#define FPGA_I2C_ONESHOT_NUM_INST 15 +#define FPGA_I2C_PERIODIC_BEGIN_INDEX (FPGA_I2C_ONESHOT_NUM_INST) +#define FPGA_I2C_PERIODIC_NUM_INST 16 +#define FPGA_I2C_NUM_INST \ + (FPGA_I2C_ONESHOT_NUM_INST + FPGA_I2C_PERIODIC_NUM_INST) +/* maximum i2c instructions that can be handled in one system call */ +#define BF_FPGA_I2C_MAX_NUM_INST 3 + +typedef struct bf_fpga_i2c_set_clk_s { + /* 0:100k, 1:400k, 2:1M, or: 125e6//3 */ + int clock_div; /* clock divider */ + unsigned char bus_id; /* controller index */ +} bf_fpga_i2c_set_clk_t; + +typedef struct bf_fpga_inst_hndl_s { + int inst_id; /* instruction index within the controller memory space */ + unsigned char bus_id; /* controller index */ + unsigned char status; +} bf_fpga_inst_hndl_t; + +typedef struct bf_fpga_i2c_inst_s { + bool preemt; + bool en; + unsigned char i2c_addr; /* i2c device address in 7 bit format */ + unsigned char i2c_type; /* type of i2c cycle */ + unsigned char delay; /* delay in TBD - microseconds before i2c transaction */ + unsigned char ctrl; + unsigned char wr_cnt; /* number of bytes to write (after i2c address) */ + unsigned char rd_cnt; /* number of bytes to read */ + union { + unsigned char + wr_buf[BF_FPGA_MAX_I2C_WR_DATA]; /* write data source buffer */ + unsigned char rd_buf[BF_FPGA_MAX_I2C_RD_DATA]; /* read data dest buffer */ + }; + unsigned char status; + unsigned char retry_cnt; /* if fpga maintains retry count */ + unsigned char mux; /* if fpga maintains internal MUX */ +} bf_fpga_i2c_inst_t; + +typedef struct bf_fpga_i2c_s { + unsigned char num_i2c; /* number of i2c operations */ + unsigned char one_time; /* one time or periodic */ + bf_fpga_inst_hndl_t inst_hndl; + bf_fpga_i2c_inst_t i2c_inst[BF_FPGA_I2C_MAX_NUM_INST]; +} bf_fpga_i2c_t; + +typedef struct bf_fpga_i2c_rd_data_s { + bf_fpga_inst_hndl_t inst_hndl; + unsigned char offset; + unsigned char rd_cnt; + unsigned char rd_buf[BF_FPGA_MAX_I2C_RD_DATA]; +} bf_fpga_i2c_rd_data_t; + +typedef struct bf_fpga_i2c_ctl_s { + bf_fpga_inst_hndl_t inst_hndl; + unsigned char control_type; /* start, stop, reset, enable, disable or busy */ + bool is_busy; +} bf_fpga_i2c_ctl_t; + +#define BF_FPGA_IOCTL_I2C_CTL _IOWR(BF_FPGA_IOC_MAGIC, 0, bf_fpga_i2c_ctl_t) +#define BF_FPGA_IOCTL_I2C_ONETIME _IOWR(BF_FPGA_IOC_MAGIC, 1, bf_fpga_i2c_t) +#define BF_FPGA_IOCTL_I2C_ADD_PR _IOWR(BF_FPGA_IOC_MAGIC, 2, bf_fpga_i2c_t) +#define BF_FPGA_IOCTL_I2C_DEL_PR _IOWR(BF_FPGA_IOC_MAGIC, 3, bf_fpga_i2c_t) +#define BF_FPGA_IOCTL_I2C_RD_DATA \ + _IOWR(BF_FPGA_IOC_MAGIC, 4, bf_fpga_i2c_rd_data_t) +#define BF_FPGA_IOCTL_I2C_SET_CLK \ + _IOW(BF_FPGA_IOC_MAGIC, 5, bf_fpga_i2c_set_clk_t) + +#endif /* _BF_FPGA_IOCTL_H_ */ diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_main.c b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_main.c new file mode 100644 index 0000000000..563cc5120d --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_main.c @@ -0,0 +1,1294 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +/* bf_fpga kernel module + * + * This is kernel mode driver for BF FPGA chip. + * Provides user space mmap service and user space "wait for interrupt", + * "enable interrupt" and i2c services + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bf_fpga_ioctl.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include +#else +#include +#endif +#include +#include +#include +#include "bf_fpga_priv.h" +#include "i2c/bf_fpga_i2c_reg.h" +#include "i2c/bf_fpga_i2c.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) +//#error unsupported linux kernel version +#endif + +/* TBD: Need to build with CONFIG_PCI_MSI */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) +#if defined(RHEL_RELEASE_CODE) +#else +extern int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec); +#endif /* defined(RHEL_RELEASE_CODE) */ +extern int pci_enable_msix(struct pci_dev *dev, + struct msix_entry *entries, + int nvec); +#else +extern int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec); +extern int pci_enable_msix_range(struct pci_dev *dev, + struct msix_entry *entries, + int minvec, + int maxvec); +#endif + +/* Keep any global information here that must survive even after the + * bf_pci_dev is free-ed up. + */ +struct bf_global { + struct bf_pci_dev *bfdev; + struct cdev *bf_cdev; + struct fasync_struct *async_queue; +}; + +static int bf_major; +static int bf_minor[BF_FPGA_MAX_DEVICE_CNT] = {0}; +static struct class *bf_class = NULL; +static char *intr_mode = NULL; + +static enum bf_intr_mode bf_intr_mode_default = BF_INTR_MODE_NONE; +static spinlock_t bf_nonisr_lock; + +/* dev->minor should index into this array */ +static struct bf_global bf_global[BF_FPGA_MAX_DEVICE_CNT]; + +static void bf_add_listener(struct bf_pci_dev *bfdev, + struct bf_listener *listener) { + struct bf_listener **cur_listener = &bfdev->listener_head; + + if (!listener) { + return; + } + spin_lock(&bf_nonisr_lock); + + while (*cur_listener) { + cur_listener = &((*cur_listener)->next); + } + *cur_listener = listener; + listener->next = NULL; + + spin_unlock(&bf_nonisr_lock); +} + +static void bf_remove_listener(struct bf_pci_dev *bfdev, + struct bf_listener *listener) { + struct bf_listener **cur_listener = &bfdev->listener_head; + + /* in case of certain error conditions, this function might be called after + * bf_pci_remove() + */ + if (!bfdev || !listener) { + return; + } + spin_lock(&bf_nonisr_lock); + + if (*cur_listener == listener) { + *cur_listener = listener->next; + } else { + while (*cur_listener) { + if ((*cur_listener)->next == listener) { + (*cur_listener)->next = listener->next; + break; + } + cur_listener = &((*cur_listener)->next); + } + listener->next = NULL; + } + + spin_unlock(&bf_nonisr_lock); +} + +/* a pool of minor numbers is maintained */ +/* return the first available minor number */ +static int bf_get_next_minor_no(int *minor) { + int i; + + spin_lock(&bf_nonisr_lock); + for (i = 0; i < BF_FPGA_MAX_DEVICE_CNT; i++) { + if (bf_minor[i] == 0) { + *minor = i; + bf_minor[i] = 1; /* mark it as taken */ + spin_unlock(&bf_nonisr_lock); + return 0; + } + } + *minor = -1; + spin_unlock(&bf_nonisr_lock); + return -1; +} + +/* return a minor number back to the pool for recycling */ +static int bf_return_minor_no(int minor) { + int err; + + spin_lock(&bf_nonisr_lock); + if (bf_minor[minor] == 0) { /* was already returned */ + err = -1; /* don't change anything, but return error */ + } else { + bf_minor[minor] = 0; /* mark it as available */ + err = 0; + } + spin_unlock(&bf_nonisr_lock); + return err; +} + +static inline struct bf_pci_dev *bf_get_pci_dev(struct bf_dev_info *info) { + return container_of(info, struct bf_pci_dev, info); +} + +/* + * It masks the msix on/off of generating MSI-X messages. + */ +static void bf_msix_mask_irq(struct msi_desc *desc, int32_t state) { + u32 mask_bits = desc->masked; + unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_VECTOR_CTRL; + + if (state != 0) { + mask_bits &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT; + } else { + mask_bits |= PCI_MSIX_ENTRY_CTRL_MASKBIT; + } + + if (mask_bits != desc->masked) { + writel(mask_bits, desc->mask_base + offset); + readl(desc->mask_base); + desc->masked = mask_bits; + } +} + +/** + * irqcontrol can be used to disable/enable interrupt from user space processes. + * + * @param bf_dev + * pointer to bf_pci_dev + * @param irq_state + * state value. 1 to enable interrupt, 0 to disable interrupt. + * + * @return + * - On success, 0. + * - On failure, a negative value. + */ +static int bf_pci_irqcontrol(struct bf_pci_dev *bfdev, s32 irq_state) { + struct pci_dev *pdev = bfdev->pdev; + + pci_cfg_access_lock(pdev); + if (bfdev->mode == BF_INTR_MODE_LEGACY) { + pci_intx(pdev, !!irq_state); + } else if (bfdev->mode == BF_INTR_MODE_MSIX) { + struct msi_desc *desc; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) + list_for_each_entry(desc, &pdev->msi_list, list) + bf_msix_mask_irq(desc, irq_state); +#else + for_each_pci_msi_entry(desc, pdev) bf_msix_mask_irq(desc, irq_state); +#endif + } + pci_cfg_access_unlock(pdev); + + return 0; +} + +/** + * interrupt handler which will check if the interrupt is from the right + * device. If so, disable it here and will be enabled later. + */ +static irqreturn_t bf_pci_irqhandler(int irq, struct bf_pci_dev *bfdev) { + /* Legacy mode need to mask in hardware */ + if (bfdev->mode == BF_INTR_MODE_LEGACY && + !pci_check_and_mask_intx(bfdev->pdev)) { + return IRQ_NONE; + } + + /* NOTE : if bfdev->info.pci_error_state == 1, then do not access the + * device and return IRQ_NOTHANDLED. + */ + return IRQ_HANDLED; +} + +/* Remap pci resources described by bar #pci_bar */ +static int bf_pci_setup_iomem(struct pci_dev *dev, + struct bf_dev_info *info, + int n, + int pci_bar, + const char *name) { + unsigned long addr, len; + void *internal_addr; + + if (sizeof(info->mem) / sizeof(info->mem[0]) <= n) { + return -EINVAL; + } + + addr = pci_resource_start(dev, pci_bar); + len = pci_resource_len(dev, pci_bar); + if (addr == 0 || len == 0) { + return -1; + } + internal_addr = pci_ioremap_bar(dev, pci_bar); + if (internal_addr == NULL) { + return -1; + } + info->mem[n].name = name; + info->mem[n].addr = addr; + info->mem[n].internal_addr = internal_addr; + info->mem[n].size = len; + return 0; +} + +/* Unmap previously ioremap'd resources */ +static void bf_pci_release_iomem(struct bf_dev_info *info) { + int i; + + for (i = 0; i < BF_MAX_BAR_MAPS; i++) { + if (info->mem[i].internal_addr) { + iounmap(info->mem[i].internal_addr); + } + } +} + +static int bf_setup_bars(struct pci_dev *dev, struct bf_dev_info *info) { + int i, iom, ret; + unsigned long flags; + static const char *bar_names[BF_MAX_BAR_MAPS] = { + "BAR0", "BAR1", "BAR2", "BAR3", "BAR4", "BAR5", + }; + + iom = 0; + + for (i = 0; i < BF_MAX_BAR_MAPS; i++) { + if (pci_resource_len(dev, i) != 0 && pci_resource_start(dev, i) != 0) { + flags = pci_resource_flags(dev, i); + if (flags & IORESOURCE_MEM) { + ret = bf_pci_setup_iomem(dev, info, iom, i, bar_names[i]); + if (ret != 0) { + return ret; + } + iom++; + } + } + } + return (iom != 0) ? ret : -ENOENT; +} + +static irqreturn_t bf_interrupt(int irq, void *bfdev_id) { + struct bf_pci_dev *bfdev = ((struct bf_int_vector *)bfdev_id)->bf_dev; + int vect_off = ((struct bf_int_vector *)bfdev_id)->int_vec_offset; + + irqreturn_t ret = bf_pci_irqhandler(irq, bfdev); + + if (ret == IRQ_HANDLED) { + atomic_inc(&(bfdev->info.event[vect_off])); + } + return ret; +} + +static unsigned int bf_poll(struct file *filep, poll_table *wait) { + struct bf_listener *listener = (struct bf_listener *)filep->private_data; + struct bf_pci_dev *bfdev = listener->bfdev; + int i; + + if (!bfdev) { + return -ENODEV; + } + if (!bfdev->info.irq) { + return -EIO; + } + + poll_wait(filep, &bfdev->info.wait, wait); + + for (i = 0; i < BF_MSIX_ENTRY_CNT; i++) { + if (listener->event_count[i] != atomic_read(&bfdev->info.event[i])) { + return POLLIN | POLLRDNORM; + } + } + return 0; +} + +static int bf_find_mem_index(struct vm_area_struct *vma) { + struct bf_pci_dev *bfdev = vma->vm_private_data; + if (vma->vm_pgoff < BF_MAX_BAR_MAPS) { + if (bfdev->info.mem[vma->vm_pgoff].size == 0) { + return -1; + } + return (int)vma->vm_pgoff; + } + return -1; +} + +static const struct vm_operations_struct bf_physical_vm_ops = { +#ifdef CONFIG_HAVE_IOREMAP_PROT + .access = generic_access_phys, +#endif +}; + +static int bf_mmap_physical(struct vm_area_struct *vma) { + struct bf_pci_dev *bfdev = vma->vm_private_data; + int bar = bf_find_mem_index(vma); + struct bf_dev_mem *mem; + if (bar < 0) { + return -EINVAL; + } + + mem = bfdev->info.mem + bar; + + if (mem->addr & ~PAGE_MASK) { + return -ENODEV; + } + if (vma->vm_end - vma->vm_start > mem->size) { + return -EINVAL; + } + + vma->vm_ops = &bf_physical_vm_ops; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + /* + * We cannot use the vm_iomap_memory() helper here, + * because vma->vm_pgoff is the map index we looked + * up above in bf_find_mem_index(), rather than an + * actual page offset into the mmap. + * + * So we just do the physical mmap without a page + * offset. + */ + return remap_pfn_range(vma, + vma->vm_start, + mem->addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static int bf_mmap(struct file *filep, struct vm_area_struct *vma) { + struct bf_listener *listener = filep->private_data; + struct bf_pci_dev *bfdev = listener->bfdev; + int bar; + unsigned long requested_pages, actual_pages; + + if (!bfdev) { + return -ENODEV; + } + if (vma->vm_end < vma->vm_start) { + return -EINVAL; + } + + vma->vm_private_data = bfdev; + + bar = bf_find_mem_index(vma); + if (bar < 0) { + return -EINVAL; + } + + requested_pages = vma_pages(vma); + actual_pages = ((bfdev->info.mem[bar].addr & ~PAGE_MASK) + + bfdev->info.mem[bar].size + PAGE_SIZE - 1) >> + PAGE_SHIFT; + if (requested_pages > actual_pages) { + return -EINVAL; + } + + return bf_mmap_physical(vma); +} + +static int bf_fasync(int fd, struct file *filep, int mode) { + int minor; + + if (!filep->private_data) { + return (-EINVAL); + } + minor = ((struct bf_listener *)filep->private_data)->minor; + if (minor >= BF_FPGA_MAX_DEVICE_CNT) { + return (-EINVAL); + } + if (mode == 0 && &bf_global[minor].async_queue == NULL) { + return 0; /* nothing to do */ + } + return (fasync_helper(fd, filep, mode, &bf_global[minor].async_queue)); +} + +static int bf_open(struct inode *inode, struct file *filep) { + struct bf_pci_dev *bfdev; + struct bf_listener *listener; + int i; + + bfdev = bf_global[iminor(inode)].bfdev; + listener = kmalloc(sizeof(*listener), GFP_KERNEL); + if (listener) { + listener->bfdev = bfdev; + listener->minor = bfdev->info.minor; + listener->next = NULL; + bf_add_listener(bfdev, listener); + for (i = 0; i < BF_MSIX_ENTRY_CNT; i++) { + listener->event_count[i] = atomic_read(&bfdev->info.event[i]); + } + filep->private_data = listener; + return 0; + } else { + return (-ENOMEM); + } +} + +static int bf_release(struct inode *inode, struct file *filep) { + struct bf_listener *listener = filep->private_data; + + bf_fasync(-1, filep, 0); /* empty any process id in the notification list */ + if (listener->bfdev) { + bf_remove_listener(listener->bfdev, listener); + } + kfree(listener); + return 0; +} + +/* user space support: make read() system call after poll() of select() */ +static ssize_t bf_read(struct file *filep, + char __user *buf, + size_t count, + loff_t *ppos) { + struct bf_listener *listener = filep->private_data; + struct bf_pci_dev *bfdev = listener->bfdev; + int retval, event_count[BF_MSIX_ENTRY_CNT]; + int i, mismatch_found = 0; /* OR of per vector mismatch */ + unsigned char cnt_match[BF_MSIX_ENTRY_CNT]; /* per vector mismatch */ + + if (!bfdev) { + return -ENODEV; + } + /* irq must be setup for read() to work */ + if (!bfdev->info.irq) { + return -EIO; + } + + /* ensure that there is enough space on user buffer for the given interrupt + * mode */ + if (bfdev->mode == BF_INTR_MODE_MSIX) { + if (count < sizeof(s32) * BF_MSIX_ENTRY_CNT) { + return -EINVAL; + } + count = sizeof(s32) * BF_MSIX_ENTRY_CNT; + } else if (bfdev->mode == BF_INTR_MODE_MSI) { + if (count < sizeof(s32) * BF_MSI_ENTRY_CNT) { + return -EINVAL; + } + count = sizeof(s32) * BF_MSI_ENTRY_CNT; + } else { + if (count < sizeof(s32)) { + return -EINVAL; + } + count = sizeof(s32); + } + + do { + set_current_state(TASK_INTERRUPTIBLE); + + for (i = 0; i < (count / sizeof(s32)); i++) { + event_count[i] = atomic_read(&(bfdev->info.event[i])); + if (event_count[i] != listener->event_count[i]) { + mismatch_found |= 1; + cnt_match[i] = 1; + } else { + event_count[i] = 0; + cnt_match[i] = 0; + } + } + if (mismatch_found) { + __set_current_state(TASK_RUNNING); + if (copy_to_user(buf, &event_count, count)) { + retval = -EFAULT; + } else { /* adjust the listener->event_count; */ + for (i = 0; i < (count / sizeof(s32)); i++) { + if (cnt_match[i]) { + listener->event_count[i] = event_count[i]; + } + } + retval = count; + } + break; + } + + if (filep->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } while (1); + + __set_current_state(TASK_RUNNING); + + return retval; +} + +/* user space is supposed to call this after it is done with interrupt + * processing + */ +static ssize_t bf_write(struct file *filep, + const char __user *buf, + size_t count, + loff_t *ppos) { + struct bf_listener *listener = filep->private_data; + struct bf_pci_dev *bfdev = listener->bfdev; + ssize_t ret; + s32 int_en; + + if (!bfdev || !bfdev->info.irq) { + return -EIO; + } + + if (count != sizeof(s32)) { + return -EINVAL; + } + + if (copy_from_user(&int_en, buf, count)) { + return -EFAULT; + } + + /* clear pci_error_state */ + bfdev->info.pci_error_state = 0; + + ret = bf_pci_irqcontrol(bfdev, int_en); + + return ret ? ret : sizeof(s32); +} + +static long bf_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { + struct bf_listener *listener = filep->private_data; + struct bf_pci_dev *bfdev = listener->bfdev; + + return (bf_fpga_ioctl(bfdev, cmd, arg)); +} + +static const struct file_operations bf_fops = { + .owner = THIS_MODULE, + .open = bf_open, + .release = bf_release, + .unlocked_ioctl = bf_ioctl, + .read = bf_read, + .write = bf_write, + .mmap = bf_mmap, + .poll = bf_poll, + .fasync = bf_fasync, +}; + +static int bf_major_init(struct bf_pci_dev *bfdev, int minor) { + struct cdev *cdev; + static const char name[] = "bf_fpga"; + dev_t bf_dev = 0; + int result; + + result = alloc_chrdev_region(&bf_dev, 0, BF_FPGA_MAX_DEVICE_CNT, name); + if (result) { + return result; + } + + result = -ENOMEM; + cdev = cdev_alloc(); + if (!cdev) { + goto fail_dev_add; + } + cdev->ops = &bf_fops; + cdev->owner = THIS_MODULE; + kobject_set_name(&cdev->kobj, "%s", name); + result = cdev_add(cdev, bf_dev, BF_FPGA_MAX_DEVICE_CNT); + + if (result) { + goto fail_dev_add; + } + + bf_major = MAJOR(bf_dev); + bf_global[minor].bf_cdev = cdev; + return 0; + +fail_dev_add: + unregister_chrdev_region(bf_dev, BF_FPGA_MAX_DEVICE_CNT); + return result; +} + +static void bf_major_cleanup(struct bf_pci_dev *bfdev, int minor) { + unregister_chrdev_region(MKDEV(bf_major, 0), BF_FPGA_MAX_DEVICE_CNT); + cdev_del(bf_global[minor].bf_cdev); +} + +static int bf_init_cdev(struct bf_pci_dev *bfdev, int minor) { + int ret; + ret = bf_major_init(bfdev, minor); + if (ret) return ret; + + bf_class = class_create(THIS_MODULE, BF_CLASS_NAME); + if (!bf_class) { + printk(KERN_ERR "create_class failed for bf_fpga_dev\n"); + ret = -ENODEV; + goto err_class_register; + } + return 0; + +err_class_register: + bf_major_cleanup(bfdev, minor); + return ret; +} + +static void bf_remove_cdev(struct bf_pci_dev *bfdev) { + class_destroy(bf_class); + bf_major_cleanup(bfdev, bfdev->info.minor); +} + +/** + * bf_register_device - register a new userspace mem device + * @parent: parent device + * @bfdev: bf pci device + * + * returns zero on success or a negative error code. + */ +int bf_register_device(struct device *parent, struct bf_pci_dev *bfdev) { + struct bf_dev_info *info = &bfdev->info; + int i, j, ret = 0; + int minor; + + if (!parent || !info || !info->version) { + return -EINVAL; + } + + init_waitqueue_head(&info->wait); + + for (i = 0; i < BF_MSIX_ENTRY_CNT; i++) { + atomic_set(&info->event[i], 0); + } + + if (bf_get_next_minor_no(&minor)) { + return -EINVAL; + } + + ret = bf_init_cdev(bfdev, minor); + if (ret) { + printk(KERN_ERR "BFi_FPGA: device cdev creation failed\n"); + return ret; + } + + info->dev = device_create( + bf_class, parent, MKDEV(bf_major, minor), bfdev, "bf_fpga_%d", minor); + if (!info->dev) { + printk(KERN_ERR "BF_FPGA: device creation failed\n"); + return -ENODEV; + } + + info->minor = minor; + + /* bind ISRs and request interrupts */ + if (info->irq && (bfdev->mode != BF_INTR_MODE_NONE)) { + /* + * Note that we deliberately don't use devm_request_irq + * here. The parent module can unregister the UIO device + * and call pci_disable_msi, which requires that this + * irq has been freed. However, the device may have open + * FDs at the time of unregister and therefore may not be + * freed until they are released. + */ + if (bfdev->mode == BF_INTR_MODE_LEGACY) { + ret = request_irq(info->irq, + bf_interrupt, + info->irq_flags, + bfdev->name, + (void *)&(bfdev->bf_int_vec[0])); + if (ret) { + printk(KERN_ERR "bf_fpga failed to request legacy irq %ld error %d\n", + info->irq, + ret); + return ret; + } + printk(KERN_NOTICE "BF_FPGA allocating legacy int vector %ld\n", + info->irq); + } else if (bfdev->mode == BF_INTR_MODE_MSIX) { + for (i = 0; i < info->num_irq; i++) { + ret = request_irq(info->msix_entries[i].vector, + bf_interrupt, + info->irq_flags, + bfdev->name, + (void *)&(bfdev->bf_int_vec[i])); + if (ret) { + /* undo all other previous bindings */ + printk(KERN_ERR "bf_fpga failed to request MSIX ret %d itr %d\n", + ret, + i); + for (j = i - 1; j >= 0; j--) { + free_irq(info->msix_entries[j].vector, + (void *)&(bfdev->bf_int_vec[j])); + } + return ret; + } + } + printk(KERN_NOTICE "BF_FPGA allocating %d MSIx vectors from %ld\n", + info->num_irq, + info->irq); + } else if (bfdev->mode == BF_INTR_MODE_MSI) { + for (i = 0; i < info->num_irq; i++) { + ret = request_irq(info->irq + i, + bf_interrupt, + info->irq_flags, + bfdev->name, + (void *)&(bfdev->bf_int_vec[i])); + if (ret) { + /* undo all other previous bindings */ + printk( + KERN_ERR "bf_fpga failed to request MSI ret %d itr %d\n", ret, i); + for (j = i - 1; j >= 0; j--) { + free_irq(info->irq + j, (void *)&(bfdev->bf_int_vec[j])); + } + return ret; + } + } + printk(KERN_NOTICE "BF_FPGA allocating %d MSI vectors from %ld\n", + info->num_irq, + info->irq); + } + } + return 0; +} + +/** + * bf_unregister_device - register a new userspace mem device + * @bfdev: bf pci device + * + * returns none + */ +void bf_unregister_device(struct bf_pci_dev *bfdev) { + struct bf_dev_info *info = &bfdev->info; + int i; + + if (info->irq) { + if (bfdev->mode == BF_INTR_MODE_LEGACY) { + free_irq(info->irq, (void *)&(bfdev->bf_int_vec[0])); + } else if (bfdev->mode == BF_INTR_MODE_MSIX) { + for (i = 0; i < info->num_irq; i++) { + free_irq(info->msix_entries[i].vector, (void *)&(bfdev->bf_int_vec[i])); + } + } else if (bfdev->mode == BF_INTR_MODE_MSI) { + for (i = 0; i < info->num_irq; i++) { + free_irq(info->irq + i, (void *)&(bfdev->bf_int_vec[i])); + } + } + } + device_destroy(bf_class, MKDEV(bf_major, info->minor)); + bf_remove_cdev(bfdev); + bf_return_minor_no(info->minor); + return; +} + +static inline struct device *pci_dev_to_dev(struct pci_dev *pdev) { + return &pdev->dev; +} + +static void bf_fpga_disable_int_dma(struct bf_pci_dev *bfdev) { + u8 *bf_base_addr; + + /* maskinterrupts and DMA */ + bf_base_addr = (bfdev->info.mem[0].internal_addr); + /* return if called before mmap */ + if (!bf_base_addr) { + return; + } + /* mask interrupt at shadow level */ + /* TBD */ +} + +static void fpga_print_build_date(u32 build_date) { + char day, month, year, hr, min, sec; + + sec = (char)(build_date & 0x3f); + build_date >>= 6; + min = (char)(build_date & 0x3f); + build_date >>= 6; + hr = (char)(build_date & 0x1f); + build_date >>= 5; + year = (char)(build_date & 0x3f); + build_date >>= 6; + month = (char)(build_date & 0x0f); + build_date >>= 4; + day = (char)(build_date & 0x1f); + printk(KERN_ALERT "fpga version %02d/%02d/%2d %02d:%02d:%02d", + month, + day, + year, + hr, + min, + sec); +} + +static int bf_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + struct bf_pci_dev *bfdev; + int err, pci_use_highmem; + int i, num_irq; + u32 build_date, build_ver; + + memset(bf_global, 0, sizeof(bf_global)); + + bfdev = kzalloc(sizeof(struct bf_pci_dev), GFP_KERNEL); + if (!bfdev) { + return -ENOMEM; + } + + /* init the cookies to be passed to ISRs */ + for (i = 0; i < BF_MSIX_ENTRY_CNT; i++) { + bfdev->bf_int_vec[i].int_vec_offset = i; + bfdev->bf_int_vec[i].bf_dev = bfdev; + } + + /* initialize intr_mode to none */ + bfdev->mode = BF_INTR_MODE_NONE; + + /* clear pci_error_state */ + bfdev->info.pci_error_state = 0; + + /* + * enable device + */ + err = pci_enable_device(pdev); + if (err != 0) { + printk(KERN_ERR "bf_fpga cannot enable PCI device\n"); + goto fail_free; + } + + /* + * reserve device's PCI memory regions for use by this + * module + */ + err = pci_request_regions(pdev, "bf_fpga_umem"); + if (err != 0) { + printk(KERN_ERR "bf_fpga Cannot request regions\n"); + goto fail_pci_disable; + } + /* remap IO memory */ + err = bf_setup_bars(pdev, &bfdev->info); + if (err != 0) { + printk(KERN_ERR "bf_fpga Cannot setup BARs\n"); + goto fail_release_iomem; + } + + if (!dma_set_mask(pci_dev_to_dev(pdev), DMA_BIT_MASK(64)) && + !dma_set_coherent_mask(pci_dev_to_dev(pdev), DMA_BIT_MASK(64))) { + pci_use_highmem = 1; + } else { + err = dma_set_mask(pci_dev_to_dev(pdev), DMA_BIT_MASK(32)); + if (err) { + err = dma_set_coherent_mask(pci_dev_to_dev(pdev), DMA_BIT_MASK(32)); + if (err) { + printk(KERN_ERR "bf_fpga no usable DMA configuration, aborting\n"); + goto fail_release_iomem; + } + } + pci_use_highmem = 0; + } + + /* enable pci error reporting */ + /* for the current kernel version, kernel config must have set the followings: + * CONFIG_PCIEPORTBUS=y and CONFIG_PCIEAER = y + * we have pci_error_handlers defined that gets invoked by kernel AER module + * upon detecting the pcie error on this device's addresses. + * However, there seems no way that AER would pass the offending addresses + * to the callback functions. AER logs the error messages on the console. + * This driver's calback function send the SIGIO signal to the user space + * to indicate the error condition. + */ + pci_enable_pcie_error_reporting(pdev); + + bf_fpga_disable_int_dma(bfdev); + + /* enable bus mastering on the device */ + pci_set_master(pdev); + + /* fill in bfdev info */ + bfdev->info.version = "0.1"; + bfdev->info.owner = THIS_MODULE; + bfdev->pdev = pdev; + + switch (bf_intr_mode_default) { +#ifdef CONFIG_PCI_MSI + case BF_INTR_MODE_MSIX: + /* Only 1 msi-x vector needed */ + bfdev->info.msix_entries = + kcalloc(BF_MSIX_ENTRY_CNT, sizeof(struct msix_entry), GFP_KERNEL); + if (!bfdev->info.msix_entries) { + err = -ENOMEM; + goto fail_clear_pci_master; + } + for (i = 0; i < BF_MSIX_ENTRY_CNT; i++) { + bfdev->info.msix_entries[i].entry = i; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) + num_irq = + pci_enable_msix(pdev, bfdev->info.msix_entries, BF_MSIX_ENTRY_CNT); + if (num_irq == 0) { + bfdev->info.num_irq = BF_MSIX_ENTRY_CNT; + bfdev->info.irq = bfdev->info.msix_entries[0].vector; + bfdev->mode = BF_INTR_MODE_MSIX; + printk(KERN_DEBUG "bf_fpga using %d MSIX irq from %ld\n", + num_irq, + bfdev->info.irq); + break; + } +#else + num_irq = pci_enable_msix_range( + pdev, bfdev->info.msix_entries, BF_MSIX_ENTRY_CNT, BF_MSIX_ENTRY_CNT); + if (num_irq == BF_MSIX_ENTRY_CNT) { + bfdev->info.num_irq = num_irq; + bfdev->info.irq = bfdev->info.msix_entries[0].vector; + bfdev->mode = BF_INTR_MODE_MSIX; + printk(KERN_DEBUG "bf_fpga using %d MSIX irq from %ld\n", + num_irq, + bfdev->info.irq); + break; + } else { + if (num_irq) pci_disable_msix(pdev); + kfree(bfdev->info.msix_entries); + bfdev->info.msix_entries = NULL; + printk(KERN_ERR + "bf_fpga error allocating MSIX vectors. Trying MSI...\n"); + /* and, fall back to MSI */ + } +#endif /* LINUX_VERSION_CODE */ + /* ** intentional no-break */ + case BF_INTR_MODE_MSI: +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) + num_irq = pci_enable_msi_block(pdev, BF_MSI_ENTRY_CNT); + /* we must get requested number of MSI vectors enabled */ + if (num_irq == 0) { + bfdev->info.num_irq = BF_MSI_ENTRY_CNT; + bfdev->info.irq = pdev->irq; + bfdev->mode = BF_INTR_MODE_MSI; + printk(KERN_DEBUG "bf_fpga using %d MSI irq from %ld\n", + bfdev->info.num_irq, + bfdev->info.irq); + break; + } +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) + num_irq = pci_enable_msi_range(pdev, BF_MSI_ENTRY_CNT, BF_MSI_ENTRY_CNT); + if (num_irq > 0) { + bfdev->info.num_irq = num_irq; + bfdev->info.irq = pdev->irq; + bfdev->mode = BF_INTR_MODE_MSI; + printk(KERN_DEBUG "bf_fpga using %d MSI irq from %ld\n", + bfdev->info.num_irq, + bfdev->info.irq); + break; + } +#else + num_irq = pci_alloc_irq_vectors_affinity(pdev, + BF_MSI_ENTRY_CNT, + BF_MSI_ENTRY_CNT, + PCI_IRQ_MSI | PCI_IRQ_AFFINITY, + NULL); + if (num_irq > 0) { + bfdev->info.num_irq = num_irq; + bfdev->info.irq = pci_irq_vector(pdev, 0); + bfdev->mode = BF_INTR_MODE_MSI; + printk(KERN_DEBUG "bf_fpga using %d MSI irq from %ld\n", + bfdev->info.num_irq, + bfdev->info.irq); + break; + } +#endif /* LINUX_VERSION_CODE */ +#endif /* CONFIG_PCI_MSI */ + /* fall back to Legacy Interrupt, intentional no-break */ + + case BF_INTR_MODE_LEGACY: + if (pci_intx_mask_supported(pdev)) { + bfdev->info.irq_flags = IRQF_SHARED; + bfdev->info.irq = pdev->irq; + bfdev->mode = BF_INTR_MODE_LEGACY; + printk(KERN_DEBUG "bf_fpga using LEGACY irq %ld\n", bfdev->info.irq); + break; + } + printk(KERN_NOTICE "bf_fpga PCI INTx mask not supported\n"); + /* fall back to no Interrupt, intentional no-break */ + case BF_INTR_MODE_NONE: + bfdev->info.irq = 0; + bfdev->info.num_irq = 0; + bfdev->mode = BF_INTR_MODE_NONE; + break; + + default: + printk(KERN_DEBUG "bf_fpga invalid IRQ mode %u", bf_intr_mode_default); + err = -EINVAL; + goto fail_clear_pci_master; + } + + pci_set_drvdata(pdev, bfdev); + sprintf(bfdev->name, "bf_fpga%d", bfdev->info.minor); + /* register bf driver */ + err = bf_register_device(&pdev->dev, bfdev); + if (err != 0) { + goto fail_release_irq; + } + + bf_global[bfdev->info.minor].async_queue = NULL; + bf_global[bfdev->info.minor].bfdev = bfdev; + + dev_info(&pdev->dev, + "bf_fpga device %d registered with irq %ld\n", + bfdev->instance, + bfdev->info.irq); + if (fpga_i2c_init(bfdev->info.mem[0].internal_addr)) { + printk(KERN_ERR "bf_fpga i2c initialization failed\n"); + goto fail_register_device; + } + if (bf_fpga_sysfs_add(bfdev)) { + printk(KERN_ERR "bf_fpga stsfs initialization failed\n"); + goto fail_i2c_init; + } + build_ver = + *((u32 *)(bfdev->info.mem[0].internal_addr) + (BF_FPGA_VER_REG / 4)); + build_date = + *((u32 *)(bfdev->info.mem[0].internal_addr) + (BF_FPGA_BUILD_DATE / 4)); + fpga_print_build_date(build_date); + printk(KERN_ALERT "bf_fpga version %hu:%hu probe ok\n", + (u16)(build_ver >> 16), + (u16)(build_ver)); + return 0; + +fail_i2c_init: + fpga_i2c_deinit(); +fail_register_device: + bf_unregister_device(bfdev); +fail_release_irq: + pci_set_drvdata(pdev, NULL); + if (bfdev->mode == BF_INTR_MODE_MSIX) { + pci_disable_msix(bfdev->pdev); + kfree(bfdev->info.msix_entries); + bfdev->info.msix_entries = NULL; + } else if (bfdev->mode == BF_INTR_MODE_MSI) { + pci_disable_msi(bfdev->pdev); + } +fail_clear_pci_master: + pci_clear_master(pdev); +fail_release_iomem: + bf_pci_release_iomem(&bfdev->info); + pci_release_regions(pdev); +fail_pci_disable: + pci_disable_device(pdev); +fail_free: + kfree(bfdev); + + printk(KERN_ERR "bf_fpga probe failed\n"); + return err; +} + +static void bf_pci_remove(struct pci_dev *pdev) { + struct bf_pci_dev *bfdev = pci_get_drvdata(pdev); + struct bf_listener *cur_listener; + + bf_fpga_disable_int_dma(bfdev); + bf_fpga_sysfs_del(bfdev); + fpga_i2c_deinit(); + bf_unregister_device(bfdev); + if (bfdev->mode == BF_INTR_MODE_MSIX) { + pci_disable_msix(pdev); + kfree(bfdev->info.msix_entries); + bfdev->info.msix_entries = NULL; + } else if (bfdev->mode == BF_INTR_MODE_MSI) { + pci_disable_msi(pdev); + } + pci_clear_master(pdev); + bf_pci_release_iomem(&bfdev->info); + pci_release_regions(pdev); + pci_disable_pcie_error_reporting(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + bf_global[bfdev->info.minor].bfdev = NULL; + /* existing filep structures in open file(s) must be informed that + * bf_pci_dev is no longer valid */ + spin_lock(&bf_nonisr_lock); + cur_listener = bfdev->listener_head; + while (cur_listener) { + cur_listener->bfdev = NULL; + cur_listener = cur_listener->next; + } + spin_unlock(&bf_nonisr_lock); + kfree(bfdev); + printk(KERN_ALERT "bf_fpga removed\n"); +} + +/** + * bf_pci_error_detected - called when PCI error is detected + * @pdev: Pointer to PCI device + * @state: The current pci connection state + * + * called when root complex detects pci error associated with the device + */ +static pci_ers_result_t bf_pci_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) { + struct bf_pci_dev *bfdev = pci_get_drvdata(pdev); + int minor; + + if (!bfdev) { + return PCI_ERS_RESULT_NONE; + } + printk(KERN_ERR "bf_fpga pci_err_detected state %d\n", state); + if (state == pci_channel_io_perm_failure || state == pci_channel_io_frozen) { + bfdev->info.pci_error_state = 1; + /* send a signal to the user space program of the error */ + minor = bfdev->info.minor; + if (minor < BF_FPGA_MAX_DEVICE_CNT && bf_global[minor].async_queue) { + kill_fasync(&bf_global[minor].async_queue, SIGIO, POLL_ERR); + } + return PCI_ERS_RESULT_DISCONNECT; + } else { + return PCI_ERS_RESULT_NONE; + } +} + +/** + * bf_pci_slot_reset - called after the pci bus has been reset. + * @pdev: Pointer to PCI device + * + * Restart the card from scratch, as if from a cold-boot. + */ +static pci_ers_result_t bf_pci_slot_reset(struct pci_dev *pdev) { + /* nothing to do for now as we do not expect to get backto normal after + * a pcie link reset + * TBD: fill in this function if tofino can recover after an error + */ + return PCI_ERS_RESULT_DISCONNECT; +} + +/** + * bf_pci_resume - called when kernel thinks the device is up on PCIe. + * @pdev: Pointer to PCI device + * + * This callback is called when the error recovery driver tells us that + * its OK to resume normal operation. + */ +static void bf_pci_resume(struct pci_dev *pdev) { + /* this function should never be called for BF FPGA */ + struct bf_pci_dev *bfdev = pci_get_drvdata(pdev); + + printk(KERN_ERR "BF_FPGA io_resume invoked after pci error\n"); + if (bfdev) { + bfdev->info.pci_error_state = 0; + } +} + +static int bf_config_intr_mode(char *intr_str) { + if (!intr_str) { + pr_info("BF_FPGA Use MSI interrupt by default\n"); + return 0; + } + + if (!strcmp(intr_str, BF_INTR_MODE_MSIX_NAME)) { + bf_intr_mode_default = BF_INTR_MODE_MSIX; + pr_info("BF_FPGA Use MSIX interrupt\n"); + } else if (!strcmp(intr_str, BF_INTR_MODE_MSI_NAME)) { + bf_intr_mode_default = BF_INTR_MODE_MSI; + pr_info("BF_FPGA Use MSI interrupt\n"); + } else if (!strcmp(intr_str, BF_INTR_MODE_LEGACY_NAME)) { + bf_intr_mode_default = BF_INTR_MODE_LEGACY; + pr_info("BF_FPGA Use legacy interrupt\n"); + } else if (!strcmp(intr_str, BF_INTR_MODE_NONE_NAME)) { + bf_intr_mode_default = BF_INTR_MODE_NONE; + pr_info("BF_FPGA interrupt disabled\n"); + } else { + pr_info("Error: BF_FPGA bad intr_mode parameter - %s\n", intr_str); + return -EINVAL; + } + + return 0; +} + +static const struct pci_device_id bf_pci_tbl[] = { + {PCI_VDEVICE(BF, BF_FPGA_DEV_ID_JBAY_0), 0}, + /* required last entry */ + {.device = 0}}; + +/* PCI bus error handlers */ +static struct pci_error_handlers bf_pci_err_handler = { + .error_detected = bf_pci_error_detected, + .slot_reset = bf_pci_slot_reset, + .resume = bf_pci_resume, +}; + +static struct pci_driver bf_pci_driver = {.name = "bf_fpga", + .id_table = bf_pci_tbl, + .probe = bf_pci_probe, + .remove = bf_pci_remove, + .err_handler = &bf_pci_err_handler}; + +static int __init bfdrv_init(void) { + int ret; + + ret = bf_config_intr_mode(intr_mode); + if (ret < 0) { + return ret; + } + spin_lock_init(&bf_nonisr_lock); + return pci_register_driver(&bf_pci_driver); +} + +static void __exit bfdrv_exit(void) { + pci_unregister_driver(&bf_pci_driver); + intr_mode = NULL; +} + +module_init(bfdrv_init); +module_exit(bfdrv_exit); + +module_param(intr_mode, charp, S_IRUGO); +MODULE_PARM_DESC(intr_mode, + "bf-fpga interrupt mode (default=none):\n" + " " BF_INTR_MODE_MSIX_NAME + " Use MSIX interrupt\n" + " " BF_INTR_MODE_MSI_NAME + " Use MSI interrupt\n" + " " BF_INTR_MODE_LEGACY_NAME + " Use Legacy interrupt\n" + " " BF_INTR_MODE_NONE_NAME + " Use no interrupt\n" + "\n"); + +MODULE_DEVICE_TABLE(pci, bf_pci_tbl); +MODULE_DESCRIPTION("Barefoot FPGA PCI-I2C device"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); +MODULE_AUTHOR("Barefoot Networks"); diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_priv.h b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_priv.h new file mode 100644 index 0000000000..7515bde745 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_priv.h @@ -0,0 +1,141 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#ifndef _BF_FPGA_H_ +#define _BF_FPGA_H_ + +#define PCI_VENDOR_ID_BF 0x1d1c +#define BF_FPGA_DEV_ID_JBAY_0 0x01F0 + +#ifndef PCI_MSIX_ENTRY_SIZE +#define PCI_MSIX_ENTRY_SIZE 16 +#define PCI_MSIX_ENTRY_LOWER_ADDR 0 +#define PCI_MSIX_ENTRY_UPPER_ADDR 4 +#define PCI_MSIX_ENTRY_DATA 8 +#define PCI_MSIX_ENTRY_VECTOR_CTRL 12 +#define PCI_MSIX_ENTRY_CTRL_MASKBIT 1 +#endif + +#define BF_CLASS_NAME "bf_fpga" +#define BF_FPGA_MAX_DEVICE_CNT 1 +#define BF_INTR_MODE_NONE_NAME "none" +#define BF_INTR_MODE_LEGACY_NAME "legacy" +#define BF_INTR_MODE_MSI_NAME "msi" +#define BF_INTR_MODE_MSIX_NAME "msix" +#define BF_MAX_BAR_MAPS 6 +#define BF_MSIX_ENTRY_CNT 1 +#define BF_MSI_ENTRY_CNT 1 + +/* sysfs codes */ +#define BF_SYSFS_NEW_DEVICE 1 +#define BF_SYSFS_RM_DEVICE 0 +#define BF_SYSFS_I2C_START 2 + +/* interrupt mode */ +enum bf_intr_mode { + BF_INTR_MODE_NONE = 0, + BF_INTR_MODE_LEGACY, + BF_INTR_MODE_MSI, + BF_INTR_MODE_MSIX +}; + +/* device memory */ +struct bf_dev_mem { + const char *name; + phys_addr_t addr; + resource_size_t size; + void __iomem *internal_addr; +}; + +struct bf_listener { + struct bf_pci_dev *bfdev; + s32 event_count[BF_MSIX_ENTRY_CNT]; + int minor; + struct bf_listener *next; +}; + +/* device information */ +struct bf_dev_info { + struct module *owner; + struct device *dev; + int minor; + atomic_t event[BF_MSIX_ENTRY_CNT]; + wait_queue_head_t wait; + const char *version; + struct bf_dev_mem mem[BF_MAX_BAR_MAPS]; + struct msix_entry *msix_entries; + long irq; /* first irq vector */ + int num_irq; /* number of irq vectors */ + unsigned long irq_flags; /* sharable ?? */ + int pci_error_state; /* was there a pci bus error */ +}; + +/* cookie to be passed to IRQ handler, useful especially with MSIX */ +struct bf_int_vector { + struct bf_pci_dev *bf_dev; + int int_vec_offset; +}; + +/* sysfs related structs */ +#define BF_FPGA_SYSFS_CNT 64 +#define BF_FPGA_SYSFS_NAME_SIZE 32 + +struct bf_fpga_sysfs_buff { + struct device_attribute dev_attr; + char name[BF_FPGA_SYSFS_NAME_SIZE]; + int bus_id; + unsigned char i2c_addr; + size_t i2c_rd_size; /* bytes to read from the device */ + int sysfs_code; /* unique code for each sysfs file */ + struct bf_pci_dev *fpgadev; /* back pointer */ + bool in_use; +}; + +/** + * structure describing the private information for a BF pcie device. + */ +struct bf_pci_dev { + struct bf_dev_info info; + struct pci_dev *pdev; + enum bf_intr_mode mode; + u8 instance; + char name[16]; + struct bf_int_vector bf_int_vec[BF_MSIX_ENTRY_CNT]; + struct bf_listener * + listener_head; /* head of a singly linked list of listeners */ + struct bf_fpga_sysfs_buff fpga_sysfs_buff[BF_FPGA_SYSFS_CNT]; + struct bf_fpga_sysfs_buff fpga_sysfs_new_device; + struct bf_fpga_sysfs_buff fpga_sysfs_rm_device; + struct bf_fpga_sysfs_buff fpga_sysfs_st_i2c; + spinlock_t sysfs_slock; +}; + +int bf_fpga_ioctl(struct bf_pci_dev *bfdev, + unsigned int cmd, + unsigned long arg); +int bf_fpga_sysfs_add(struct bf_pci_dev *fpgadev); +void bf_fpga_sysfs_del(struct bf_pci_dev *fpgadev); + +#endif /* _BF_FPGA_H_ */ diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_sysfs.c b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_sysfs.c new file mode 100644 index 0000000000..6970a7a959 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/bf_fpga_sysfs.c @@ -0,0 +1,335 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#include +#include +#include +#include +#include +#include "bf_fpga_priv.h" +#include "bf_fpga_ioctl.h" +#include "i2c/bf_fpga_i2c.h" + +/* reads 1 byte from the i2c device */ +static ssize_t bf_fpga_sysfs_i2c_get(struct device *dev, + struct device_attribute *attr, + char *buf) { + bf_fpga_i2c_t i2c_op; + ssize_t size, cur_size; + struct bf_fpga_sysfs_buff *sysfs_buf = + container_of(attr, struct bf_fpga_sysfs_buff, dev_attr); + + if (!sysfs_buf) { + printk(KERN_ERR "fpga-i2c bad attr pointer in sysfs_read\n"); + return -ENXIO; /* something not quite right here; but, don't panic */ + } + i2c_op.num_i2c = 1; + i2c_op.one_time = 1; + i2c_op.inst_hndl.bus_id = sysfs_buf->bus_id; + i2c_op.i2c_inst[0].preemt = false; + i2c_op.i2c_inst[0].en = true; + i2c_op.i2c_inst[0].i2c_addr = sysfs_buf->i2c_addr; + i2c_op.i2c_inst[0].i2c_type = BF_FPGA_I2C_READ; + i2c_op.i2c_inst[0].delay = 0; + i2c_op.i2c_inst[0].wr_cnt = 0; + cur_size = sysfs_buf->i2c_rd_size; + /* limit to PAGE_SIZE per the sysfs contract */ + if (cur_size >= PAGE_SIZE) { + cur_size = PAGE_SIZE; + } + size = 0; + while (cur_size > 0) { + unsigned char cur_cnt; + if (cur_size > 64) { + cur_cnt = 64; + } else { + cur_cnt = (unsigned char)cur_size; + } + i2c_op.i2c_inst[0].rd_cnt = cur_cnt; + if (fpga_i2c_oneshot(&i2c_op)) { + printk(KERN_ERR + "fpga-i2c read one-shot error bus %d addr 0x%hhx status 0x%hhx\n", + i2c_op.inst_hndl.bus_id, + i2c_op.i2c_inst[0].i2c_addr, + i2c_op.i2c_inst[0].status); + return -EIO; + } + memcpy(buf, i2c_op.i2c_inst[0].rd_buf, cur_cnt); + buf += cur_cnt; + size += cur_cnt; + cur_size -= cur_cnt; + } + return size; +} + +/* write the number of bytes supplied to the i2c device, 1st byte has to be + the count(max 8) */ +static ssize_t bf_fpga_sysfs_i2c_set(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) { + bf_fpga_i2c_t i2c_op; + size_t size, cur_cnt; + struct bf_fpga_sysfs_buff *sysfs_buf = + container_of(attr, struct bf_fpga_sysfs_buff, dev_attr); + + if (!sysfs_buf || (count == 0)) { + printk(KERN_ERR "fpga-i2c bad attr pointer in sysfs_write\n"); + return -ENXIO; /* something not quite right here; but, don't panic */ + } + size = 0; + while (count > 0) { + if (count > 64) { + cur_cnt = 64; + } else { + cur_cnt = count; + } + i2c_op.i2c_inst[0].wr_cnt = cur_cnt; + i2c_op.i2c_inst[0].rd_cnt = 0; + memcpy(i2c_op.i2c_inst[0].wr_buf, buf, cur_cnt); + i2c_op.num_i2c = 1; + i2c_op.one_time = 1; + i2c_op.inst_hndl.bus_id = sysfs_buf->bus_id; + i2c_op.i2c_inst[0].preemt = false; + i2c_op.i2c_inst[0].en = true; + i2c_op.i2c_inst[0].i2c_addr = sysfs_buf->i2c_addr; + i2c_op.i2c_inst[0].i2c_type = BF_FPGA_I2C_WRITE; + i2c_op.i2c_inst[0].delay = 0; + if (fpga_i2c_oneshot(&i2c_op)) { + printk( + KERN_ERR + "fpga-i2c write one-shot error bus %d addr 0x%hhx status 0x%hhx\n", + i2c_op.inst_hndl.bus_id, + i2c_op.i2c_inst[0].i2c_addr, + i2c_op.i2c_inst[0].status); + return -EIO; + } + buf += cur_cnt; + size += cur_cnt; + count -= cur_cnt; + } + return size; +} + +static int find_matching_sysfs_buf(struct bf_pci_dev *fpgadev, + int bus_id, + unsigned char i2c_addr) { + int i; + + /* check if a sysfs entry already exists */ + for (i = 0; i < BF_FPGA_SYSFS_CNT; i++) { + if (fpgadev->fpga_sysfs_buff[i].bus_id == bus_id && + fpgadev->fpga_sysfs_buff[i].i2c_addr == i2c_addr) { + return i; + } + } + /* could not find a matching sysfs buffer */ + return -1; +} + +static ssize_t bf_fpga_sysfs_fixed_get(struct device *dev, + struct device_attribute *attr, + char *buf) { + (void)dev; + (void)attr; + (void)buf; + return -ENOSYS; +} + +static ssize_t bf_fpga_sysfs_fixed_set(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) { + struct bf_pci_dev *fpgadev; + int i, en, bus_id, ret, rd_size; + char fname[BF_FPGA_SYSFS_NAME_SIZE]; + unsigned char i2c_addr; + struct bf_fpga_sysfs_buff *new_buf; + struct bf_fpga_sysfs_buff *sysfs_buf = + container_of(attr, struct bf_fpga_sysfs_buff, dev_attr); + + if (!sysfs_buf || (count == 0)) { + printk(KERN_ERR "fpga i2c bad attr pointer in fixed_sysfs_write\n"); + return -ENXIO; /* something not quite right here; but, don't panic */ + } + fpgadev = sysfs_buf->fpgadev; + + switch (sysfs_buf->sysfs_code) { + case BF_SYSFS_NEW_DEVICE: /* new_device request */ + ret = sscanf(buf, "%s %d %hhx %d", fname, &bus_id, &i2c_addr, &rd_size); + /* default rd_size to 1 if not supplied or invalid */ + if (ret < 3) { + return -EINVAL; + } + if (ret < 4 || rd_size > PAGE_SIZE) { + rd_size = 1; + } + if (bus_id >= BF_I2C_FPGA_NUM_CTRL || i2c_addr >= 0x80) { + return -EINVAL; + } + /* find out the free sysfs_buffer to use */ + spin_lock(&fpgadev->sysfs_slock); + if (find_matching_sysfs_buf(fpgadev, bus_id, i2c_addr) != -1) { + /* there is already an matching entry */ + spin_unlock(&fpgadev->sysfs_slock); + return -ENOSPC; + } + for (i = 0; i < BF_FPGA_SYSFS_CNT; i++) { + if (!fpgadev->fpga_sysfs_buff[i].in_use) { + fpgadev->fpga_sysfs_buff[i].in_use = true; + new_buf = &fpgadev->fpga_sysfs_buff[i]; + new_buf->i2c_addr = i2c_addr; + new_buf->i2c_rd_size = (size_t)rd_size; + new_buf->bus_id = bus_id; + break; + } + } + spin_unlock(&fpgadev->sysfs_slock); + if (i >= BF_FPGA_SYSFS_CNT) { + /* no free buffer available, return with ERROR */ + return -ENOSPC; + } + /* create a new sysfs entry now */ + new_buf->dev_attr.show = bf_fpga_sysfs_i2c_get; + new_buf->dev_attr.store = bf_fpga_sysfs_i2c_set; + new_buf->fpgadev = fpgadev; + new_buf->dev_attr.attr.mode = S_IWUSR | S_IRUGO; + new_buf->sysfs_code = 0; + snprintf(new_buf->name, BF_FPGA_SYSFS_NAME_SIZE, "%s", fname); + new_buf->dev_attr.attr.name = new_buf->name; + ret = device_create_file(&(fpgadev->pdev->dev), &new_buf->dev_attr); + break; + + case BF_SYSFS_RM_DEVICE: /* remove device request */ + ret = sscanf(buf, "%d %hhx", &bus_id, &i2c_addr); + if (ret < 2) { + return -EINVAL; + } + if (bus_id >= BF_I2C_FPGA_NUM_CTRL || i2c_addr >= 0x80) { + return -EINVAL; + } + /* delete the sysfs file corresponding to the i2c address */ + spin_lock(&fpgadev->sysfs_slock); + i = find_matching_sysfs_buf(fpgadev, bus_id, i2c_addr); + if (i == -1) { + /* there is no matching entry */ + spin_unlock(&fpgadev->sysfs_slock); + return -EINVAL; + } + /* must invalidate bus_id and i2c_addr when marking the buffer + * not-in-use + */ + new_buf = &fpgadev->fpga_sysfs_buff[i]; + new_buf->i2c_addr = 0xff; + new_buf->bus_id = -1; + fpgadev->fpga_sysfs_buff[i].in_use = false; + spin_unlock(&fpgadev->sysfs_slock); + device_remove_file(&fpgadev->pdev->dev, &new_buf->dev_attr); + new_buf->name[0] = 0; /* nullify the name */ + ret = 0; + break; + + case BF_SYSFS_I2C_START: /* start-stop i2c request */ + ret = sscanf(buf, "%d %d", &bus_id, &en); + if (ret < 2) { + return -EINVAL; + } + if (bus_id >= BF_I2C_FPGA_NUM_CTRL) { + return -EINVAL; + } + if (en) { + ret = fpga_i2c_start(bus_id); + } else { + ret = fpga_i2c_stop(bus_id); + } + break; + + default: + ret = -EINVAL; + } + return ((ret == 0) ? count : ret); +} + +int bf_fpga_sysfs_add(struct bf_pci_dev *fpgadev) { + int rc = 0; + u8 *name; + + spin_lock_init(&fpgadev->sysfs_slock); + /* Add two sysfs files statically, new_device and remove_device. + * Handlers of these two fles can dynamically add more sysfs + * files (or remove files) based on the platform. + */ + fpgadev->fpga_sysfs_new_device.dev_attr.show = bf_fpga_sysfs_fixed_get; + fpgadev->fpga_sysfs_new_device.dev_attr.store = bf_fpga_sysfs_fixed_set; + fpgadev->fpga_sysfs_new_device.fpgadev = fpgadev; + fpgadev->fpga_sysfs_new_device.dev_attr.attr.mode = S_IWUSR | S_IRUGO; + fpgadev->fpga_sysfs_new_device.sysfs_code = BF_SYSFS_NEW_DEVICE; + name = fpgadev->fpga_sysfs_new_device.name; + snprintf(name, BF_FPGA_SYSFS_NAME_SIZE, "new_device"); + fpgadev->fpga_sysfs_new_device.dev_attr.attr.name = name; + rc |= device_create_file(&(fpgadev->pdev->dev), + &fpgadev->fpga_sysfs_new_device.dev_attr); + + fpgadev->fpga_sysfs_rm_device.dev_attr.show = bf_fpga_sysfs_fixed_get; + fpgadev->fpga_sysfs_rm_device.dev_attr.store = bf_fpga_sysfs_fixed_set; + fpgadev->fpga_sysfs_rm_device.fpgadev = fpgadev; + fpgadev->fpga_sysfs_rm_device.dev_attr.attr.mode = S_IWUSR | S_IRUGO; + fpgadev->fpga_sysfs_rm_device.sysfs_code = BF_SYSFS_RM_DEVICE; + name = fpgadev->fpga_sysfs_rm_device.name; + snprintf(name, BF_FPGA_SYSFS_NAME_SIZE, "remove_device"); + fpgadev->fpga_sysfs_rm_device.dev_attr.attr.name = name; + rc |= device_create_file(&(fpgadev->pdev->dev), + &fpgadev->fpga_sysfs_rm_device.dev_attr); + + /* sysfs for i2c start-stop control */ + fpgadev->fpga_sysfs_st_i2c.dev_attr.show = bf_fpga_sysfs_fixed_get; + fpgadev->fpga_sysfs_st_i2c.dev_attr.store = bf_fpga_sysfs_fixed_set; + fpgadev->fpga_sysfs_st_i2c.fpgadev = fpgadev; + fpgadev->fpga_sysfs_st_i2c.dev_attr.attr.mode = S_IWUSR | S_IRUGO; + fpgadev->fpga_sysfs_st_i2c.sysfs_code = BF_SYSFS_I2C_START; + name = fpgadev->fpga_sysfs_st_i2c.name; + snprintf(name, BF_FPGA_SYSFS_NAME_SIZE, "i2c_start"); + fpgadev->fpga_sysfs_st_i2c.dev_attr.attr.name = name; + rc |= device_create_file(&(fpgadev->pdev->dev), + &fpgadev->fpga_sysfs_st_i2c.dev_attr); + + return rc; +} + +void bf_fpga_sysfs_del(struct bf_pci_dev *fpgadev) { + int i; + + device_remove_file(&fpgadev->pdev->dev, + &fpgadev->fpga_sysfs_new_device.dev_attr); + device_remove_file(&fpgadev->pdev->dev, + &fpgadev->fpga_sysfs_rm_device.dev_attr); + device_remove_file(&fpgadev->pdev->dev, &fpgadev->fpga_sysfs_st_i2c.dev_attr); + for (i = 0; i < BF_FPGA_SYSFS_CNT; i++) { + if (fpgadev->fpga_sysfs_buff[i].in_use) { + device_remove_file(&fpgadev->pdev->dev, + &fpgadev->fpga_sysfs_buff[i].dev_attr); + } + } +} diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c.c b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c.c new file mode 100644 index 0000000000..1a622e5392 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c.c @@ -0,0 +1,516 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#include +#include "bf_fpga_i2c_priv_porting.h" +#include +#include "bf_fpga_i2c_priv.h" +#include "bf_fpga_i2c.h" +#include "bf_fpga_i2c_reg.h" + +/* allocate find physically contiguous free blocks of instructions in one time + * or periodic area and mark them "in-use" */ +static int get_next_free_index(fpga_i2c_controller_t *i2c_ctrl, + int cnt, + bool pr) { + int i, j, begin, end; + + if (pr) { + begin = FPGA_I2C_PERIODIC_BEGIN_INDEX; + end = FPGA_I2C_NUM_INST; + } else { + begin = FPGA_I2c_ONESHOT_BEGIN_INDEX; + end = FPGA_I2C_ONESHOT_NUM_INST; + } + + bf_fpga_fast_lock(&i2c_ctrl->spinlock); + for (i = begin; i < end;) { + /* check if there are cnt number of free slots here */ + for (j = 0; j < cnt; j++) { + if (i2c_ctrl->i2c_inst[i + j].in_use) { + break; + } + } + if (j == cnt) { + /* we found enough free slots, so, return i */ + break; + } else { + /* we did not find enough free slots, continue searching */ + i += (j + 1); + continue; + } + } + if (i < end) { + for (j = 0; j < cnt; j++) { + i2c_ctrl->i2c_inst[i + j].in_use = true; + } + } else { + i = -1; + } + bf_fpga_fast_unlock(&i2c_ctrl->spinlock); + return i; +} + +/* free physically contiguous in-use blocks of instructions */ +static void release_index(fpga_i2c_controller_t *i2c_ctrl, + int inst_id, + int cnt) { + int i; + + if (inst_id < 0 || (inst_id + cnt) >= FPGA_I2C_NUM_INST) { + return; /* invalid id */ + } + bf_fpga_fast_lock(&i2c_ctrl->spinlock); + for (i = inst_id; i < (inst_id + cnt); i++) { + i2c_ctrl->i2c_inst[i].in_use = false; + } + bf_fpga_fast_unlock(&i2c_ctrl->spinlock); +} + +/* convert miroseconds to i2c-instruction delay parameter */ +static int us_to_fpga_delay(int microsec) { + int delay; + + if (microsec < 10) { + delay = 0; + } else if (microsec < 100) { + delay = 1; + } else if (microsec < 1000) { + delay = 2; + } else if (microsec < 10000) { + delay = 3; + } else if (microsec < 100000) { + delay = 4; + } else if (microsec < 1000000) { + delay = 5; + } else { + delay = 6; + } + return delay; +} + +/* populate single i2c_instruction at the given instruction slot */ +static int fpga_i2c_enqueue(int bus_id, + int inst_id, + bf_fpga_i2c_inst_t *i2c_inst) { + fpga_i2c_controller_t *i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + int delay = us_to_fpga_delay(i2c_inst->delay); + uint32_t wd0 = 0, wd1 = 0; + uint32_t i2c_data[2]; + uint8_t i2c_addr, num_wr, num_rd; + + i2c_addr = i2c_inst->i2c_addr; + num_wr = i2c_inst->wr_cnt; + num_rd = i2c_inst->rd_cnt; + if (i2c_addr > 0x7F || num_wr > 129 || num_rd > 128) { + return BF_FPGA_EINVAL; + } + if (i2c_inst->preemt) { + wd0 |= I2C_INST_PMT; + } + if (i2c_inst->en) { + wd0 |= I2C_INST_EN; + } + i2c_data[0] = i2c_data[1] = 0; /* clear on init */ + switch (i2c_inst->i2c_type) { + case BF_FPGA_I2C_NOP: + /* add delay + enable */ + wd0 |= (I2C_NOP | (delay << I2C_DELAY_SHF)); + break; + case BF_FPGA_I2C_WRITE: + if (num_wr == 0) { + return BF_FPGA_EINVAL; + } + wd0 |= (I2C_WR_ADDR_DATA | (delay << I2C_DELAY_SHF)); + wd1 |= (i2c_inst->i2c_addr << I2C_DEV_ADDR_SHF); + /* copy the first byte into register address */ + wd1 |= ((i2c_inst->wr_buf[0]) << I2C_CMD_OFFSET); + wd1 |= ((num_wr - 1) << I2C_WR_CNT_SHF); + if (num_wr <= 9) { + /* copy data into instruction area */ + memcpy(i2c_data, &i2c_inst->wr_buf[1], (num_wr - 1)); + bf_fpga_i2c_reg_write32( + i2c_ctrl, Bf_FPGA_I2C_INST_DATA_LO(inst_id), i2c_data[0]); + bf_fpga_i2c_reg_write32( + i2c_ctrl, Bf_FPGA_I2C_INST_DATA_HI(inst_id), i2c_data[1]); + } else { + /* copy the data in data area */ + int len = num_wr - 1; + uint32_t addr; + uint8_t *val = (uint8_t *)(&i2c_inst->wr_buf[1]); + /* store the data pointer Note the indexing required by FPGA specs */ + i2c_data[0] = BF_FPGA_I2C_DATA_AREA(inst_id); + addr = i2c_data[0]; + bf_fpga_i2c_reg_write32( + i2c_ctrl, Bf_FPGA_I2C_INST_DATA_LO(inst_id), i2c_data[0] / 4); + /* do byte write to avoid endianness mismatch */ + while (len--) { + bf_fpga_i2c_reg_write8(i2c_ctrl, addr, *val); + addr++; + val++; + } + } + break; + case BF_FPGA_I2C_READ: + if (num_rd == 0) { + return BF_FPGA_EINVAL; + } + wd0 |= (I2C_RD_DATA | (delay << I2C_DELAY_SHF)); + wd1 |= (i2c_inst->i2c_addr << I2C_DEV_ADDR_SHF); + wd1 |= ((num_rd) << I2C_RD_CNT_SHF); + if (num_rd > 8) { + /* store the data area pointer */ + i2c_data[0] = BF_FPGA_I2C_DATA_AREA(inst_id); + bf_fpga_i2c_reg_write32( + i2c_ctrl, Bf_FPGA_I2C_INST_DATA_LO(inst_id), i2c_data[0] / 4); + } + break; + case BF_FPGA_I2C_ADDR_READ: + if (num_wr == 0 || num_rd == 0) { + return BF_FPGA_EINVAL; + } + wd0 |= (I2C_RD_ADDR_DATA_BURST | (delay << I2C_DELAY_SHF)); + wd1 |= (i2c_inst->i2c_addr << I2C_DEV_ADDR_SHF); + /* 1st byte of the write buf goes into "register address" field */ + wd1 |= ((num_wr - 1) << I2C_WR_CNT_SHF); + wd1 |= ((i2c_inst->wr_buf[0]) << I2C_CMD_OFFSET); + wd1 |= ((num_rd) << I2C_RD_CNT_SHF); + /* less than 8 bytes data goes to the instruction area */ + if ((num_wr - 1 + num_rd) <= 8) { + memcpy(i2c_data, &i2c_inst->wr_buf[1], (num_wr - 1)); + bf_fpga_i2c_reg_write32( + i2c_ctrl, Bf_FPGA_I2C_INST_DATA_LO(inst_id), i2c_data[0]); + bf_fpga_i2c_reg_write32( + i2c_ctrl, Bf_FPGA_I2C_INST_DATA_HI(inst_id), i2c_data[1]); + } else { + int len = num_wr - 1; + uint32_t addr; + uint8_t *val = (uint8_t *)(&i2c_inst->wr_buf[1]); + /* store the data area pointer */ + i2c_data[0] = BF_FPGA_I2C_DATA_AREA(inst_id); + addr = i2c_data[0]; + bf_fpga_i2c_reg_write32( + i2c_ctrl, Bf_FPGA_I2C_INST_DATA_LO(inst_id), i2c_data[0] / 4); + /* copy the data in data area */ + while (len--) { + bf_fpga_i2c_reg_write8(i2c_ctrl, addr, *val); + addr++; + val++; + } + } + break; + default: + return BF_FPGA_EINVAL; + } + bf_fpga_i2c_reg_write32(i2c_ctrl, Bf_FPGA_I2C_INST_PARAM(inst_id), wd1); + bf_fpga_i2c_reg_write32(i2c_ctrl, Bf_FPGA_I2C_INST_CTRL(inst_id), wd0); + return BF_FPGA_OK; +} + +/* get the i2c completion status of a particular instruction */ +static uint32_t fpga_i2c_get_status(int bus_id, int inst_id) { + fpga_i2c_controller_t *i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + uint32_t addr = Bf_FPGA_I2C_INST_CTRL(inst_id); + return (bf_fpga_i2c_reg_read32(i2c_ctrl, addr) & I2C_STATUS_MASK); +} + +/** FPGA I2C data read (assumes locked by caller and no need to stop i2c) + * + * read the data following a read type i2c operation + * + * @param bus_id + * i2c controller id + * @param inst_id + * instruction id within this controller space + * @param offset + * offset in the data-area where read-data is available + * @param num_rd + * number of bytes to read + * @param rd_buf + * buffer to read into + * @return + * 0 on success and <0 on error + */ +static int fpga_i2c_data_read_locked(fpga_i2c_controller_t *i2c_ctrl, + int inst_id, + uint8_t offset, + uint8_t num_rd, + uint8_t *rd_buf) { + uint8_t i; + uint32_t addr, data_cnt; + + if (!i2c_ctrl || !rd_buf || !num_rd || inst_id < 0 || + inst_id >= FPGA_I2C_NUM_INST) { + return BF_FPGA_EINVAL; + } + /* find out the wr_cnt + rd_cnt from the already executed instruction field */ + data_cnt = bf_fpga_i2c_reg_read32(i2c_ctrl, Bf_FPGA_I2C_INST_PARAM(inst_id)); + /* point to data area if the (wr_cnt + rd_cnt) > 8 */ + data_cnt &= 0xffff; /* retain only the length fields */ + if (((data_cnt & 0xff) + (data_cnt >> 8)) <= 8) { + addr = Bf_FPGA_I2C_INST_DATA_LO(inst_id) + offset; + } else { + addr = BF_FPGA_I2C_DATA_AREA(inst_id) + offset; + } + for (i = 0; i < num_rd; i++) { + *rd_buf = bf_fpga_i2c_reg_read8(i2c_ctrl, addr); + addr++; + rd_buf++; + } + return BF_FPGA_OK; +} + +/** FPGA I2C data read + * + * read the data following a read type i2c operation + * + * @param bus_id + * i2c controller id + * @param inst_id + * instruction id within this controller space + * @param offset + * offset in the data-area where read-data is available + * @param num_rd + * number of bytes to read + * @param rd_buf + * buffer to read into + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_data_read( + int bus_id, int inst_id, uint8_t offset, uint8_t num_rd, uint8_t *rd_buf) { + fpga_i2c_controller_t *i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + int ret; + bool i2c_running; + uint8_t val; + + if (!i2c_ctrl || !rd_buf || !num_rd || inst_id < 0 || + inst_id >= FPGA_I2C_NUM_INST) { + return BF_FPGA_EINVAL; + } + + /* aligned (upto) 4 bytes can be read without stopping the ongoing i2c, + * this is guaranteed by FPGA design. i2c has to be stopped, in all other + * cases, to read a consistent set of read-data. + */ + if ((offset % 4 == 0) && (num_rd <= 4)) { + return ( + fpga_i2c_data_read_locked(i2c_ctrl, inst_id, offset, num_rd, rd_buf)); + } + + /* non-aligned case; stop i2c if running, read data and restart i2c */ + if (bf_fpga_i2c_lock(i2c_ctrl)) { + return BF_FPGA_EAGAIN; + } + /* check if i2c_controller is running */ + val = bf_fpga_i2c_reg_read8(i2c_ctrl, Bf_FPGA_TOP_I2C_STATUS); + i2c_running = ((val & I2C_STS_BUSY) ? true : false); + if (i2c_running) { + /* stop ongoing i2c operations */ + fpga_i2c_stop_locked(i2c_ctrl); + } + ret = fpga_i2c_data_read_locked(i2c_ctrl, inst_id, offset, num_rd, rd_buf); + if (i2c_running) { + /* restart ongoing i2c operations */ + fpga_i2c_start_locked(i2c_ctrl); + } + bf_fpga_i2c_unlock(i2c_ctrl); + return ret; +} + +/** FPGA I2C onetime i2c operation + * + * @param i2c_op + * bf_fpga_i2c_t parameters * + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_oneshot(bf_fpga_i2c_t *i2c_op) { + int i, ret; + uint32_t val; + int bus_id; + fpga_i2c_controller_t *i2c_ctrl; + + if (!i2c_op) { + return BF_FPGA_EINVAL; + } + + bus_id = i2c_op->inst_hndl.bus_id; + i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + + if (i2c_op->num_i2c == 0 || i2c_op->num_i2c >= FPGA_I2C_ONESHOT_NUM_INST || + i2c_op->one_time == 0 || bus_id >= BF_I2C_FPGA_NUM_CTRL || !i2c_ctrl) { + return BF_FPGA_EINVAL; + } + if (bf_fpga_i2c_lock(i2c_ctrl)) { + return BF_FPGA_EAGAIN; + } + /* stop ongoing i2c operations */ + ret = fpga_i2c_stop_locked(i2c_ctrl); + if (ret) { + bf_fpga_i2c_unlock(i2c_ctrl); + return ret; + } + /* populate one time i2c operation instruction(s) from offset zero */ + for (i = 0; i < i2c_op->num_i2c; i++) { + ret = fpga_i2c_enqueue(bus_id, i, &i2c_op->i2c_inst[i]); + if (ret) { + goto oneshot_error_exit; + } + } + /* start i2c operations */ + ret = fpga_i2c_start_locked(i2c_ctrl); + if (ret) { + goto oneshot_error_exit; + } + + /* wait until complete and read the data if necessary */ + for (i = 0; i < i2c_op->num_i2c; i++) { + int cnt; + val = 0; + /* cnt is roughly the number of bytes of this i2c cycle + * overhead of 100 bytes for for worst case timeout, one + * should not hit that in normal working case + */ + cnt = i2c_op->i2c_inst[i].wr_cnt + i2c_op->i2c_inst[i].rd_cnt; + /* bump up the cnt for an i2c transaction containing some data + * for computing worst case timeout */ + if (cnt > 0) { + cnt = cnt + 100; + } + while (!(val & I2C_STATUS_COMPLETED) && (cnt-- > 0)) { + /* 1 byte ~= 10 bits takes 25 microsec on i2c cycle at 400khz */ + bf_fpga_us_delay(50); + val = fpga_i2c_get_status(bus_id, i); + } + i2c_op->i2c_inst[i].status = val; /* store the h/w status */ + if (val & I2C_STATUS_ERR_MASK) { + ret = BF_FPGA_EIO; + goto oneshot_error_exit; + } + if (i2c_op->i2c_inst[i].rd_cnt) { + uint8_t offset = 0; + if (i2c_op->i2c_inst[i].wr_cnt > 1) { + offset = i2c_op->i2c_inst[i].wr_cnt - 1; + } + if (fpga_i2c_data_read_locked(i2c_ctrl, + i, + offset, + i2c_op->i2c_inst[i].rd_cnt, + i2c_op->i2c_inst[i].rd_buf)) { + ret = BF_FPGA_EIO; + goto oneshot_error_exit; + } + } + } + ret = BF_FPGA_OK; + +oneshot_error_exit: + for (i = 0; i < i2c_op->num_i2c; i++) { + /* cleanup the enable bit */ + val = bf_fpga_i2c_reg_read32(i2c_ctrl, Bf_FPGA_I2C_INST_CTRL(i)); + val &= (~I2C_INST_EN); + bf_fpga_i2c_reg_write32(i2c_ctrl, Bf_FPGA_I2C_INST_CTRL(i), val); + } + bf_fpga_i2c_unlock(i2c_ctrl); + return ret; +} + +/** FPGA I2C insert periodic i2c operation + * + * @param i2c_op + * bf_fpga_i2c_t parameters * + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_pr_add(bf_fpga_i2c_t *i2c_op) { + fpga_i2c_controller_t *i2c_ctrl; + int i, ret, next_id; + bool preemt; + + if (!i2c_op) { + return BF_FPGA_EINVAL; + } + i2c_ctrl = fpga_i2c_ctrl_get(i2c_op->inst_hndl.bus_id); + if (!i2c_ctrl) { + return BF_FPGA_EINVAL; + } + if (bf_fpga_i2c_lock(i2c_ctrl)) { + return BF_FPGA_EAGAIN; + } + /* get the next available free slot */ + next_id = get_next_free_index(i2c_ctrl, i2c_op->num_i2c, true); + if (next_id < 0) { + bf_fpga_i2c_unlock(i2c_ctrl); + return BF_FPGA_EBUSY; + } + /* populate periodic i2c operation instruction(s) */ + for (i = 0; i < i2c_op->num_i2c; i++) { + preemt = ((i == (i2c_op->num_i2c - 1)) ? false : true); + i2c_op->i2c_inst[i].preemt = preemt; + ret = fpga_i2c_enqueue( + i2c_op->inst_hndl.bus_id, next_id + i, &i2c_op->i2c_inst[i]); + + if (ret) { + bf_fpga_i2c_unlock(i2c_ctrl); + return ret; + } + } + bf_fpga_i2c_unlock(i2c_ctrl); + i2c_op->inst_hndl.inst_id = next_id; + return BF_FPGA_OK; +} + +/** FPGA I2C remove periodic i2c operation(s) from instruction memory + * + * @param i2c_op + * bf_fpga_i2c_t parameters * + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_del(bf_fpga_i2c_t *i2c_op) { + fpga_i2c_controller_t *i2c_ctrl; + int i, inst_id; + + if (!i2c_op) { + return BF_FPGA_EINVAL; + } + i2c_ctrl = fpga_i2c_ctrl_get(i2c_op->inst_hndl.bus_id); + if (!i2c_ctrl) { + return BF_FPGA_EINVAL; + } + inst_id = i2c_op->inst_hndl.inst_id; + if (bf_fpga_i2c_lock(i2c_ctrl)) { + return BF_FPGA_EAGAIN; + } + for (i = 0; i < i2c_op->num_i2c; i++) { + /* nullify the instruction */ + bf_fpga_i2c_reg_write32(i2c_ctrl, Bf_FPGA_I2C_INST_CTRL(inst_id + i), 0); + } + /* reset the in_use flag */ + release_index(i2c_ctrl, inst_id, i2c_op->num_i2c); + bf_fpga_i2c_unlock(i2c_ctrl); + return BF_FPGA_OK; +} diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c.h b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c.h new file mode 100644 index 0000000000..6f56ed270b --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c.h @@ -0,0 +1,55 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#ifndef _BF_FPGA_I2C_H +#define _BF_FPGA_I2C_H + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +int fpga_i2c_start(int bus_id); +int fpga_i2c_stop(int bus_id); +int fpga_i2c_reset(int bus_id); +int fpga_i2c_is_busy(int bus_id, bool *is_busy); +int fpga_i2c_inst_en(int bus_id, int inst_id, bool en); +int fpga_i2c_set_clk(int bus_id, int clock_div); +int fpga_i2c_controller_init(int bus_id); +int fpga_i2c_controller_cleanup(int bus_id); +int fpga_i2c_init(uint8_t *base_addr); +void fpga_i2c_deinit(void); +int fpga_i2c_oneshot(bf_fpga_i2c_t *i2c_op); +int fpga_i2c_pr_add(bf_fpga_i2c_t *i2c_op); +int fpga_i2c_del(bf_fpga_i2c_t *i2c_op); +int fpga_i2c_data_read( + int bus_id, int inst_id, uint8_t offset, uint8_t len, uint8_t *buf); +bool fpga_i2c_is_inited(void); + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* _BF_FPGA_I2C_H */ diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_ctrl.c b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_ctrl.c new file mode 100644 index 0000000000..a8837ba3b6 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_ctrl.c @@ -0,0 +1,397 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#include +#include "bf_fpga_i2c_priv_porting.h" +#include +#include "bf_fpga_i2c.h" +#include "bf_fpga_i2c_priv.h" +#include "bf_fpga_i2c_reg.h" + +/* static i2c controller contents */ +static fpga_i2c_controller_t fpga_i2c_ctrl[BF_I2C_FPGA_NUM_CTRL]; +static bool fpga_i2c_inited = false; + +/* fpga memory space access APIs */ +/* 32 bit write into fpga BAR0 */ +void bf_fpga_reg_write(fpga_i2c_controller_t *i2c_ctrl, + uint32_t offset, + uint32_t val) { + uint8_t *ptr = i2c_ctrl->fpga_base_addr + offset; + bf_fpga_write32(ptr, val); +} + +/* 32 bit read into fpga BAR0 */ +uint32_t bf_fpga_reg_read(fpga_i2c_controller_t *i2c_ctrl, uint32_t offset) { + uint8_t *ptr = i2c_ctrl->fpga_base_addr + offset; + return (bf_fpga_read32(ptr)); +} + +/* 32 bit write into fpga i2c space */ +void bf_fpga_i2c_reg_write32(fpga_i2c_controller_t *i2c_ctrl, + uint32_t offset, + uint32_t val) { + uint8_t *ptr = i2c_ctrl->i2c_base_addr + offset; + bf_fpga_write32(ptr, val); +} + +/* 32 bit read into fpga i2c space */ +uint32_t bf_fpga_i2c_reg_read32(fpga_i2c_controller_t *i2c_ctrl, + uint32_t offset) { + uint8_t *ptr = i2c_ctrl->i2c_base_addr + offset; + return (bf_fpga_read32(ptr)); +} + +/* 8 bit write into fpga i2c space */ +void bf_fpga_i2c_reg_write8(fpga_i2c_controller_t *i2c_ctrl, + uint32_t offset, + uint8_t val) { + uint8_t *ptr = i2c_ctrl->i2c_base_addr + offset; + bf_fpga_write8(ptr, val); +} + +/* 8 bit read into fpga i2c space */ +uint8_t bf_fpga_i2c_reg_read8(fpga_i2c_controller_t *i2c_ctrl, + uint32_t offset) { + uint8_t *ptr = i2c_ctrl->i2c_base_addr + offset; + return (bf_fpga_read8(ptr)); +} + +int bf_fpga_i2c_lock(fpga_i2c_controller_t *i2c_ctrl) { + return (bf_fpga_cr_enter(&i2c_ctrl->fpga_ctrl_lock)); +} + +void bf_fpga_i2c_unlock(fpga_i2c_controller_t *i2c_ctrl) { + return (bf_fpga_cr_leave(&i2c_ctrl->fpga_ctrl_lock)); +} + +/** FPGA return pointer to i2c_controller struct + * + * @param bus_id + * i2c controller id + * @return + * pointer to i2c_controller struct of + */ +fpga_i2c_controller_t *fpga_i2c_ctrl_get(int bus_id) { + if (bus_id >= BF_I2C_FPGA_NUM_CTRL) { + return NULL; + } else { + return &fpga_i2c_ctrl[bus_id]; + } +} + +/* is fpga module is soft inited */ +bool fpga_i2c_is_inited() { return fpga_i2c_inited; } + +/** FPGA I2C set clock: sets clock of i2c operations + * + * controller must be stopped before, if applicable. + * + * @param bus_id + * i2c controller id + * @param clk_div + * clock divider value as per fpga specs + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_set_clk(int bus_id, int clk_div) { + uint32_t val; + fpga_i2c_controller_t *i2c_ctrl; + + if (bus_id >= BF_I2C_FPGA_NUM_CTRL) { + return BF_FPGA_EINVAL; + } + i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + if (bf_fpga_i2c_lock(i2c_ctrl)) { + return BF_FPGA_EAGAIN; + } + clk_div = (clk_div & I2C_CTRL_CLK_DIV_MASK) << I2C_CTRL_CLK_DIV_SHF; + val = bf_fpga_i2c_reg_read32(i2c_ctrl, Bf_FPGA_I2C_CTRL_TOP); + val &= ~(I2C_CTRL_CLK_DIV_MASK << I2C_CTRL_CLK_DIV_SHF); + val |= clk_div; + bf_fpga_i2c_reg_write32(i2c_ctrl, Bf_FPGA_I2C_CTRL_TOP, val); + bf_fpga_i2c_unlock(i2c_ctrl); + return BF_FPGA_OK; +} + +/** FPGA I2C stop : stops ongoing i2c operations without mutex locking + * + * internal function + * + * @param bus_id + * i2c controller struct + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_stop_locked(fpga_i2c_controller_t *i2c_ctrl) { + int to_ms, ret; + uint8_t val; + + val = bf_fpga_i2c_reg_read8(i2c_ctrl, Bf_FPGA_I2C_CTRL_TOP); + val &= ~I2C_CTRL_START; + bf_fpga_i2c_reg_write8(i2c_ctrl, Bf_FPGA_I2C_CTRL_TOP, val); + + to_ms = 100; /* 5 msec converted to multiple of 50 micro sec */ + val = bf_fpga_i2c_reg_read8(i2c_ctrl, Bf_FPGA_TOP_I2C_STATUS); + while ((val & I2C_STS_BUSY) && to_ms) { + bf_fpga_us_delay(50); + to_ms--; + val = bf_fpga_i2c_reg_read8(i2c_ctrl, Bf_FPGA_TOP_I2C_STATUS); + } + if (to_ms > 0) { + ret = BF_FPGA_OK; + } else { + ret = BF_FPGA_EIO; + } + return ret; +} + +/** FPGA I2C start : starts i2c operations without mutex locking + * + * internal function + * + * @param bus_id + * i2c controller struct + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_start_locked(fpga_i2c_controller_t *i2c_ctrl) { + uint8_t val; + val = bf_fpga_i2c_reg_read8(i2c_ctrl, Bf_FPGA_I2C_CTRL_TOP); + bf_fpga_i2c_reg_write8(i2c_ctrl, Bf_FPGA_I2C_CTRL_TOP, val | I2C_CTRL_START); + return BF_FPGA_OK; +} + +/** FPGA I2C stop : stops ongoing i2c operations + * + * @param bus_id + * i2c controller id + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_stop(int bus_id) { + fpga_i2c_controller_t *i2c_ctrl; + int ret; + + if (bus_id >= BF_I2C_FPGA_NUM_CTRL || !fpga_i2c_is_inited()) { + return BF_FPGA_EINVAL; + } + i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + if (bf_fpga_i2c_lock(i2c_ctrl)) { + return BF_FPGA_EAGAIN; + } + ret = fpga_i2c_stop_locked(i2c_ctrl); + bf_fpga_i2c_unlock(i2c_ctrl); + return ret; +} + +/** FPGA I2C start : starts i2c operations + * + * @param bus_id + * i2c controller id + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_start(int bus_id) { + fpga_i2c_controller_t *i2c_ctrl; + int ret; + + if (bus_id >= BF_I2C_FPGA_NUM_CTRL || !fpga_i2c_is_inited()) { + return BF_FPGA_EINVAL; + } + i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + if (bf_fpga_i2c_lock(i2c_ctrl)) { + return BF_FPGA_EAGAIN; + } + ret = fpga_i2c_start_locked(i2c_ctrl); + bf_fpga_i2c_unlock(i2c_ctrl); + return ret; +} + +/** FPGA I2C reset: reset i2c by issuing 9 clocks in a specific way + * + * @param bus_id + * i2c controller id + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_reset(int bus_id) { + fpga_i2c_controller_t *i2c_ctrl; + + if (bus_id >= BF_I2C_FPGA_NUM_CTRL || !fpga_i2c_is_inited()) { + return BF_FPGA_EINVAL; + } + i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + if (bf_fpga_i2c_lock(i2c_ctrl)) { + return BF_FPGA_EAGAIN; + } + bf_fpga_i2c_reg_write8(i2c_ctrl, Bf_FPGA_I2C_CTRL_TOP, I2C_CTRL_RESET); + bf_fpga_i2c_unlock(i2c_ctrl); + return BF_FPGA_OK; +} + +/** FPGA I2C is running? + * + * @param bus_id + * i2c controller id + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_is_busy(int bus_id, bool *busy) { + uint8_t val; + fpga_i2c_controller_t *i2c_ctrl; + + if (bus_id >= BF_I2C_FPGA_NUM_CTRL || !fpga_i2c_is_inited()) { + return BF_FPGA_EINVAL; + } + i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + if (bf_fpga_i2c_lock(i2c_ctrl)) { + return BF_FPGA_EAGAIN; + } + val = bf_fpga_i2c_reg_read8(i2c_ctrl, Bf_FPGA_TOP_I2C_STATUS); + *busy = ((val & I2C_STS_BUSY) ? true : false); + bf_fpga_i2c_unlock(i2c_ctrl); + return BF_FPGA_OK; +} + +/** FPGA I2C instruction enable/disable + * + * enable or disable a particular instruction in i2c instruction memory + * @param bus_id + * i2c controller id + * @param inst_id + * instruction id within this controller space + * @param en + * true for enable, false for disable + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_inst_en(int bus_id, int inst_id, bool en) { + uint32_t val; + fpga_i2c_controller_t *i2c_ctrl; + + if (bus_id >= BF_I2C_FPGA_NUM_CTRL || !fpga_i2c_is_inited()) { + return BF_FPGA_EINVAL; + } + if (inst_id < 0 || inst_id >= FPGA_I2C_NUM_INST) { + return BF_FPGA_EINVAL; + } + i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + if (bf_fpga_i2c_lock(i2c_ctrl)) { + return BF_FPGA_EAGAIN; + } + val = bf_fpga_i2c_reg_read32(i2c_ctrl, Bf_FPGA_I2C_INST_CTRL(inst_id)); + if (en) { + val |= I2C_INST_EN; + } else { + val &= ~I2C_INST_EN; + } + bf_fpga_i2c_reg_write32(i2c_ctrl, Bf_FPGA_I2C_INST_CTRL(inst_id), val); + fpga_i2c_ctrl[bus_id].i2c_inst[inst_id].en = en; + bf_fpga_i2c_unlock(i2c_ctrl); + return BF_FPGA_OK; +} + +/** FPGA I2C controller initialization + * + * @param bus_id + * i2c controller id + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_controller_init(int bus_id) { + fpga_i2c_controller_t *i2c_ctrl; + int i, ret; + + if (bus_id >= BF_I2C_FPGA_NUM_CTRL) { + return BF_FPGA_EINVAL; + } + i2c_ctrl = fpga_i2c_ctrl_get(bus_id); + if (!i2c_ctrl) { + return BF_FPGA_EINVAL; + } + bf_fpga_fast_lock_init(&i2c_ctrl->spinlock, 0); + bf_fpga_cr_init(&i2c_ctrl->fpga_ctrl_lock); + bf_fpga_cr_enter(&i2c_ctrl->fpga_ctrl_lock); + for (i = 0; i < FPGA_I2C_NUM_INST; i++) { + fpga_i2c_ctrl[bus_id].i2c_inst[i].inst = (uint32_t)i; + bf_fpga_i2c_reg_write32(i2c_ctrl, Bf_FPGA_I2C_INST_CTRL(i), 0); + } + bf_fpga_cr_leave(&i2c_ctrl->fpga_ctrl_lock); + ret = fpga_i2c_set_clk(bus_id, 1); /* 400 khz default */ + ret |= fpga_i2c_stop(bus_id); /* just in case */ + return ret; +} + +/** FPGA I2C controller de initialization + * + * @param bus_id + * i2c controller id + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_controller_cleanup(int bus_id) { + int i; + + fpga_i2c_stop(bus_id); + for (i = 0; i < FPGA_I2C_NUM_INST; i++) { + fpga_i2c_ctrl[bus_id].i2c_inst[i].en = false; + } + bf_fpga_cr_destroy(&fpga_i2c_ctrl[bus_id].fpga_ctrl_lock); + bf_fpga_fast_lock_destroy(&fpga_i2c_ctrl[bus_id].spinlock); + return BF_FPGA_OK; +} + +/** FPGA I2C global initialization + * + * @param base_addr + * virtual address of i2c memory relative to BAR0 base + * @return + * 0 on success and <0 on error + */ +int fpga_i2c_init(uint8_t *base_addr) { + int i; + + memset(fpga_i2c_ctrl, 0, sizeof(fpga_i2c_ctrl)); + for (i = 0; i < BF_I2C_FPGA_NUM_CTRL; i++) { + fpga_i2c_ctrl[i].i2c_base_addr = base_addr + BF_FPGA_I2C_CTRL_BASE_ADDR(i); + fpga_i2c_ctrl[i].fpga_base_addr = base_addr; + fpga_i2c_controller_init(i); + } + fpga_i2c_inited = true; + return BF_FPGA_OK; +} + +/** FPGA I2C global de initialization + * + */ +void fpga_i2c_deinit(void) { + int i; + + for (i = 0; i < BF_I2C_FPGA_NUM_CTRL; i++) { + fpga_i2c_controller_cleanup(i); + } + fpga_i2c_inited = false; +} diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_porting.c b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_porting.c new file mode 100644 index 0000000000..8b126c2e6d --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_porting.c @@ -0,0 +1,150 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#include +#include +#include +#include +#include +#include "bf_fpga_i2c_priv_porting.h" +#include +#include "bf_fpga_i2c.h" +#include "bf_fpga_i2c_priv.h" + +/* This file contains OS and system specific porting functions for i2c APIs. + * Implementation in this file is for porting to linux kernel. + */ +/* mutex APIs are for mutual exclusion with capability to sleep while in + * exclusion mode + */ + +/* sleepable virtual exclusion region */ +typedef struct { + atomic_t lock_state; /* 1: in exclusion mode, 0: not in exclusion mode */ +} sleepable_v_mutex_t; + +int bf_fpga_cr_init(bf_fpga_mutex_t *lock) { + sleepable_v_mutex_t *mtx; + + if (!lock) { + return -1; + } + mtx = vzalloc(sizeof(sleepable_v_mutex_t)); + + if (mtx) { + atomic_set(&mtx->lock_state, 0); + *lock = (bf_fpga_mutex_t *)mtx; + return 0; + } else { + *lock = NULL; + return -1; + } +} + +void bf_fpga_cr_destroy(bf_fpga_mutex_t *lock) { + if (lock && *lock) { + vfree(*lock); + *lock = NULL; + } +} + +void bf_fpga_cr_leave(bf_fpga_mutex_t *lock) { + sleepable_v_mutex_t *mtx; + + if (lock && *lock) { + mtx = (sleepable_v_mutex_t *)*lock; + atomic_xchg(&mtx->lock_state, 0); + } +} + +int bf_fpga_cr_enter(bf_fpga_mutex_t *lock) { + sleepable_v_mutex_t *mtx; + + /* All we do here is: test and set */ + if (lock && *lock) { + int cnt = 10000; /* This will provide maximum of 500-1000 ms timeout */ + mtx = (sleepable_v_mutex_t *)*lock; + while (atomic_cmpxchg(&mtx->lock_state, 0, 1) != 0) { + if (cnt-- <= 0) { + return -1; /* this is a worst case timeout situation */ + } + usleep_range(50, 100); /* 50 us = about 2 bytes at 400Kbs i2c */ + } + return 0; + } else { + return -1; + } +} + +/* **** not implemented in current mode of locking */ +int bf_fpga_mutex_trylock(bf_fpga_mutex_t *lock) { + if (lock && *lock) { + return -1; + } else { + return -1; + } +} + +/* fast lock is a non-blocking busy lock, implemented with spinlock */ +int bf_fpga_fast_lock_init(bf_fpga_fast_lock_t *sl, unsigned int initial) { + spinlock_t *slock; + + (void)initial; + if (!sl) { + return -1; + } + slock = vzalloc(sizeof(spinlock_t)); + + if (slock) { + spin_lock_init(slock); + *sl = (bf_fpga_fast_lock_t *)slock; + return 0; + } else { + *sl = NULL; + return -1; + } +} + +void bf_fpga_fast_lock_destroy(bf_fpga_fast_lock_t *sl) { + if (sl && *sl) { + vfree(*sl); + *sl = NULL; + } +} + +int bf_fpga_fast_lock(bf_fpga_fast_lock_t *sl) { + if (sl && *sl) { + spin_lock(*sl); + return 0; + } else { + return -1; + } +} + +void bf_fpga_fast_unlock(bf_fpga_fast_lock_t *sl) { + if (sl && *sl) { + spin_unlock(*sl); + } +} diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_priv.h b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_priv.h new file mode 100644 index 0000000000..0b3b1e47b0 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_priv.h @@ -0,0 +1,104 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#ifndef _BF_FPGA_I2C_PRIV_H +#define _BF_FPGA_I2C_PRIV_H + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +#define FPGA_I2C_INST_OFFSET(idx) (0x10 + (16 * idx)) + +typedef enum { + FGPA_I2C_NOP = 0x0, + FGPA_I2C_WR_ADDR_DATA = 0x1, /* wr: reg_addr, wr: data */ + FGPA_I2C_WR_ADDR_RD_DATA = 0x2, /* wr: reg_addrss, r/s, rd: data */ + FGPA_I2C_WR_ADDR = 0x3, /* wr: reg_addr */ + FGPA_I2C_RD_DATA = 0x4, /* rd: data */ + FGPA_I2C_MULTI_WR_RD = 0x5 /* wr: n bytes, r/s, rd: m bytes */ +} i2c_cmd_type; + +/* contents of each instruction instance */ +typedef struct i2c_inst_cmd_s { + i2c_cmd_type i2c_cmd; /* i2x cycle type */ + uint32_t data_lo; /* lower 4 bytes of data associated with this inst */ + uint32_t data_hi; /* upper 4 bytes of data associated with this inst */ + uint32_t us_delay; /* delay before i2c cycle */ + uint8_t i2c_addr; /* i2c device address, in 7 bit format */ + uint8_t reg_addr; /* 1st write byte, if present in i2c cycle */ + uint8_t num_read; /* number of bytes to write excluding reg_addr */ + uint8_t num_write; /* number of bytes to read */ +} i2c_inst_cmd_t; + +/* attributes of each instruction instance */ +typedef struct i2c_inst_s { + uint32_t inst; /* index of the instruction within the controller memory */ + bool en; /* is instruction enabled */ + bool preemt; /* atomically execute next instruction */ + bool int_en; /* enable interrupt after execution */ + bool in_use; /* is this instruction currently used */ +} i2c_inst_t; + +typedef struct fpga_i2c_controller_s { + bf_fpga_mutex_t fpga_ctrl_lock; + bf_fpga_fast_lock_t spinlock; + uint8_t *fpga_base_addr; /* virtual address of start of fpga memory */ + uint8_t *i2c_base_addr; /* virtual address of i2c controller memory */ + uint32_t start; /* offset of start of i2c instruction memory */ + uint32_t len; /* number of i2c instructions belonging to this i2c engine */ + uint32_t clk_div; /* clock divider used by this i2c engine */ + bool int_en; + i2c_inst_t i2c_inst[FPGA_I2C_NUM_INST]; +} fpga_i2c_controller_t; + +fpga_i2c_controller_t *fpga_i2c_ctrl_get(int bus_id); +void bf_fpga_reg_write(fpga_i2c_controller_t *i2c_ctrl, + uint32_t offset, + uint32_t val); +uint32_t bf_fpga_reg_read(fpga_i2c_controller_t *i2c_ctrl, uint32_t offset); +void bf_fpga_i2c_reg_write32(fpga_i2c_controller_t *i2c_ctrl, + uint32_t offset, + uint32_t val); +uint32_t bf_fpga_i2c_reg_read32(fpga_i2c_controller_t *i2c_ctrl, + uint32_t offset); +void bf_fpga_i2c_reg_write8(fpga_i2c_controller_t *i2c_ctrl, + uint32_t offset, + uint8_t val); +uint8_t bf_fpga_i2c_reg_read8(fpga_i2c_controller_t *i2c_ctrl, uint32_t offset); + +int bf_fpga_i2c_lock(fpga_i2c_controller_t *i2c_ctrl); + +void bf_fpga_i2c_unlock(fpga_i2c_controller_t *i2c_ctrl); + +int fpga_i2c_start_locked(fpga_i2c_controller_t *i2c_ctrl); +int fpga_i2c_stop_locked(fpga_i2c_controller_t *i2c_ctrl); + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* _BF_FPGA_I2C_PRIV_H */ diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_priv_porting.h b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_priv_porting.h new file mode 100644 index 0000000000..adea5c9311 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_priv_porting.h @@ -0,0 +1,93 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#ifndef _BF_FPGA_I2C_PRIV_PORTING_H +#define _BF_FPGA_I2C_PRIV_PORTING_H + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* This file contains OS and system specific porting functions declarations. + */ +/* return status compliant with linux system calls */ +#define BF_FPGA_OK 0 +#define BF_FPGA_EINVAL (-EINVAL) +#define BF_FPGA_EIO (-EIO) +#define BF_FPGA_EBUSY (-EBUSY) +#define BF_FPGA_EAGAIN (-EAGAIN) + +/* pci memory access functions */ +static inline void bf_fpga_write32(uint8_t *addr, uint32_t val) { + u8 __iomem *reg_addr = addr; + writel(val, reg_addr); +} + +static inline uint32_t bf_fpga_read32(uint8_t *addr) { + u8 __iomem *reg_addr = addr; + return (readl(reg_addr)); +} + +static inline void bf_fpga_write8(uint8_t *addr, uint8_t val) { + u8 __iomem *reg_addr = addr; + writeb(val, reg_addr); +} + +static inline uint8_t bf_fpga_read8(uint8_t *addr) { + u8 __iomem *reg_addr = addr; + return (readb(reg_addr)); +} + +static inline void bf_fpga_us_delay(unsigned long usecs) { + usleep_range(usecs, usecs + 10); +} + +/* general purpose mutual exclusion lock */ +typedef void *bf_fpga_mutex_t; + +/* fast_lock for locking only non-blocking and quick operations */ +typedef void *bf_fpga_fast_lock_t; + +/* APIs to init/enter/leave critical (exclusive access) regions */ +int bf_fpga_cr_init(bf_fpga_mutex_t *lock); +void bf_fpga_cr_destroy(bf_fpga_mutex_t *lock); +int bf_fpga_cr_enter(bf_fpga_mutex_t *lock); +void bf_fpga_cr_leave(bf_fpga_mutex_t *lock); + +int bf_fpga_fast_lock_init(bf_fpga_fast_lock_t *sl, unsigned int initial); +void bf_fpga_fast_lock_destroy(bf_fpga_fast_lock_t *sl); +int bf_fpga_fast_lock(bf_fpga_fast_lock_t *sl); +void bf_fpga_fast_unlock(bf_fpga_fast_lock_t *sl); + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* _BF_FPGA_I2C_PRIV_PORTING_H */ diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_reg.h b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_reg.h new file mode 100644 index 0000000000..9ad9b31b3b --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/modules/i2c/bf_fpga_i2c_reg.h @@ -0,0 +1,116 @@ +/******************************************************************************* + Barefoot Networks FPGA Linux driver + Copyright(c) 2018 - 2019 Barefoot Networks, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + info@barefootnetworks.com + Barefoot Networks, 4750 Patrick Henry Drive, Santa Clara CA 95054 + +*******************************************************************************/ +#ifndef _BF_FPGA_I2C_REG_H +#define _BF_FPGA_I2C_REG_H + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* registers outsize of all i2c controller register space */ +#define BF_FPGA_I2C_BASE_ADDR 0x0 +#define BF_FPGA_I2C_CTRL_BASE_ADDR(i) (BF_FPGA_I2C_BASE_ADDR + (i * 4096)) + +/* per i2c controller register offset relative to BF_FPGA_I2C_CTRL_BASE_ADDR */ +#define Bf_FPGA_I2C_CTRL_TOP 0 +#define I2C_CTRL_START (1 << 0) +#define I2C_CTRL_RESET (1 << 1) +#define I2C_CTRL_CLK_DIV_SHF (8) +#define I2C_CTRL_CLK_DIV_MASK (0x1FF) + +#define Bf_FPGA_TOP_I2C_STATUS 4 +#define I2C_STS_BUSY (1 << 0) +#define I2C_STS_ERR (1 << 3) + +#define Bf_FPGA_I2C_INST_CTRL(i) (0x10 + (16 * i)) +#define I2C_INST_EN (1 << 31) +#define I2C_INST_PMT (1 << 30) +#define I2C_TYPE_SHF (26) +#define I2C_DELAY_SHF (23) +#define I2C_STOP_ON_ERROR (22) +/* various status values */ +#define I2C_STATUS_MASK 0x3F +#define I2C_STATUS_ERR_MASK 0x3C +#define I2C_STATUS_RUNNING 0x1 +#define I2C_STATUS_COMPLETED 0x2 +#define I2C_STATUS_NACK_ADDR 0x4 +#define I2C_STATUS_NACK_CMD 0x8 +#define I2C_STATUS_NACK_WR_DATA 0x10 +#define I2C_STATUS_TOUT 0x20 + +/* i2c instruction types */ +#define I2C_WR_ADDR_DATA (0 << I2C_TYPE_SHF) +#define I2C_RD_DATA (3 << I2C_TYPE_SHF) +#define I2C_WR_ADDR (2 << I2C_TYPE_SHF) +#define I2C_RD_ADDR_DATA (1 << I2C_TYPE_SHF) +#define I2C_RD_ADDR_DATA_BURST (4 << I2C_TYPE_SHF) +#define I2C_NOP (6 << I2C_TYPE_SHF) + +#define Bf_FPGA_I2C_INST_PARAM(i) (0x14 + (16 * i)) +#define I2C_DEV_ADDR_SHF (24) +#define I2C_CMD_OFFSET (16) +#define I2C_WR_CNT_SHF (8) +#define I2C_RD_CNT_SHF (0) + +#define Bf_FPGA_I2C_INST_DATA_LO(i) (0x18 + (16 * i)) +#define Bf_FPGA_I2C_INST_DATA_HI(i) (0x1C + (16 * i)) +/****************** +#define Bf_FPGA_I2C_PR_CTRL(i) (0x100 + (16 * i)) +#define Bf_FPGA_I2C_PR_PARM(i) (0x104 + (16 * i)) +#define Bf_FPGA_I2C_PR_LO(i) (0x108 + (16 * i)) +#define Bf_FPGA_I2C_PR_HI(i) (0x10C + (16 * i)) +******************/ + +/* data area pointers */ +/* the driver makes fixed static allocation of the available memory + * on per instruction basis + * allocate 128 bytes per one time instruction = total 0x780 bytes + * allocate 64 bytes per one periodic instruction = total 0x400 bytes + * FPGA_I2C_ONESHOT_NUM_INST -> comes from a header file that must be included + * before this file. + */ +#define BF_FPGA_ONE_MAX_BURST 128 +#define BF_FPGA_PR_MAX_BURST 64 +#define BF_FPGA_I2C_DATA_AREA(i) \ + ((i < FPGA_I2C_ONESHOT_NUM_INST) \ + ? (0x200 + (i * BF_FPGA_ONE_MAX_BURST)) \ + : (0x980 + ((i - FPGA_I2C_ONESHOT_NUM_INST) * BF_FPGA_PR_MAX_BURST))) + +#if BF_FPGA_I2C_DATA_AREA(FPGA_I2C_PERIODIC_NUM_INST) > 0x1000 +#error erroneous allocation of FPGA memory to i2c data area. Fix it! +#endif + +#define BF_FPGA_VER_REG 0x3F000 +#define BF_FPGA_BUILD_DATE 0x3F004 +#define BF_FPGA_RESET_CTRL_1 0x3F008 +#define BF_FPGA_RESET_CTRL_2 0x3F00C + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* _BF_FPGA_I2C_REG_H */ diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/bf-sfputil b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/bf-sfputil new file mode 100755 index 0000000000..3df67614e4 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/bf-sfputil @@ -0,0 +1,10 @@ +#!/bin/bash + +DOCKER_EXEC_FLAGS="i" + +# Determine whether stdout is on a terminal +if [ -t 1 ] ; then + DOCKER_EXEC_FLAGS+="t" +fi + +docker exec -$DOCKER_EXEC_FLAGS syncd sfputil "$@" diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/eeprom b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/eeprom new file mode 100755 index 0000000000..07d98556cb --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/eeprom @@ -0,0 +1,10 @@ +#!/bin/bash + +DOCKER_EXEC_FLAGS="i" + +# Determine whether stdout is on a terminal +if [ -t 1 ] ; then + DOCKER_EXEC_FLAGS+="t" +fi + +docker exec -$DOCKER_EXEC_FLAGS syncd eeprom "$@" diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/fancontrol b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/fancontrol new file mode 100755 index 0000000000..515fcbdd69 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/fancontrol @@ -0,0 +1,11 @@ +#!/bin/bash + +DOCKER_EXEC_FLAGS="i" + +# Determine whether stdout is on a terminal +if [ -t 1 ] ; then + DOCKER_EXEC_FLAGS+="t" +fi + +docker exec -$DOCKER_EXEC_FLAGS syncd fancontrol "$@" + diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/ps_info b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/ps_info new file mode 100755 index 0000000000..38c9d33304 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/ps_info @@ -0,0 +1,10 @@ +#!/bin/bash + +DOCKER_EXEC_FLAGS="i" + +# Determine whether stdout is on a terminal +if [ -t 1 ] ; then + DOCKER_EXEC_FLAGS+="t" +fi + +docker exec -$DOCKER_EXEC_FLAGS syncd ps_info "$@" diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/sensors b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/sensors new file mode 100755 index 0000000000..07af695532 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/sensors @@ -0,0 +1,12 @@ +#!/bin/bash + +DOCKER_EXEC_FLAGS="i" + +# Determine whether stdout is on a terminal +if [ -t 1 ] ; then + DOCKER_EXEC_FLAGS+="t" +fi + +docker exec -$DOCKER_EXEC_FLAGS syncd sensors "$@" + + diff --git a/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/test b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/test new file mode 100755 index 0000000000..38327722c9 --- /dev/null +++ b/platform/barefoot/sonic-platform-modules-bfn-newport/scripts/test @@ -0,0 +1 @@ +echo "test"