diff options
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/Kconfig | 10 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/Makefile | 4 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 67 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.h | 103 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2.c | 9 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2.h | 85 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2_avb.c | 193 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2_scratch.c | 240 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/hwtstamp.c | 584 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/hwtstamp.h | 172 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/ptp.c | 381 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/ptp.h | 108 | ||||
-rw-r--r-- | include/linux/ptp_classify.h | 4 | ||||
-rw-r--r-- | include/net/dsa.h | 20 | ||||
-rw-r--r-- | net/dsa/dsa.c | 36 | ||||
-rw-r--r-- | net/dsa/slave.c | 59 |
16 files changed, 2070 insertions, 5 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/Kconfig b/drivers/net/dsa/mv88e6xxx/Kconfig index 1aaa7a95ebc4..ae9e7f7cb31c 100644 --- a/drivers/net/dsa/mv88e6xxx/Kconfig +++ b/drivers/net/dsa/mv88e6xxx/Kconfig | |||
@@ -18,3 +18,13 @@ config NET_DSA_MV88E6XXX_GLOBAL2 | |||
18 | 18 | ||
19 | It is required on most chips. If the chip you compile the support for | 19 | It is required on most chips. If the chip you compile the support for |
20 | doesn't have such registers set, say N here. In doubt, say Y. | 20 | doesn't have such registers set, say N here. In doubt, say Y. |
21 | |||
22 | config NET_DSA_MV88E6XXX_PTP | ||
23 | bool "PTP support for Marvell 88E6xxx" | ||
24 | default n | ||
25 | depends on NET_DSA_MV88E6XXX_GLOBAL2 | ||
26 | imply NETWORK_PHY_TIMESTAMPING | ||
27 | imply PTP_1588_CLOCK | ||
28 | help | ||
29 | Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch | ||
30 | chips that support it. | ||
diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index 58a4a0014e59..50de304abe2f 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile | |||
@@ -5,6 +5,10 @@ mv88e6xxx-objs += global1.o | |||
5 | mv88e6xxx-objs += global1_atu.o | 5 | mv88e6xxx-objs += global1_atu.o |
6 | mv88e6xxx-objs += global1_vtu.o | 6 | mv88e6xxx-objs += global1_vtu.o |
7 | mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o | 7 | mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o |
8 | mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_avb.o | ||
9 | mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_scratch.o | ||
10 | mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o | ||
8 | mv88e6xxx-objs += phy.o | 11 | mv88e6xxx-objs += phy.o |
9 | mv88e6xxx-objs += port.o | 12 | mv88e6xxx-objs += port.o |
13 | mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o | ||
10 | mv88e6xxx-objs += serdes.o | 14 | mv88e6xxx-objs += serdes.o |
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index eb328bade225..af63710e93c1 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c | |||
@@ -36,8 +36,10 @@ | |||
36 | #include "chip.h" | 36 | #include "chip.h" |
37 | #include "global1.h" | 37 | #include "global1.h" |
38 | #include "global2.h" | 38 | #include "global2.h" |
39 | #include "hwtstamp.h" | ||
39 | #include "phy.h" | 40 | #include "phy.h" |
40 | #include "port.h" | 41 | #include "port.h" |
42 | #include "ptp.h" | ||
41 | #include "serdes.h" | 43 | #include "serdes.h" |
42 | 44 | ||
43 | static void assert_reg_lock(struct mv88e6xxx_chip *chip) | 45 | static void assert_reg_lock(struct mv88e6xxx_chip *chip) |
@@ -2092,6 +2094,17 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) | |||
2092 | if (err) | 2094 | if (err) |
2093 | goto unlock; | 2095 | goto unlock; |
2094 | 2096 | ||
2097 | /* Setup PTP Hardware Clock and timestamping */ | ||
2098 | if (chip->info->ptp_support) { | ||
2099 | err = mv88e6xxx_ptp_setup(chip); | ||
2100 | if (err) | ||
2101 | goto unlock; | ||
2102 | |||
2103 | err = mv88e6xxx_hwtstamp_setup(chip); | ||
2104 | if (err) | ||
2105 | goto unlock; | ||
2106 | } | ||
2107 | |||
2095 | unlock: | 2108 | unlock: |
2096 | mutex_unlock(&chip->reg_lock); | 2109 | mutex_unlock(&chip->reg_lock); |
2097 | 2110 | ||
@@ -2472,6 +2485,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { | |||
2472 | .reset = mv88e6352_g1_reset, | 2485 | .reset = mv88e6352_g1_reset, |
2473 | .vtu_getnext = mv88e6352_g1_vtu_getnext, | 2486 | .vtu_getnext = mv88e6352_g1_vtu_getnext, |
2474 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, | 2487 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, |
2488 | .gpio_ops = &mv88e6352_gpio_ops, | ||
2475 | }; | 2489 | }; |
2476 | 2490 | ||
2477 | static const struct mv88e6xxx_ops mv88e6161_ops = { | 2491 | static const struct mv88e6xxx_ops mv88e6161_ops = { |
@@ -2602,6 +2616,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { | |||
2602 | .vtu_getnext = mv88e6352_g1_vtu_getnext, | 2616 | .vtu_getnext = mv88e6352_g1_vtu_getnext, |
2603 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, | 2617 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, |
2604 | .serdes_power = mv88e6352_serdes_power, | 2618 | .serdes_power = mv88e6352_serdes_power, |
2619 | .gpio_ops = &mv88e6352_gpio_ops, | ||
2605 | }; | 2620 | }; |
2606 | 2621 | ||
2607 | static const struct mv88e6xxx_ops mv88e6175_ops = { | 2622 | static const struct mv88e6xxx_ops mv88e6175_ops = { |
@@ -2673,6 +2688,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { | |||
2673 | .vtu_getnext = mv88e6352_g1_vtu_getnext, | 2688 | .vtu_getnext = mv88e6352_g1_vtu_getnext, |
2674 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, | 2689 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, |
2675 | .serdes_power = mv88e6352_serdes_power, | 2690 | .serdes_power = mv88e6352_serdes_power, |
2691 | .gpio_ops = &mv88e6352_gpio_ops, | ||
2676 | }; | 2692 | }; |
2677 | 2693 | ||
2678 | static const struct mv88e6xxx_ops mv88e6185_ops = { | 2694 | static const struct mv88e6xxx_ops mv88e6185_ops = { |
@@ -2736,6 +2752,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { | |||
2736 | .vtu_getnext = mv88e6390_g1_vtu_getnext, | 2752 | .vtu_getnext = mv88e6390_g1_vtu_getnext, |
2737 | .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, | 2753 | .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, |
2738 | .serdes_power = mv88e6390_serdes_power, | 2754 | .serdes_power = mv88e6390_serdes_power, |
2755 | .gpio_ops = &mv88e6352_gpio_ops, | ||
2739 | }; | 2756 | }; |
2740 | 2757 | ||
2741 | static const struct mv88e6xxx_ops mv88e6190x_ops = { | 2758 | static const struct mv88e6xxx_ops mv88e6190x_ops = { |
@@ -2771,6 +2788,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { | |||
2771 | .vtu_getnext = mv88e6390_g1_vtu_getnext, | 2788 | .vtu_getnext = mv88e6390_g1_vtu_getnext, |
2772 | .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, | 2789 | .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, |
2773 | .serdes_power = mv88e6390_serdes_power, | 2790 | .serdes_power = mv88e6390_serdes_power, |
2791 | .gpio_ops = &mv88e6352_gpio_ops, | ||
2774 | }; | 2792 | }; |
2775 | 2793 | ||
2776 | static const struct mv88e6xxx_ops mv88e6191_ops = { | 2794 | static const struct mv88e6xxx_ops mv88e6191_ops = { |
@@ -2843,6 +2861,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { | |||
2843 | .vtu_getnext = mv88e6352_g1_vtu_getnext, | 2861 | .vtu_getnext = mv88e6352_g1_vtu_getnext, |
2844 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, | 2862 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, |
2845 | .serdes_power = mv88e6352_serdes_power, | 2863 | .serdes_power = mv88e6352_serdes_power, |
2864 | .gpio_ops = &mv88e6352_gpio_ops, | ||
2865 | .avb_ops = &mv88e6352_avb_ops, | ||
2846 | }; | 2866 | }; |
2847 | 2867 | ||
2848 | static const struct mv88e6xxx_ops mv88e6290_ops = { | 2868 | static const struct mv88e6xxx_ops mv88e6290_ops = { |
@@ -2879,6 +2899,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { | |||
2879 | .vtu_getnext = mv88e6390_g1_vtu_getnext, | 2899 | .vtu_getnext = mv88e6390_g1_vtu_getnext, |
2880 | .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, | 2900 | .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, |
2881 | .serdes_power = mv88e6390_serdes_power, | 2901 | .serdes_power = mv88e6390_serdes_power, |
2902 | .gpio_ops = &mv88e6352_gpio_ops, | ||
2903 | .avb_ops = &mv88e6390_avb_ops, | ||
2882 | }; | 2904 | }; |
2883 | 2905 | ||
2884 | static const struct mv88e6xxx_ops mv88e6320_ops = { | 2906 | static const struct mv88e6xxx_ops mv88e6320_ops = { |
@@ -2913,6 +2935,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { | |||
2913 | .reset = mv88e6352_g1_reset, | 2935 | .reset = mv88e6352_g1_reset, |
2914 | .vtu_getnext = mv88e6185_g1_vtu_getnext, | 2936 | .vtu_getnext = mv88e6185_g1_vtu_getnext, |
2915 | .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, | 2937 | .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, |
2938 | .gpio_ops = &mv88e6352_gpio_ops, | ||
2939 | .avb_ops = &mv88e6352_avb_ops, | ||
2916 | }; | 2940 | }; |
2917 | 2941 | ||
2918 | static const struct mv88e6xxx_ops mv88e6321_ops = { | 2942 | static const struct mv88e6xxx_ops mv88e6321_ops = { |
@@ -2945,6 +2969,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { | |||
2945 | .reset = mv88e6352_g1_reset, | 2969 | .reset = mv88e6352_g1_reset, |
2946 | .vtu_getnext = mv88e6185_g1_vtu_getnext, | 2970 | .vtu_getnext = mv88e6185_g1_vtu_getnext, |
2947 | .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, | 2971 | .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, |
2972 | .gpio_ops = &mv88e6352_gpio_ops, | ||
2973 | .avb_ops = &mv88e6352_avb_ops, | ||
2948 | }; | 2974 | }; |
2949 | 2975 | ||
2950 | static const struct mv88e6xxx_ops mv88e6341_ops = { | 2976 | static const struct mv88e6xxx_ops mv88e6341_ops = { |
@@ -2981,6 +3007,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { | |||
2981 | .reset = mv88e6352_g1_reset, | 3007 | .reset = mv88e6352_g1_reset, |
2982 | .vtu_getnext = mv88e6352_g1_vtu_getnext, | 3008 | .vtu_getnext = mv88e6352_g1_vtu_getnext, |
2983 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, | 3009 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, |
3010 | .gpio_ops = &mv88e6352_gpio_ops, | ||
3011 | .avb_ops = &mv88e6390_avb_ops, | ||
2984 | }; | 3012 | }; |
2985 | 3013 | ||
2986 | static const struct mv88e6xxx_ops mv88e6350_ops = { | 3014 | static const struct mv88e6xxx_ops mv88e6350_ops = { |
@@ -3049,6 +3077,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { | |||
3049 | .reset = mv88e6352_g1_reset, | 3077 | .reset = mv88e6352_g1_reset, |
3050 | .vtu_getnext = mv88e6352_g1_vtu_getnext, | 3078 | .vtu_getnext = mv88e6352_g1_vtu_getnext, |
3051 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, | 3079 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, |
3080 | .avb_ops = &mv88e6352_avb_ops, | ||
3052 | }; | 3081 | }; |
3053 | 3082 | ||
3054 | static const struct mv88e6xxx_ops mv88e6352_ops = { | 3083 | static const struct mv88e6xxx_ops mv88e6352_ops = { |
@@ -3086,6 +3115,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { | |||
3086 | .vtu_getnext = mv88e6352_g1_vtu_getnext, | 3115 | .vtu_getnext = mv88e6352_g1_vtu_getnext, |
3087 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, | 3116 | .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, |
3088 | .serdes_power = mv88e6352_serdes_power, | 3117 | .serdes_power = mv88e6352_serdes_power, |
3118 | .gpio_ops = &mv88e6352_gpio_ops, | ||
3119 | .avb_ops = &mv88e6352_avb_ops, | ||
3089 | }; | 3120 | }; |
3090 | 3121 | ||
3091 | static const struct mv88e6xxx_ops mv88e6390_ops = { | 3122 | static const struct mv88e6xxx_ops mv88e6390_ops = { |
@@ -3124,6 +3155,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { | |||
3124 | .vtu_getnext = mv88e6390_g1_vtu_getnext, | 3155 | .vtu_getnext = mv88e6390_g1_vtu_getnext, |
3125 | .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, | 3156 | .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, |
3126 | .serdes_power = mv88e6390_serdes_power, | 3157 | .serdes_power = mv88e6390_serdes_power, |
3158 | .gpio_ops = &mv88e6352_gpio_ops, | ||
3159 | .avb_ops = &mv88e6390_avb_ops, | ||
3127 | }; | 3160 | }; |
3128 | 3161 | ||
3129 | static const struct mv88e6xxx_ops mv88e6390x_ops = { | 3162 | static const struct mv88e6xxx_ops mv88e6390x_ops = { |
@@ -3162,6 +3195,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { | |||
3162 | .vtu_getnext = mv88e6390_g1_vtu_getnext, | 3195 | .vtu_getnext = mv88e6390_g1_vtu_getnext, |
3163 | .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, | 3196 | .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, |
3164 | .serdes_power = mv88e6390_serdes_power, | 3197 | .serdes_power = mv88e6390_serdes_power, |
3198 | .gpio_ops = &mv88e6352_gpio_ops, | ||
3199 | .avb_ops = &mv88e6390_avb_ops, | ||
3165 | }; | 3200 | }; |
3166 | 3201 | ||
3167 | static const struct mv88e6xxx_info mv88e6xxx_table[] = { | 3202 | static const struct mv88e6xxx_info mv88e6xxx_table[] = { |
@@ -3267,6 +3302,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3267 | .name = "Marvell 88E6341", | 3302 | .name = "Marvell 88E6341", |
3268 | .num_databases = 4096, | 3303 | .num_databases = 4096, |
3269 | .num_ports = 6, | 3304 | .num_ports = 6, |
3305 | .num_gpio = 11, | ||
3270 | .max_vid = 4095, | 3306 | .max_vid = 4095, |
3271 | .port_base_addr = 0x10, | 3307 | .port_base_addr = 0x10, |
3272 | .global1_addr = 0x1b, | 3308 | .global1_addr = 0x1b, |
@@ -3346,6 +3382,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3346 | .name = "Marvell 88E6172", | 3382 | .name = "Marvell 88E6172", |
3347 | .num_databases = 4096, | 3383 | .num_databases = 4096, |
3348 | .num_ports = 7, | 3384 | .num_ports = 7, |
3385 | .num_gpio = 15, | ||
3349 | .max_vid = 4095, | 3386 | .max_vid = 4095, |
3350 | .port_base_addr = 0x10, | 3387 | .port_base_addr = 0x10, |
3351 | .global1_addr = 0x1b, | 3388 | .global1_addr = 0x1b, |
@@ -3386,6 +3423,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3386 | .name = "Marvell 88E6176", | 3423 | .name = "Marvell 88E6176", |
3387 | .num_databases = 4096, | 3424 | .num_databases = 4096, |
3388 | .num_ports = 7, | 3425 | .num_ports = 7, |
3426 | .num_gpio = 15, | ||
3389 | .max_vid = 4095, | 3427 | .max_vid = 4095, |
3390 | .port_base_addr = 0x10, | 3428 | .port_base_addr = 0x10, |
3391 | .global1_addr = 0x1b, | 3429 | .global1_addr = 0x1b, |
@@ -3424,6 +3462,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3424 | .name = "Marvell 88E6190", | 3462 | .name = "Marvell 88E6190", |
3425 | .num_databases = 4096, | 3463 | .num_databases = 4096, |
3426 | .num_ports = 11, /* 10 + Z80 */ | 3464 | .num_ports = 11, /* 10 + Z80 */ |
3465 | .num_gpio = 16, | ||
3427 | .max_vid = 8191, | 3466 | .max_vid = 8191, |
3428 | .port_base_addr = 0x0, | 3467 | .port_base_addr = 0x0, |
3429 | .global1_addr = 0x1b, | 3468 | .global1_addr = 0x1b, |
@@ -3444,6 +3483,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3444 | .name = "Marvell 88E6190X", | 3483 | .name = "Marvell 88E6190X", |
3445 | .num_databases = 4096, | 3484 | .num_databases = 4096, |
3446 | .num_ports = 11, /* 10 + Z80 */ | 3485 | .num_ports = 11, /* 10 + Z80 */ |
3486 | .num_gpio = 16, | ||
3447 | .max_vid = 8191, | 3487 | .max_vid = 8191, |
3448 | .port_base_addr = 0x0, | 3488 | .port_base_addr = 0x0, |
3449 | .global1_addr = 0x1b, | 3489 | .global1_addr = 0x1b, |
@@ -3475,6 +3515,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3475 | .pvt = true, | 3515 | .pvt = true, |
3476 | .multi_chip = true, | 3516 | .multi_chip = true, |
3477 | .tag_protocol = DSA_TAG_PROTO_DSA, | 3517 | .tag_protocol = DSA_TAG_PROTO_DSA, |
3518 | .ptp_support = true, | ||
3478 | .ops = &mv88e6191_ops, | 3519 | .ops = &mv88e6191_ops, |
3479 | }, | 3520 | }, |
3480 | 3521 | ||
@@ -3484,6 +3525,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3484 | .name = "Marvell 88E6240", | 3525 | .name = "Marvell 88E6240", |
3485 | .num_databases = 4096, | 3526 | .num_databases = 4096, |
3486 | .num_ports = 7, | 3527 | .num_ports = 7, |
3528 | .num_gpio = 15, | ||
3487 | .max_vid = 4095, | 3529 | .max_vid = 4095, |
3488 | .port_base_addr = 0x10, | 3530 | .port_base_addr = 0x10, |
3489 | .global1_addr = 0x1b, | 3531 | .global1_addr = 0x1b, |
@@ -3495,6 +3537,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3495 | .pvt = true, | 3537 | .pvt = true, |
3496 | .multi_chip = true, | 3538 | .multi_chip = true, |
3497 | .tag_protocol = DSA_TAG_PROTO_EDSA, | 3539 | .tag_protocol = DSA_TAG_PROTO_EDSA, |
3540 | .ptp_support = true, | ||
3498 | .ops = &mv88e6240_ops, | 3541 | .ops = &mv88e6240_ops, |
3499 | }, | 3542 | }, |
3500 | 3543 | ||
@@ -3504,6 +3547,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3504 | .name = "Marvell 88E6290", | 3547 | .name = "Marvell 88E6290", |
3505 | .num_databases = 4096, | 3548 | .num_databases = 4096, |
3506 | .num_ports = 11, /* 10 + Z80 */ | 3549 | .num_ports = 11, /* 10 + Z80 */ |
3550 | .num_gpio = 16, | ||
3507 | .max_vid = 8191, | 3551 | .max_vid = 8191, |
3508 | .port_base_addr = 0x0, | 3552 | .port_base_addr = 0x0, |
3509 | .global1_addr = 0x1b, | 3553 | .global1_addr = 0x1b, |
@@ -3515,6 +3559,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3515 | .pvt = true, | 3559 | .pvt = true, |
3516 | .multi_chip = true, | 3560 | .multi_chip = true, |
3517 | .tag_protocol = DSA_TAG_PROTO_DSA, | 3561 | .tag_protocol = DSA_TAG_PROTO_DSA, |
3562 | .ptp_support = true, | ||
3518 | .ops = &mv88e6290_ops, | 3563 | .ops = &mv88e6290_ops, |
3519 | }, | 3564 | }, |
3520 | 3565 | ||
@@ -3524,6 +3569,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3524 | .name = "Marvell 88E6320", | 3569 | .name = "Marvell 88E6320", |
3525 | .num_databases = 4096, | 3570 | .num_databases = 4096, |
3526 | .num_ports = 7, | 3571 | .num_ports = 7, |
3572 | .num_gpio = 15, | ||
3527 | .max_vid = 4095, | 3573 | .max_vid = 4095, |
3528 | .port_base_addr = 0x10, | 3574 | .port_base_addr = 0x10, |
3529 | .global1_addr = 0x1b, | 3575 | .global1_addr = 0x1b, |
@@ -3534,6 +3580,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3534 | .pvt = true, | 3580 | .pvt = true, |
3535 | .multi_chip = true, | 3581 | .multi_chip = true, |
3536 | .tag_protocol = DSA_TAG_PROTO_EDSA, | 3582 | .tag_protocol = DSA_TAG_PROTO_EDSA, |
3583 | .ptp_support = true, | ||
3537 | .ops = &mv88e6320_ops, | 3584 | .ops = &mv88e6320_ops, |
3538 | }, | 3585 | }, |
3539 | 3586 | ||
@@ -3543,6 +3590,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3543 | .name = "Marvell 88E6321", | 3590 | .name = "Marvell 88E6321", |
3544 | .num_databases = 4096, | 3591 | .num_databases = 4096, |
3545 | .num_ports = 7, | 3592 | .num_ports = 7, |
3593 | .num_gpio = 15, | ||
3546 | .max_vid = 4095, | 3594 | .max_vid = 4095, |
3547 | .port_base_addr = 0x10, | 3595 | .port_base_addr = 0x10, |
3548 | .global1_addr = 0x1b, | 3596 | .global1_addr = 0x1b, |
@@ -3552,6 +3600,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3552 | .atu_move_port_mask = 0xf, | 3600 | .atu_move_port_mask = 0xf, |
3553 | .multi_chip = true, | 3601 | .multi_chip = true, |
3554 | .tag_protocol = DSA_TAG_PROTO_EDSA, | 3602 | .tag_protocol = DSA_TAG_PROTO_EDSA, |
3603 | .ptp_support = true, | ||
3555 | .ops = &mv88e6321_ops, | 3604 | .ops = &mv88e6321_ops, |
3556 | }, | 3605 | }, |
3557 | 3606 | ||
@@ -3561,6 +3610,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3561 | .name = "Marvell 88E6341", | 3610 | .name = "Marvell 88E6341", |
3562 | .num_databases = 4096, | 3611 | .num_databases = 4096, |
3563 | .num_ports = 6, | 3612 | .num_ports = 6, |
3613 | .num_gpio = 11, | ||
3564 | .max_vid = 4095, | 3614 | .max_vid = 4095, |
3565 | .port_base_addr = 0x10, | 3615 | .port_base_addr = 0x10, |
3566 | .global1_addr = 0x1b, | 3616 | .global1_addr = 0x1b, |
@@ -3571,6 +3621,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3571 | .pvt = true, | 3621 | .pvt = true, |
3572 | .multi_chip = true, | 3622 | .multi_chip = true, |
3573 | .tag_protocol = DSA_TAG_PROTO_EDSA, | 3623 | .tag_protocol = DSA_TAG_PROTO_EDSA, |
3624 | .ptp_support = true, | ||
3574 | .ops = &mv88e6341_ops, | 3625 | .ops = &mv88e6341_ops, |
3575 | }, | 3626 | }, |
3576 | 3627 | ||
@@ -3620,6 +3671,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3620 | .name = "Marvell 88E6352", | 3671 | .name = "Marvell 88E6352", |
3621 | .num_databases = 4096, | 3672 | .num_databases = 4096, |
3622 | .num_ports = 7, | 3673 | .num_ports = 7, |
3674 | .num_gpio = 15, | ||
3623 | .max_vid = 4095, | 3675 | .max_vid = 4095, |
3624 | .port_base_addr = 0x10, | 3676 | .port_base_addr = 0x10, |
3625 | .global1_addr = 0x1b, | 3677 | .global1_addr = 0x1b, |
@@ -3631,6 +3683,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3631 | .pvt = true, | 3683 | .pvt = true, |
3632 | .multi_chip = true, | 3684 | .multi_chip = true, |
3633 | .tag_protocol = DSA_TAG_PROTO_EDSA, | 3685 | .tag_protocol = DSA_TAG_PROTO_EDSA, |
3686 | .ptp_support = true, | ||
3634 | .ops = &mv88e6352_ops, | 3687 | .ops = &mv88e6352_ops, |
3635 | }, | 3688 | }, |
3636 | [MV88E6390] = { | 3689 | [MV88E6390] = { |
@@ -3639,6 +3692,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3639 | .name = "Marvell 88E6390", | 3692 | .name = "Marvell 88E6390", |
3640 | .num_databases = 4096, | 3693 | .num_databases = 4096, |
3641 | .num_ports = 11, /* 10 + Z80 */ | 3694 | .num_ports = 11, /* 10 + Z80 */ |
3695 | .num_gpio = 16, | ||
3642 | .max_vid = 8191, | 3696 | .max_vid = 8191, |
3643 | .port_base_addr = 0x0, | 3697 | .port_base_addr = 0x0, |
3644 | .global1_addr = 0x1b, | 3698 | .global1_addr = 0x1b, |
@@ -3650,6 +3704,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3650 | .pvt = true, | 3704 | .pvt = true, |
3651 | .multi_chip = true, | 3705 | .multi_chip = true, |
3652 | .tag_protocol = DSA_TAG_PROTO_DSA, | 3706 | .tag_protocol = DSA_TAG_PROTO_DSA, |
3707 | .ptp_support = true, | ||
3653 | .ops = &mv88e6390_ops, | 3708 | .ops = &mv88e6390_ops, |
3654 | }, | 3709 | }, |
3655 | [MV88E6390X] = { | 3710 | [MV88E6390X] = { |
@@ -3658,6 +3713,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3658 | .name = "Marvell 88E6390X", | 3713 | .name = "Marvell 88E6390X", |
3659 | .num_databases = 4096, | 3714 | .num_databases = 4096, |
3660 | .num_ports = 11, /* 10 + Z80 */ | 3715 | .num_ports = 11, /* 10 + Z80 */ |
3716 | .num_gpio = 16, | ||
3661 | .max_vid = 8191, | 3717 | .max_vid = 8191, |
3662 | .port_base_addr = 0x0, | 3718 | .port_base_addr = 0x0, |
3663 | .global1_addr = 0x1b, | 3719 | .global1_addr = 0x1b, |
@@ -3669,6 +3725,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { | |||
3669 | .pvt = true, | 3725 | .pvt = true, |
3670 | .multi_chip = true, | 3726 | .multi_chip = true, |
3671 | .tag_protocol = DSA_TAG_PROTO_DSA, | 3727 | .tag_protocol = DSA_TAG_PROTO_DSA, |
3728 | .ptp_support = true, | ||
3672 | .ops = &mv88e6390x_ops, | 3729 | .ops = &mv88e6390x_ops, |
3673 | }, | 3730 | }, |
3674 | }; | 3731 | }; |
@@ -3880,6 +3937,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { | |||
3880 | .port_mdb_del = mv88e6xxx_port_mdb_del, | 3937 | .port_mdb_del = mv88e6xxx_port_mdb_del, |
3881 | .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join, | 3938 | .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join, |
3882 | .crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave, | 3939 | .crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave, |
3940 | .port_hwtstamp_set = mv88e6xxx_port_hwtstamp_set, | ||
3941 | .port_hwtstamp_get = mv88e6xxx_port_hwtstamp_get, | ||
3942 | .port_txtstamp = mv88e6xxx_port_txtstamp, | ||
3943 | .port_rxtstamp = mv88e6xxx_port_rxtstamp, | ||
3944 | .get_ts_info = mv88e6xxx_get_ts_info, | ||
3883 | }; | 3945 | }; |
3884 | 3946 | ||
3885 | static struct dsa_switch_driver mv88e6xxx_switch_drv = { | 3947 | static struct dsa_switch_driver mv88e6xxx_switch_drv = { |
@@ -4022,6 +4084,11 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) | |||
4022 | struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); | 4084 | struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); |
4023 | struct mv88e6xxx_chip *chip = ds->priv; | 4085 | struct mv88e6xxx_chip *chip = ds->priv; |
4024 | 4086 | ||
4087 | if (chip->info->ptp_support) { | ||
4088 | mv88e6xxx_hwtstamp_free(chip); | ||
4089 | mv88e6xxx_ptp_free(chip); | ||
4090 | } | ||
4091 | |||
4025 | mv88e6xxx_phy_destroy(chip); | 4092 | mv88e6xxx_phy_destroy(chip); |
4026 | mv88e6xxx_unregister_switch(chip); | 4093 | mv88e6xxx_unregister_switch(chip); |
4027 | mv88e6xxx_mdios_unregister(chip); | 4094 | mv88e6xxx_mdios_unregister(chip); |
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 3dba6e90adcf..97d7915f32c7 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h | |||
@@ -16,6 +16,8 @@ | |||
16 | #include <linux/irq.h> | 16 | #include <linux/irq.h> |
17 | #include <linux/gpio/consumer.h> | 17 | #include <linux/gpio/consumer.h> |
18 | #include <linux/phy.h> | 18 | #include <linux/phy.h> |
19 | #include <linux/ptp_clock_kernel.h> | ||
20 | #include <linux/timecounter.h> | ||
19 | #include <net/dsa.h> | 21 | #include <net/dsa.h> |
20 | 22 | ||
21 | #ifndef UINT64_MAX | 23 | #ifndef UINT64_MAX |
@@ -39,6 +41,8 @@ | |||
39 | #define MV88E6XXX_MAX_PVT_SWITCHES 32 | 41 | #define MV88E6XXX_MAX_PVT_SWITCHES 32 |
40 | #define MV88E6XXX_MAX_PVT_PORTS 16 | 42 | #define MV88E6XXX_MAX_PVT_PORTS 16 |
41 | 43 | ||
44 | #define MV88E6XXX_MAX_GPIO 16 | ||
45 | |||
42 | enum mv88e6xxx_egress_mode { | 46 | enum mv88e6xxx_egress_mode { |
43 | MV88E6XXX_EGRESS_MODE_UNMODIFIED, | 47 | MV88E6XXX_EGRESS_MODE_UNMODIFIED, |
44 | MV88E6XXX_EGRESS_MODE_UNTAGGED, | 48 | MV88E6XXX_EGRESS_MODE_UNTAGGED, |
@@ -105,6 +109,7 @@ struct mv88e6xxx_info { | |||
105 | const char *name; | 109 | const char *name; |
106 | unsigned int num_databases; | 110 | unsigned int num_databases; |
107 | unsigned int num_ports; | 111 | unsigned int num_ports; |
112 | unsigned int num_gpio; | ||
108 | unsigned int max_vid; | 113 | unsigned int max_vid; |
109 | unsigned int port_base_addr; | 114 | unsigned int port_base_addr; |
110 | unsigned int global1_addr; | 115 | unsigned int global1_addr; |
@@ -126,6 +131,9 @@ struct mv88e6xxx_info { | |||
126 | */ | 131 | */ |
127 | u8 atu_move_port_mask; | 132 | u8 atu_move_port_mask; |
128 | const struct mv88e6xxx_ops *ops; | 133 | const struct mv88e6xxx_ops *ops; |
134 | |||
135 | /* Supports PTP */ | ||
136 | bool ptp_support; | ||
129 | }; | 137 | }; |
130 | 138 | ||
131 | struct mv88e6xxx_atu_entry { | 139 | struct mv88e6xxx_atu_entry { |
@@ -146,6 +154,8 @@ struct mv88e6xxx_vtu_entry { | |||
146 | 154 | ||
147 | struct mv88e6xxx_bus_ops; | 155 | struct mv88e6xxx_bus_ops; |
148 | struct mv88e6xxx_irq_ops; | 156 | struct mv88e6xxx_irq_ops; |
157 | struct mv88e6xxx_gpio_ops; | ||
158 | struct mv88e6xxx_avb_ops; | ||
149 | 159 | ||
150 | struct mv88e6xxx_irq { | 160 | struct mv88e6xxx_irq { |
151 | u16 masked; | 161 | u16 masked; |
@@ -154,6 +164,32 @@ struct mv88e6xxx_irq { | |||
154 | unsigned int nirqs; | 164 | unsigned int nirqs; |
155 | }; | 165 | }; |
156 | 166 | ||
167 | /* state flags for mv88e6xxx_port_hwtstamp::state */ | ||
168 | enum { | ||
169 | MV88E6XXX_HWTSTAMP_ENABLED, | ||
170 | MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, | ||
171 | }; | ||
172 | |||
173 | struct mv88e6xxx_port_hwtstamp { | ||
174 | /* Port index */ | ||
175 | int port_id; | ||
176 | |||
177 | /* Timestamping state */ | ||
178 | unsigned long state; | ||
179 | |||
180 | /* Resources for receive timestamping */ | ||
181 | struct sk_buff_head rx_queue; | ||
182 | struct sk_buff_head rx_queue2; | ||
183 | |||
184 | /* Resources for transmit timestamping */ | ||
185 | unsigned long tx_tstamp_start; | ||
186 | struct sk_buff *tx_skb; | ||
187 | u16 tx_seq_id; | ||
188 | |||
189 | /* Current timestamp configuration */ | ||
190 | struct hwtstamp_config tstamp_config; | ||
191 | }; | ||
192 | |||
157 | struct mv88e6xxx_chip { | 193 | struct mv88e6xxx_chip { |
158 | const struct mv88e6xxx_info *info; | 194 | const struct mv88e6xxx_info *info; |
159 | 195 | ||
@@ -209,6 +245,26 @@ struct mv88e6xxx_chip { | |||
209 | int watchdog_irq; | 245 | int watchdog_irq; |
210 | int atu_prob_irq; | 246 | int atu_prob_irq; |
211 | int vtu_prob_irq; | 247 | int vtu_prob_irq; |
248 | |||
249 | /* GPIO resources */ | ||
250 | u8 gpio_data[2]; | ||
251 | |||
252 | /* This cyclecounter abstracts the switch PTP time. | ||
253 | * reg_lock must be held for any operation that read()s. | ||
254 | */ | ||
255 | struct cyclecounter tstamp_cc; | ||
256 | struct timecounter tstamp_tc; | ||
257 | struct delayed_work overflow_work; | ||
258 | |||
259 | struct ptp_clock *ptp_clock; | ||
260 | struct ptp_clock_info ptp_clock_info; | ||
261 | struct delayed_work tai_event_work; | ||
262 | struct ptp_pin_desc pin_config[MV88E6XXX_MAX_GPIO]; | ||
263 | u16 trig_config; | ||
264 | u16 evcap_config; | ||
265 | |||
266 | /* Per-port timestamping resources. */ | ||
267 | struct mv88e6xxx_port_hwtstamp port_hwtstamp[DSA_MAX_PORTS]; | ||
212 | }; | 268 | }; |
213 | 269 | ||
214 | struct mv88e6xxx_bus_ops { | 270 | struct mv88e6xxx_bus_ops { |
@@ -344,6 +400,12 @@ struct mv88e6xxx_ops { | |||
344 | struct mv88e6xxx_vtu_entry *entry); | 400 | struct mv88e6xxx_vtu_entry *entry); |
345 | int (*vtu_loadpurge)(struct mv88e6xxx_chip *chip, | 401 | int (*vtu_loadpurge)(struct mv88e6xxx_chip *chip, |
346 | struct mv88e6xxx_vtu_entry *entry); | 402 | struct mv88e6xxx_vtu_entry *entry); |
403 | |||
404 | /* GPIO operations */ | ||
405 | const struct mv88e6xxx_gpio_ops *gpio_ops; | ||
406 | |||
407 | /* Interface to the AVB/PTP registers */ | ||
408 | const struct mv88e6xxx_avb_ops *avb_ops; | ||
347 | }; | 409 | }; |
348 | 410 | ||
349 | struct mv88e6xxx_irq_ops { | 411 | struct mv88e6xxx_irq_ops { |
@@ -355,6 +417,42 @@ struct mv88e6xxx_irq_ops { | |||
355 | void (*irq_free)(struct mv88e6xxx_chip *chip); | 417 | void (*irq_free)(struct mv88e6xxx_chip *chip); |
356 | }; | 418 | }; |
357 | 419 | ||
420 | struct mv88e6xxx_gpio_ops { | ||
421 | /* Get/set data on GPIO pin */ | ||
422 | int (*get_data)(struct mv88e6xxx_chip *chip, unsigned int pin); | ||
423 | int (*set_data)(struct mv88e6xxx_chip *chip, unsigned int pin, | ||
424 | int value); | ||
425 | |||
426 | /* get/set GPIO direction */ | ||
427 | int (*get_dir)(struct mv88e6xxx_chip *chip, unsigned int pin); | ||
428 | int (*set_dir)(struct mv88e6xxx_chip *chip, unsigned int pin, | ||
429 | bool input); | ||
430 | |||
431 | /* get/set GPIO pin control */ | ||
432 | int (*get_pctl)(struct mv88e6xxx_chip *chip, unsigned int pin, | ||
433 | int *func); | ||
434 | int (*set_pctl)(struct mv88e6xxx_chip *chip, unsigned int pin, | ||
435 | int func); | ||
436 | }; | ||
437 | |||
438 | struct mv88e6xxx_avb_ops { | ||
439 | /* Access port-scoped Precision Time Protocol registers */ | ||
440 | int (*port_ptp_read)(struct mv88e6xxx_chip *chip, int port, int addr, | ||
441 | u16 *data, int len); | ||
442 | int (*port_ptp_write)(struct mv88e6xxx_chip *chip, int port, int addr, | ||
443 | u16 data); | ||
444 | |||
445 | /* Access global Precision Time Protocol registers */ | ||
446 | int (*ptp_read)(struct mv88e6xxx_chip *chip, int addr, u16 *data, | ||
447 | int len); | ||
448 | int (*ptp_write)(struct mv88e6xxx_chip *chip, int addr, u16 data); | ||
449 | |||
450 | /* Access global Time Application Interface registers */ | ||
451 | int (*tai_read)(struct mv88e6xxx_chip *chip, int addr, u16 *data, | ||
452 | int len); | ||
453 | int (*tai_write)(struct mv88e6xxx_chip *chip, int addr, u16 data); | ||
454 | }; | ||
455 | |||
358 | #define STATS_TYPE_PORT BIT(0) | 456 | #define STATS_TYPE_PORT BIT(0) |
359 | #define STATS_TYPE_BANK0 BIT(1) | 457 | #define STATS_TYPE_BANK0 BIT(1) |
360 | #define STATS_TYPE_BANK1 BIT(2) | 458 | #define STATS_TYPE_BANK1 BIT(2) |
@@ -386,6 +484,11 @@ static inline u16 mv88e6xxx_port_mask(struct mv88e6xxx_chip *chip) | |||
386 | return GENMASK(mv88e6xxx_num_ports(chip) - 1, 0); | 484 | return GENMASK(mv88e6xxx_num_ports(chip) - 1, 0); |
387 | } | 485 | } |
388 | 486 | ||
487 | static inline unsigned int mv88e6xxx_num_gpio(struct mv88e6xxx_chip *chip) | ||
488 | { | ||
489 | return chip->info->num_gpio; | ||
490 | } | ||
491 | |||
389 | int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val); | 492 | int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val); |
390 | int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); | 493 | int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); |
391 | int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, | 494 | int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, |
diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index af0727877825..5f370f1fc7c4 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c | |||
@@ -20,22 +20,22 @@ | |||
20 | #include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */ | 20 | #include "global1.h" /* for MV88E6XXX_G1_STS_IRQ_DEVICE */ |
21 | #include "global2.h" | 21 | #include "global2.h" |
22 | 22 | ||
23 | static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) | 23 | int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) |
24 | { | 24 | { |
25 | return mv88e6xxx_read(chip, chip->info->global2_addr, reg, val); | 25 | return mv88e6xxx_read(chip, chip->info->global2_addr, reg, val); |
26 | } | 26 | } |
27 | 27 | ||
28 | static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) | 28 | int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) |
29 | { | 29 | { |
30 | return mv88e6xxx_write(chip, chip->info->global2_addr, reg, val); | 30 | return mv88e6xxx_write(chip, chip->info->global2_addr, reg, val); |
31 | } | 31 | } |
32 | 32 | ||
33 | static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update) | 33 | int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update) |
34 | { | 34 | { |
35 | return mv88e6xxx_update(chip, chip->info->global2_addr, reg, update); | 35 | return mv88e6xxx_update(chip, chip->info->global2_addr, reg, update); |
36 | } | 36 | } |
37 | 37 | ||
38 | static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) | 38 | int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) |
39 | { | 39 | { |
40 | return mv88e6xxx_wait(chip, chip->info->global2_addr, reg, mask); | 40 | return mv88e6xxx_wait(chip, chip->info->global2_addr, reg, mask); |
41 | } | 41 | } |
@@ -798,6 +798,7 @@ int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus, | |||
798 | val); | 798 | val); |
799 | } | 799 | } |
800 | 800 | ||
801 | /* Offset 0x1B: Watchdog Control */ | ||
801 | static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq) | 802 | static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq) |
802 | { | 803 | { |
803 | u16 reg; | 804 | u16 reg; |
diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 669f59017b12..25f92b3d7157 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h | |||
@@ -149,7 +149,26 @@ | |||
149 | #define MV88E6390_G2_EEPROM_ADDR_MASK 0xffff | 149 | #define MV88E6390_G2_EEPROM_ADDR_MASK 0xffff |
150 | 150 | ||
151 | /* Offset 0x16: AVB Command Register */ | 151 | /* Offset 0x16: AVB Command Register */ |
152 | #define MV88E6352_G2_AVB_CMD 0x16 | 152 | #define MV88E6352_G2_AVB_CMD 0x16 |
153 | #define MV88E6352_G2_AVB_CMD_BUSY 0x8000 | ||
154 | #define MV88E6352_G2_AVB_CMD_OP_READ 0x4000 | ||
155 | #define MV88E6352_G2_AVB_CMD_OP_READ_INCR 0x6000 | ||
156 | #define MV88E6352_G2_AVB_CMD_OP_WRITE 0x3000 | ||
157 | #define MV88E6390_G2_AVB_CMD_OP_READ 0x0000 | ||
158 | #define MV88E6390_G2_AVB_CMD_OP_READ_INCR 0x4000 | ||
159 | #define MV88E6390_G2_AVB_CMD_OP_WRITE 0x6000 | ||
160 | #define MV88E6352_G2_AVB_CMD_PORT_MASK 0x0f00 | ||
161 | #define MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL 0xe | ||
162 | #define MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL 0xf | ||
163 | #define MV88E6390_G2_AVB_CMD_PORT_MASK 0x1f00 | ||
164 | #define MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL 0x1e | ||
165 | #define MV88E6390_G2_AVB_CMD_PORT_PTPGLOBAL 0x1f | ||
166 | #define MV88E6352_G2_AVB_CMD_BLOCK_PTP 0 | ||
167 | #define MV88E6352_G2_AVB_CMD_BLOCK_AVB 1 | ||
168 | #define MV88E6352_G2_AVB_CMD_BLOCK_QAV 2 | ||
169 | #define MV88E6352_G2_AVB_CMD_BLOCK_QVB 3 | ||
170 | #define MV88E6352_G2_AVB_CMD_BLOCK_MASK 0x00e0 | ||
171 | #define MV88E6352_G2_AVB_CMD_ADDR_MASK 0x001f | ||
153 | 172 | ||
154 | /* Offset 0x17: AVB Data Register */ | 173 | /* Offset 0x17: AVB Data Register */ |
155 | #define MV88E6352_G2_AVB_DATA 0x17 | 174 | #define MV88E6352_G2_AVB_DATA 0x17 |
@@ -223,6 +242,35 @@ | |||
223 | #define MV88E6352_G2_NOEGR_POLICY 0x2000 | 242 | #define MV88E6352_G2_NOEGR_POLICY 0x2000 |
224 | #define MV88E6390_G2_LAG_ID_4 0x2000 | 243 | #define MV88E6390_G2_LAG_ID_4 0x2000 |
225 | 244 | ||
245 | /* Scratch/Misc registers accessed through MV88E6XXX_G2_SCRATCH_MISC */ | ||
246 | /* Offset 0x02: Misc Configuration */ | ||
247 | #define MV88E6352_G2_SCRATCH_MISC_CFG 0x02 | ||
248 | #define MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI 0x80 | ||
249 | /* Offset 0x60-0x61: GPIO Configuration */ | ||
250 | #define MV88E6352_G2_SCRATCH_GPIO_CFG0 0x60 | ||
251 | #define MV88E6352_G2_SCRATCH_GPIO_CFG1 0x61 | ||
252 | /* Offset 0x62-0x63: GPIO Direction */ | ||
253 | #define MV88E6352_G2_SCRATCH_GPIO_DIR0 0x62 | ||
254 | #define MV88E6352_G2_SCRATCH_GPIO_DIR1 0x63 | ||
255 | #define MV88E6352_G2_SCRATCH_GPIO_DIR_OUT 0 | ||
256 | #define MV88E6352_G2_SCRATCH_GPIO_DIR_IN 1 | ||
257 | /* Offset 0x64-0x65: GPIO Data */ | ||
258 | #define MV88E6352_G2_SCRATCH_GPIO_DATA0 0x64 | ||
259 | #define MV88E6352_G2_SCRATCH_GPIO_DATA1 0x65 | ||
260 | /* Offset 0x68-0x6F: GPIO Pin Control */ | ||
261 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL0 0x68 | ||
262 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL1 0x69 | ||
263 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL2 0x6A | ||
264 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL3 0x6B | ||
265 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL4 0x6C | ||
266 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL5 0x6D | ||
267 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL6 0x6E | ||
268 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL7 0x6F | ||
269 | |||
270 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO 0 | ||
271 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL_TRIG 1 | ||
272 | #define MV88E6352_G2_SCRATCH_GPIO_PCTL_EVREQ 2 | ||
273 | |||
226 | #ifdef CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 | 274 | #ifdef CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 |
227 | 275 | ||
228 | static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip) | 276 | static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip) |
@@ -230,6 +278,11 @@ static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip) | |||
230 | return 0; | 278 | return 0; |
231 | } | 279 | } |
232 | 280 | ||
281 | int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val); | ||
282 | int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val); | ||
283 | int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update); | ||
284 | int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); | ||
285 | |||
233 | int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port); | 286 | int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port); |
234 | int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port); | 287 | int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip, int port); |
235 | 288 | ||
@@ -267,6 +320,11 @@ int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip); | |||
267 | extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops; | 320 | extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops; |
268 | extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops; | 321 | extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops; |
269 | 322 | ||
323 | extern const struct mv88e6xxx_avb_ops mv88e6352_avb_ops; | ||
324 | extern const struct mv88e6xxx_avb_ops mv88e6390_avb_ops; | ||
325 | |||
326 | extern const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops; | ||
327 | |||
270 | #else /* !CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ | 328 | #else /* !CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ |
271 | 329 | ||
272 | static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip) | 330 | static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip) |
@@ -279,6 +337,26 @@ static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip) | |||
279 | return 0; | 337 | return 0; |
280 | } | 338 | } |
281 | 339 | ||
340 | static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) | ||
341 | { | ||
342 | return -EOPNOTSUPP; | ||
343 | } | ||
344 | |||
345 | static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) | ||
346 | { | ||
347 | return -EOPNOTSUPP; | ||
348 | } | ||
349 | |||
350 | static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update) | ||
351 | { | ||
352 | return -EOPNOTSUPP; | ||
353 | } | ||
354 | |||
355 | static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) | ||
356 | { | ||
357 | return -EOPNOTSUPP; | ||
358 | } | ||
359 | |||
282 | static inline int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, | 360 | static inline int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip, |
283 | int port) | 361 | int port) |
284 | { | 362 | { |
@@ -382,6 +460,11 @@ static inline int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip) | |||
382 | static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {}; | 460 | static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {}; |
383 | static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {}; | 461 | static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {}; |
384 | 462 | ||
463 | static const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = {}; | ||
464 | static const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = {}; | ||
465 | |||
466 | static const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops = {}; | ||
467 | |||
385 | #endif /* CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ | 468 | #endif /* CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */ |
386 | 469 | ||
387 | #endif /* _MV88E6XXX_GLOBAL2_H */ | 470 | #endif /* _MV88E6XXX_GLOBAL2_H */ |
diff --git a/drivers/net/dsa/mv88e6xxx/global2_avb.c b/drivers/net/dsa/mv88e6xxx/global2_avb.c new file mode 100644 index 000000000000..2e398ccb88ca --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/global2_avb.c | |||
@@ -0,0 +1,193 @@ | |||
1 | /* | ||
2 | * Marvell 88E6xxx Switch Global 2 Registers support | ||
3 | * | ||
4 | * Copyright (c) 2008 Marvell Semiconductor | ||
5 | * | ||
6 | * Copyright (c) 2016-2017 Savoir-faire Linux Inc. | ||
7 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> | ||
8 | * | ||
9 | * Copyright (c) 2017 National Instruments | ||
10 | * Brandon Streiff <brandon.streiff@ni.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | */ | ||
17 | |||
18 | #include "global2.h" | ||
19 | |||
20 | /* Offset 0x16: AVB Command Register | ||
21 | * Offset 0x17: AVB Data Register | ||
22 | * | ||
23 | * There are two different versions of this register interface: | ||
24 | * "6352": 3-bit "op" field, 4-bit "port" field. | ||
25 | * "6390": 2-bit "op" field, 5-bit "port" field. | ||
26 | * | ||
27 | * The "op" codes are different between the two, as well as the special | ||
28 | * port fields for global PTP and TAI configuration. | ||
29 | */ | ||
30 | |||
31 | /* mv88e6xxx_g2_avb_read -- Read one or multiple 16-bit words. | ||
32 | * The hardware supports snapshotting up to four contiguous registers. | ||
33 | */ | ||
34 | static int mv88e6xxx_g2_avb_read(struct mv88e6xxx_chip *chip, u16 readop, | ||
35 | u16 *data, int len) | ||
36 | { | ||
37 | int err; | ||
38 | int i; | ||
39 | |||
40 | /* Hardware can only snapshot four words. */ | ||
41 | if (len > 4) | ||
42 | return -E2BIG; | ||
43 | |||
44 | err = mv88e6xxx_g2_update(chip, MV88E6352_G2_AVB_CMD, readop); | ||
45 | if (err) | ||
46 | return err; | ||
47 | |||
48 | for (i = 0; i < len; ++i) { | ||
49 | err = mv88e6xxx_g2_read(chip, MV88E6352_G2_AVB_DATA, | ||
50 | &data[i]); | ||
51 | if (err) | ||
52 | return err; | ||
53 | } | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | /* mv88e6xxx_g2_avb_write -- Write one 16-bit word. */ | ||
59 | static int mv88e6xxx_g2_avb_write(struct mv88e6xxx_chip *chip, u16 writeop, | ||
60 | u16 data) | ||
61 | { | ||
62 | int err; | ||
63 | |||
64 | err = mv88e6xxx_g2_write(chip, MV88E6352_G2_AVB_DATA, data); | ||
65 | if (err) | ||
66 | return err; | ||
67 | |||
68 | return mv88e6xxx_g2_update(chip, MV88E6352_G2_AVB_CMD, writeop); | ||
69 | } | ||
70 | |||
71 | static int mv88e6352_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip, | ||
72 | int port, int addr, u16 *data, | ||
73 | int len) | ||
74 | { | ||
75 | u16 readop = (len == 1 ? MV88E6352_G2_AVB_CMD_OP_READ : | ||
76 | MV88E6352_G2_AVB_CMD_OP_READ_INCR) | | ||
77 | (port << 8) | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | | ||
78 | addr; | ||
79 | |||
80 | return mv88e6xxx_g2_avb_read(chip, readop, data, len); | ||
81 | } | ||
82 | |||
83 | static int mv88e6352_g2_avb_port_ptp_write(struct mv88e6xxx_chip *chip, | ||
84 | int port, int addr, u16 data) | ||
85 | { | ||
86 | u16 writeop = MV88E6352_G2_AVB_CMD_OP_WRITE | (port << 8) | | ||
87 | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | addr; | ||
88 | |||
89 | return mv88e6xxx_g2_avb_write(chip, writeop, data); | ||
90 | } | ||
91 | |||
92 | static int mv88e6352_g2_avb_ptp_read(struct mv88e6xxx_chip *chip, int addr, | ||
93 | u16 *data, int len) | ||
94 | { | ||
95 | return mv88e6352_g2_avb_port_ptp_read(chip, | ||
96 | MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL, | ||
97 | addr, data, len); | ||
98 | } | ||
99 | |||
100 | static int mv88e6352_g2_avb_ptp_write(struct mv88e6xxx_chip *chip, int addr, | ||
101 | u16 data) | ||
102 | { | ||
103 | return mv88e6352_g2_avb_port_ptp_write(chip, | ||
104 | MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL, | ||
105 | addr, data); | ||
106 | } | ||
107 | |||
108 | static int mv88e6352_g2_avb_tai_read(struct mv88e6xxx_chip *chip, int addr, | ||
109 | u16 *data, int len) | ||
110 | { | ||
111 | return mv88e6352_g2_avb_port_ptp_read(chip, | ||
112 | MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL, | ||
113 | addr, data, len); | ||
114 | } | ||
115 | |||
116 | static int mv88e6352_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr, | ||
117 | u16 data) | ||
118 | { | ||
119 | return mv88e6352_g2_avb_port_ptp_write(chip, | ||
120 | MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL, | ||
121 | addr, data); | ||
122 | } | ||
123 | |||
124 | const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = { | ||
125 | .port_ptp_read = mv88e6352_g2_avb_port_ptp_read, | ||
126 | .port_ptp_write = mv88e6352_g2_avb_port_ptp_write, | ||
127 | .ptp_read = mv88e6352_g2_avb_ptp_read, | ||
128 | .ptp_write = mv88e6352_g2_avb_ptp_write, | ||
129 | .tai_read = mv88e6352_g2_avb_tai_read, | ||
130 | .tai_write = mv88e6352_g2_avb_tai_write, | ||
131 | }; | ||
132 | |||
133 | static int mv88e6390_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip, | ||
134 | int port, int addr, u16 *data, | ||
135 | int len) | ||
136 | { | ||
137 | u16 readop = (len == 1 ? MV88E6390_G2_AVB_CMD_OP_READ : | ||
138 | MV88E6390_G2_AVB_CMD_OP_READ_INCR) | | ||
139 | (port << 8) | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | | ||
140 | addr; | ||
141 | |||
142 | return mv88e6xxx_g2_avb_read(chip, readop, data, len); | ||
143 | } | ||
144 | |||
145 | static int mv88e6390_g2_avb_port_ptp_write(struct mv88e6xxx_chip *chip, | ||
146 | int port, int addr, u16 data) | ||
147 | { | ||
148 | u16 writeop = MV88E6390_G2_AVB_CMD_OP_WRITE | (port << 8) | | ||
149 | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | addr; | ||
150 | |||
151 | return mv88e6xxx_g2_avb_write(chip, writeop, data); | ||
152 | } | ||
153 | |||
154 | static int mv88e6390_g2_avb_ptp_read(struct mv88e6xxx_chip *chip, int addr, | ||
155 | u16 *data, int len) | ||
156 | { | ||
157 | return mv88e6390_g2_avb_port_ptp_read(chip, | ||
158 | MV88E6390_G2_AVB_CMD_PORT_PTPGLOBAL, | ||
159 | addr, data, len); | ||
160 | } | ||
161 | |||
162 | static int mv88e6390_g2_avb_ptp_write(struct mv88e6xxx_chip *chip, int addr, | ||
163 | u16 data) | ||
164 | { | ||
165 | return mv88e6390_g2_avb_port_ptp_write(chip, | ||
166 | MV88E6390_G2_AVB_CMD_PORT_PTPGLOBAL, | ||
167 | addr, data); | ||
168 | } | ||
169 | |||
170 | static int mv88e6390_g2_avb_tai_read(struct mv88e6xxx_chip *chip, int addr, | ||
171 | u16 *data, int len) | ||
172 | { | ||
173 | return mv88e6390_g2_avb_port_ptp_read(chip, | ||
174 | MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL, | ||
175 | addr, data, len); | ||
176 | } | ||
177 | |||
178 | static int mv88e6390_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr, | ||
179 | u16 data) | ||
180 | { | ||
181 | return mv88e6390_g2_avb_port_ptp_write(chip, | ||
182 | MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL, | ||
183 | addr, data); | ||
184 | } | ||
185 | |||
186 | const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = { | ||
187 | .port_ptp_read = mv88e6390_g2_avb_port_ptp_read, | ||
188 | .port_ptp_write = mv88e6390_g2_avb_port_ptp_write, | ||
189 | .ptp_read = mv88e6390_g2_avb_ptp_read, | ||
190 | .ptp_write = mv88e6390_g2_avb_ptp_write, | ||
191 | .tai_read = mv88e6390_g2_avb_tai_read, | ||
192 | .tai_write = mv88e6390_g2_avb_tai_write, | ||
193 | }; | ||
diff --git a/drivers/net/dsa/mv88e6xxx/global2_scratch.c b/drivers/net/dsa/mv88e6xxx/global2_scratch.c new file mode 100644 index 000000000000..0ff12bff9f0e --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c | |||
@@ -0,0 +1,240 @@ | |||
1 | /* | ||
2 | * Marvell 88E6xxx Switch Global 2 Scratch & Misc Registers support | ||
3 | * | ||
4 | * Copyright (c) 2008 Marvell Semiconductor | ||
5 | * | ||
6 | * Copyright (c) 2017 National Instruments | ||
7 | * Brandon Streiff <brandon.streiff@ni.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | */ | ||
14 | |||
15 | #include "chip.h" | ||
16 | #include "global2.h" | ||
17 | |||
18 | /* Offset 0x1A: Scratch and Misc. Register */ | ||
19 | static int mv88e6xxx_g2_scratch_read(struct mv88e6xxx_chip *chip, int reg, | ||
20 | u8 *data) | ||
21 | { | ||
22 | u16 value; | ||
23 | int err; | ||
24 | |||
25 | err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, | ||
26 | reg << 8); | ||
27 | if (err) | ||
28 | return err; | ||
29 | |||
30 | err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, &value); | ||
31 | if (err) | ||
32 | return err; | ||
33 | |||
34 | *data = (value & MV88E6XXX_G2_SCRATCH_MISC_DATA_MASK); | ||
35 | |||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static int mv88e6xxx_g2_scratch_write(struct mv88e6xxx_chip *chip, int reg, | ||
40 | u8 data) | ||
41 | { | ||
42 | u16 value = (reg << 8) | data; | ||
43 | |||
44 | return mv88e6xxx_g2_update(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, value); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * mv88e6xxx_g2_scratch_gpio_get_bit - get a bit | ||
49 | * @chip: chip private data | ||
50 | * @nr: bit index | ||
51 | * @set: is bit set? | ||
52 | */ | ||
53 | static int mv88e6xxx_g2_scratch_get_bit(struct mv88e6xxx_chip *chip, | ||
54 | int base_reg, unsigned int offset, | ||
55 | int *set) | ||
56 | { | ||
57 | int reg = base_reg + (offset / 8); | ||
58 | u8 mask = (1 << (offset & 0x7)); | ||
59 | u8 val; | ||
60 | int err; | ||
61 | |||
62 | err = mv88e6xxx_g2_scratch_read(chip, reg, &val); | ||
63 | if (err) | ||
64 | return err; | ||
65 | |||
66 | *set = !!(mask & val); | ||
67 | |||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * mv88e6xxx_g2_scratch_gpio_set_bit - set (or clear) a bit | ||
73 | * @chip: chip private data | ||
74 | * @nr: bit index | ||
75 | * @set: set if true, clear if false | ||
76 | * | ||
77 | * Helper function for dealing with the direction and data registers. | ||
78 | */ | ||
79 | static int mv88e6xxx_g2_scratch_set_bit(struct mv88e6xxx_chip *chip, | ||
80 | int base_reg, unsigned int offset, | ||
81 | int set) | ||
82 | { | ||
83 | int reg = base_reg + (offset / 8); | ||
84 | u8 mask = (1 << (offset & 0x7)); | ||
85 | u8 val; | ||
86 | int err; | ||
87 | |||
88 | err = mv88e6xxx_g2_scratch_read(chip, reg, &val); | ||
89 | if (err) | ||
90 | return err; | ||
91 | |||
92 | if (set) | ||
93 | val |= mask; | ||
94 | else | ||
95 | val &= ~mask; | ||
96 | |||
97 | return mv88e6xxx_g2_scratch_write(chip, reg, val); | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * mv88e6352_g2_scratch_gpio_get_data - get data on gpio pin | ||
102 | * @chip: chip private data | ||
103 | * @pin: gpio index | ||
104 | * | ||
105 | * Return: 0 for low, 1 for high, negative error | ||
106 | */ | ||
107 | static int mv88e6352_g2_scratch_gpio_get_data(struct mv88e6xxx_chip *chip, | ||
108 | unsigned int pin) | ||
109 | { | ||
110 | int val = 0; | ||
111 | int err; | ||
112 | |||
113 | err = mv88e6xxx_g2_scratch_get_bit(chip, | ||
114 | MV88E6352_G2_SCRATCH_GPIO_DATA0, | ||
115 | pin, &val); | ||
116 | if (err) | ||
117 | return err; | ||
118 | |||
119 | return val; | ||
120 | } | ||
121 | |||
122 | /** | ||
123 | * mv88e6352_g2_scratch_gpio_set_data - set data on gpio pin | ||
124 | * @chip: chip private data | ||
125 | * @pin: gpio index | ||
126 | * @value: value to set | ||
127 | */ | ||
128 | static int mv88e6352_g2_scratch_gpio_set_data(struct mv88e6xxx_chip *chip, | ||
129 | unsigned int pin, int value) | ||
130 | { | ||
131 | u8 mask = (1 << (pin & 0x7)); | ||
132 | int offset = (pin / 8); | ||
133 | int reg; | ||
134 | |||
135 | reg = MV88E6352_G2_SCRATCH_GPIO_DATA0 + offset; | ||
136 | |||
137 | if (value) | ||
138 | chip->gpio_data[offset] |= mask; | ||
139 | else | ||
140 | chip->gpio_data[offset] &= ~mask; | ||
141 | |||
142 | return mv88e6xxx_g2_scratch_write(chip, reg, chip->gpio_data[offset]); | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * mv88e6352_g2_scratch_gpio_get_dir - get direction of gpio pin | ||
147 | * @chip: chip private data | ||
148 | * @pin: gpio index | ||
149 | * | ||
150 | * Return: 0 for output, 1 for input (same as GPIOF_DIR_XXX). | ||
151 | */ | ||
152 | static int mv88e6352_g2_scratch_gpio_get_dir(struct mv88e6xxx_chip *chip, | ||
153 | unsigned int pin) | ||
154 | { | ||
155 | int val = 0; | ||
156 | int err; | ||
157 | |||
158 | err = mv88e6xxx_g2_scratch_get_bit(chip, | ||
159 | MV88E6352_G2_SCRATCH_GPIO_DIR0, | ||
160 | pin, &val); | ||
161 | if (err) | ||
162 | return err; | ||
163 | |||
164 | return val; | ||
165 | } | ||
166 | |||
167 | /** | ||
168 | * mv88e6352_g2_scratch_gpio_set_dir - set direction of gpio pin | ||
169 | * @chip: chip private data | ||
170 | * @pin: gpio index | ||
171 | */ | ||
172 | static int mv88e6352_g2_scratch_gpio_set_dir(struct mv88e6xxx_chip *chip, | ||
173 | unsigned int pin, bool input) | ||
174 | { | ||
175 | int value = (input ? MV88E6352_G2_SCRATCH_GPIO_DIR_IN : | ||
176 | MV88E6352_G2_SCRATCH_GPIO_DIR_OUT); | ||
177 | |||
178 | return mv88e6xxx_g2_scratch_set_bit(chip, | ||
179 | MV88E6352_G2_SCRATCH_GPIO_DIR0, | ||
180 | pin, value); | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * mv88e6352_g2_scratch_gpio_get_pctl - get pin control setting | ||
185 | * @chip: chip private data | ||
186 | * @pin: gpio index | ||
187 | * @func: function number | ||
188 | * | ||
189 | * Note that the function numbers themselves may vary by chipset. | ||
190 | */ | ||
191 | static int mv88e6352_g2_scratch_gpio_get_pctl(struct mv88e6xxx_chip *chip, | ||
192 | unsigned int pin, int *func) | ||
193 | { | ||
194 | int reg = MV88E6352_G2_SCRATCH_GPIO_PCTL0 + (pin / 2); | ||
195 | int offset = (pin & 0x1) ? 4 : 0; | ||
196 | u8 mask = (0x7 << offset); | ||
197 | int err; | ||
198 | u8 val; | ||
199 | |||
200 | err = mv88e6xxx_g2_scratch_read(chip, reg, &val); | ||
201 | if (err) | ||
202 | return err; | ||
203 | |||
204 | *func = (val & mask) >> offset; | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * mv88e6352_g2_scratch_gpio_set_pctl - set pin control setting | ||
211 | * @chip: chip private data | ||
212 | * @pin: gpio index | ||
213 | * @func: function number | ||
214 | */ | ||
215 | static int mv88e6352_g2_scratch_gpio_set_pctl(struct mv88e6xxx_chip *chip, | ||
216 | unsigned int pin, int func) | ||
217 | { | ||
218 | int reg = MV88E6352_G2_SCRATCH_GPIO_PCTL0 + (pin / 2); | ||
219 | int offset = (pin & 0x1) ? 4 : 0; | ||
220 | u8 mask = (0x7 << offset); | ||
221 | int err; | ||
222 | u8 val; | ||
223 | |||
224 | err = mv88e6xxx_g2_scratch_read(chip, reg, &val); | ||
225 | if (err) | ||
226 | return err; | ||
227 | |||
228 | val = (val & ~mask) | ((func & mask) << offset); | ||
229 | |||
230 | return mv88e6xxx_g2_scratch_write(chip, reg, val); | ||
231 | } | ||
232 | |||
233 | const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops = { | ||
234 | .get_data = mv88e6352_g2_scratch_gpio_get_data, | ||
235 | .set_data = mv88e6352_g2_scratch_gpio_set_data, | ||
236 | .get_dir = mv88e6352_g2_scratch_gpio_get_dir, | ||
237 | .set_dir = mv88e6352_g2_scratch_gpio_set_dir, | ||
238 | .get_pctl = mv88e6352_g2_scratch_gpio_get_pctl, | ||
239 | .set_pctl = mv88e6352_g2_scratch_gpio_set_pctl, | ||
240 | }; | ||
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c new file mode 100644 index 000000000000..b251d534b70d --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c | |||
@@ -0,0 +1,584 @@ | |||
1 | /* | ||
2 | * Marvell 88E6xxx Switch hardware timestamping support | ||
3 | * | ||
4 | * Copyright (c) 2008 Marvell Semiconductor | ||
5 | * | ||
6 | * Copyright (c) 2017 National Instruments | ||
7 | * Erik Hons <erik.hons@ni.com> | ||
8 | * Brandon Streiff <brandon.streiff@ni.com> | ||
9 | * Dane Wagner <dane.wagner@ni.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | */ | ||
16 | |||
17 | #include "chip.h" | ||
18 | #include "global2.h" | ||
19 | #include "hwtstamp.h" | ||
20 | #include "ptp.h" | ||
21 | #include <linux/ptp_classify.h> | ||
22 | |||
23 | #define SKB_PTP_TYPE(__skb) (*(unsigned int *)((__skb)->cb)) | ||
24 | |||
25 | static int mv88e6xxx_port_ptp_read(struct mv88e6xxx_chip *chip, int port, | ||
26 | int addr, u16 *data, int len) | ||
27 | { | ||
28 | if (!chip->info->ops->avb_ops->port_ptp_read) | ||
29 | return -EOPNOTSUPP; | ||
30 | |||
31 | return chip->info->ops->avb_ops->port_ptp_read(chip, port, addr, | ||
32 | data, len); | ||
33 | } | ||
34 | |||
35 | static int mv88e6xxx_port_ptp_write(struct mv88e6xxx_chip *chip, int port, | ||
36 | int addr, u16 data) | ||
37 | { | ||
38 | if (!chip->info->ops->avb_ops->port_ptp_write) | ||
39 | return -EOPNOTSUPP; | ||
40 | |||
41 | return chip->info->ops->avb_ops->port_ptp_write(chip, port, addr, | ||
42 | data); | ||
43 | } | ||
44 | |||
45 | static int mv88e6xxx_ptp_write(struct mv88e6xxx_chip *chip, int addr, | ||
46 | u16 data) | ||
47 | { | ||
48 | if (!chip->info->ops->avb_ops->ptp_write) | ||
49 | return -EOPNOTSUPP; | ||
50 | |||
51 | return chip->info->ops->avb_ops->ptp_write(chip, addr, data); | ||
52 | } | ||
53 | |||
54 | /* TX_TSTAMP_TIMEOUT: This limits the time spent polling for a TX | ||
55 | * timestamp. When working properly, hardware will produce a timestamp | ||
56 | * within 1ms. Software may enounter delays due to MDIO contention, so | ||
57 | * the timeout is set accordingly. | ||
58 | */ | ||
59 | #define TX_TSTAMP_TIMEOUT msecs_to_jiffies(20) | ||
60 | |||
61 | int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, | ||
62 | struct ethtool_ts_info *info) | ||
63 | { | ||
64 | struct mv88e6xxx_chip *chip = ds->priv; | ||
65 | |||
66 | if (!chip->info->ptp_support) | ||
67 | return -EOPNOTSUPP; | ||
68 | |||
69 | info->so_timestamping = | ||
70 | SOF_TIMESTAMPING_TX_HARDWARE | | ||
71 | SOF_TIMESTAMPING_RX_HARDWARE | | ||
72 | SOF_TIMESTAMPING_RAW_HARDWARE; | ||
73 | info->phc_index = ptp_clock_index(chip->ptp_clock); | ||
74 | info->tx_types = | ||
75 | (1 << HWTSTAMP_TX_OFF) | | ||
76 | (1 << HWTSTAMP_TX_ON); | ||
77 | info->rx_filters = | ||
78 | (1 << HWTSTAMP_FILTER_NONE) | | ||
79 | (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | | ||
80 | (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | | ||
81 | (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | | ||
82 | (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | | ||
83 | (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | | ||
84 | (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) | | ||
85 | (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | | ||
86 | (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | | ||
87 | (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ); | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static int mv88e6xxx_set_hwtstamp_config(struct mv88e6xxx_chip *chip, int port, | ||
93 | struct hwtstamp_config *config) | ||
94 | { | ||
95 | struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; | ||
96 | bool tstamp_enable = false; | ||
97 | u16 port_config0; | ||
98 | int err; | ||
99 | |||
100 | /* Prevent the TX/RX paths from trying to interact with the | ||
101 | * timestamp hardware while we reconfigure it. | ||
102 | */ | ||
103 | clear_bit_unlock(MV88E6XXX_HWTSTAMP_ENABLED, &ps->state); | ||
104 | |||
105 | /* reserved for future extensions */ | ||
106 | if (config->flags) | ||
107 | return -EINVAL; | ||
108 | |||
109 | switch (config->tx_type) { | ||
110 | case HWTSTAMP_TX_OFF: | ||
111 | tstamp_enable = false; | ||
112 | break; | ||
113 | case HWTSTAMP_TX_ON: | ||
114 | tstamp_enable = true; | ||
115 | break; | ||
116 | default: | ||
117 | return -ERANGE; | ||
118 | } | ||
119 | |||
120 | /* The switch supports timestamping both L2 and L4; one cannot be | ||
121 | * disabled independently of the other. | ||
122 | */ | ||
123 | switch (config->rx_filter) { | ||
124 | case HWTSTAMP_FILTER_NONE: | ||
125 | tstamp_enable = false; | ||
126 | break; | ||
127 | case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: | ||
128 | case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: | ||
129 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: | ||
130 | case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: | ||
131 | case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: | ||
132 | case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: | ||
133 | case HWTSTAMP_FILTER_PTP_V2_EVENT: | ||
134 | case HWTSTAMP_FILTER_PTP_V2_SYNC: | ||
135 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: | ||
136 | config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; | ||
137 | break; | ||
138 | case HWTSTAMP_FILTER_ALL: | ||
139 | default: | ||
140 | config->rx_filter = HWTSTAMP_FILTER_NONE; | ||
141 | return -ERANGE; | ||
142 | } | ||
143 | |||
144 | if (tstamp_enable) { | ||
145 | /* Disable transportSpecific value matching, so that packets | ||
146 | * with either 1588 (0) and 802.1AS (1) will be timestamped. | ||
147 | */ | ||
148 | port_config0 = MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH; | ||
149 | } else { | ||
150 | /* Disable PTP. This disables both RX and TX timestamping. */ | ||
151 | port_config0 = MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP; | ||
152 | } | ||
153 | |||
154 | mutex_lock(&chip->reg_lock); | ||
155 | err = mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0, | ||
156 | port_config0); | ||
157 | mutex_unlock(&chip->reg_lock); | ||
158 | |||
159 | if (err < 0) | ||
160 | return err; | ||
161 | |||
162 | /* Once hardware has been configured, enable timestamp checks | ||
163 | * in the RX/TX paths. | ||
164 | */ | ||
165 | if (tstamp_enable) | ||
166 | set_bit(MV88E6XXX_HWTSTAMP_ENABLED, &ps->state); | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | int mv88e6xxx_port_hwtstamp_set(struct dsa_switch *ds, int port, | ||
172 | struct ifreq *ifr) | ||
173 | { | ||
174 | struct mv88e6xxx_chip *chip = ds->priv; | ||
175 | struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; | ||
176 | struct hwtstamp_config config; | ||
177 | int err; | ||
178 | |||
179 | if (!chip->info->ptp_support) | ||
180 | return -EOPNOTSUPP; | ||
181 | |||
182 | if (port < 0 || port >= mv88e6xxx_num_ports(chip)) | ||
183 | return -EINVAL; | ||
184 | |||
185 | if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) | ||
186 | return -EFAULT; | ||
187 | |||
188 | err = mv88e6xxx_set_hwtstamp_config(chip, port, &config); | ||
189 | if (err) | ||
190 | return err; | ||
191 | |||
192 | /* Save the chosen configuration to be returned later. */ | ||
193 | memcpy(&ps->tstamp_config, &config, sizeof(config)); | ||
194 | |||
195 | return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? | ||
196 | -EFAULT : 0; | ||
197 | } | ||
198 | |||
199 | int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds, int port, | ||
200 | struct ifreq *ifr) | ||
201 | { | ||
202 | struct mv88e6xxx_chip *chip = ds->priv; | ||
203 | struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; | ||
204 | struct hwtstamp_config *config = &ps->tstamp_config; | ||
205 | |||
206 | if (!chip->info->ptp_support) | ||
207 | return -EOPNOTSUPP; | ||
208 | |||
209 | if (port < 0 || port >= mv88e6xxx_num_ports(chip)) | ||
210 | return -EINVAL; | ||
211 | |||
212 | return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? | ||
213 | -EFAULT : 0; | ||
214 | } | ||
215 | |||
216 | /* Get the start of the PTP header in this skb */ | ||
217 | static u8 *parse_ptp_header(struct sk_buff *skb, unsigned int type) | ||
218 | { | ||
219 | u8 *data = skb_mac_header(skb); | ||
220 | unsigned int offset = 0; | ||
221 | |||
222 | if (type & PTP_CLASS_VLAN) | ||
223 | offset += VLAN_HLEN; | ||
224 | |||
225 | switch (type & PTP_CLASS_PMASK) { | ||
226 | case PTP_CLASS_IPV4: | ||
227 | offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN; | ||
228 | break; | ||
229 | case PTP_CLASS_IPV6: | ||
230 | offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; | ||
231 | break; | ||
232 | case PTP_CLASS_L2: | ||
233 | offset += ETH_HLEN; | ||
234 | break; | ||
235 | default: | ||
236 | return NULL; | ||
237 | } | ||
238 | |||
239 | /* Ensure that the entire header is present in this packet. */ | ||
240 | if (skb->len + ETH_HLEN < offset + 34) | ||
241 | return NULL; | ||
242 | |||
243 | return data + offset; | ||
244 | } | ||
245 | |||
246 | /* Returns a pointer to the PTP header if the caller should time stamp, | ||
247 | * or NULL if the caller should not. | ||
248 | */ | ||
249 | static u8 *mv88e6xxx_should_tstamp(struct mv88e6xxx_chip *chip, int port, | ||
250 | struct sk_buff *skb, unsigned int type) | ||
251 | { | ||
252 | struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; | ||
253 | u8 *hdr; | ||
254 | |||
255 | if (!chip->info->ptp_support) | ||
256 | return NULL; | ||
257 | |||
258 | if (port < 0 || port >= mv88e6xxx_num_ports(chip)) | ||
259 | return NULL; | ||
260 | |||
261 | hdr = parse_ptp_header(skb, type); | ||
262 | if (!hdr) | ||
263 | return NULL; | ||
264 | |||
265 | if (!test_bit(MV88E6XXX_HWTSTAMP_ENABLED, &ps->state)) | ||
266 | return NULL; | ||
267 | |||
268 | return hdr; | ||
269 | } | ||
270 | |||
271 | static int mv88e6xxx_ts_valid(u16 status) | ||
272 | { | ||
273 | if (!(status & MV88E6XXX_PTP_TS_VALID)) | ||
274 | return 0; | ||
275 | if (status & MV88E6XXX_PTP_TS_STATUS_MASK) | ||
276 | return 0; | ||
277 | return 1; | ||
278 | } | ||
279 | |||
280 | static int seq_match(struct sk_buff *skb, u16 ts_seqid) | ||
281 | { | ||
282 | unsigned int type = SKB_PTP_TYPE(skb); | ||
283 | u8 *hdr = parse_ptp_header(skb, type); | ||
284 | __be16 *seqid; | ||
285 | |||
286 | seqid = (__be16 *)(hdr + OFF_PTP_SEQUENCE_ID); | ||
287 | |||
288 | return ts_seqid == ntohs(*seqid); | ||
289 | } | ||
290 | |||
291 | static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip, | ||
292 | struct mv88e6xxx_port_hwtstamp *ps, | ||
293 | struct sk_buff *skb, u16 reg, | ||
294 | struct sk_buff_head *rxq) | ||
295 | { | ||
296 | u16 buf[4] = { 0 }, status, timelo, timehi, seq_id; | ||
297 | struct skb_shared_hwtstamps *shwt; | ||
298 | int err; | ||
299 | |||
300 | mutex_lock(&chip->reg_lock); | ||
301 | err = mv88e6xxx_port_ptp_read(chip, ps->port_id, | ||
302 | reg, buf, ARRAY_SIZE(buf)); | ||
303 | mutex_unlock(&chip->reg_lock); | ||
304 | if (err) | ||
305 | pr_err("failed to get the receive time stamp\n"); | ||
306 | |||
307 | status = buf[0]; | ||
308 | timelo = buf[1]; | ||
309 | timehi = buf[2]; | ||
310 | seq_id = buf[3]; | ||
311 | |||
312 | if (status & MV88E6XXX_PTP_TS_VALID) { | ||
313 | mutex_lock(&chip->reg_lock); | ||
314 | err = mv88e6xxx_port_ptp_write(chip, ps->port_id, reg, 0); | ||
315 | mutex_unlock(&chip->reg_lock); | ||
316 | if (err) | ||
317 | pr_err("failed to clear the receive status\n"); | ||
318 | } | ||
319 | /* Since the device can only handle one time stamp at a time, | ||
320 | * we purge any extra frames from the queue. | ||
321 | */ | ||
322 | for ( ; skb; skb = skb_dequeue(rxq)) { | ||
323 | if (mv88e6xxx_ts_valid(status) && seq_match(skb, seq_id)) { | ||
324 | u64 ns = timehi << 16 | timelo; | ||
325 | |||
326 | mutex_lock(&chip->reg_lock); | ||
327 | ns = timecounter_cyc2time(&chip->tstamp_tc, ns); | ||
328 | mutex_unlock(&chip->reg_lock); | ||
329 | shwt = skb_hwtstamps(skb); | ||
330 | memset(shwt, 0, sizeof(*shwt)); | ||
331 | shwt->hwtstamp = ns_to_ktime(ns); | ||
332 | status &= ~MV88E6XXX_PTP_TS_VALID; | ||
333 | } | ||
334 | netif_rx_ni(skb); | ||
335 | } | ||
336 | } | ||
337 | |||
338 | static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_chip *chip, | ||
339 | struct mv88e6xxx_port_hwtstamp *ps) | ||
340 | { | ||
341 | struct sk_buff *skb; | ||
342 | |||
343 | skb = skb_dequeue(&ps->rx_queue); | ||
344 | |||
345 | if (skb) | ||
346 | mv88e6xxx_get_rxts(chip, ps, skb, MV88E6XXX_PORT_PTP_ARR0_STS, | ||
347 | &ps->rx_queue); | ||
348 | |||
349 | skb = skb_dequeue(&ps->rx_queue2); | ||
350 | if (skb) | ||
351 | mv88e6xxx_get_rxts(chip, ps, skb, MV88E6XXX_PORT_PTP_ARR1_STS, | ||
352 | &ps->rx_queue2); | ||
353 | } | ||
354 | |||
355 | static int is_pdelay_resp(u8 *msgtype) | ||
356 | { | ||
357 | return (*msgtype & 0xf) == 3; | ||
358 | } | ||
359 | |||
360 | bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, | ||
361 | struct sk_buff *skb, unsigned int type) | ||
362 | { | ||
363 | struct mv88e6xxx_port_hwtstamp *ps; | ||
364 | struct mv88e6xxx_chip *chip; | ||
365 | u8 *hdr; | ||
366 | |||
367 | chip = ds->priv; | ||
368 | ps = &chip->port_hwtstamp[port]; | ||
369 | |||
370 | if (ps->tstamp_config.rx_filter != HWTSTAMP_FILTER_PTP_V2_EVENT) | ||
371 | return false; | ||
372 | |||
373 | hdr = mv88e6xxx_should_tstamp(chip, port, skb, type); | ||
374 | if (!hdr) | ||
375 | return false; | ||
376 | |||
377 | SKB_PTP_TYPE(skb) = type; | ||
378 | |||
379 | if (is_pdelay_resp(hdr)) | ||
380 | skb_queue_tail(&ps->rx_queue2, skb); | ||
381 | else | ||
382 | skb_queue_tail(&ps->rx_queue, skb); | ||
383 | |||
384 | ptp_schedule_worker(chip->ptp_clock, 0); | ||
385 | |||
386 | return true; | ||
387 | } | ||
388 | |||
389 | static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip, | ||
390 | struct mv88e6xxx_port_hwtstamp *ps) | ||
391 | { | ||
392 | struct skb_shared_hwtstamps shhwtstamps; | ||
393 | u16 departure_block[4], status; | ||
394 | struct sk_buff *tmp_skb; | ||
395 | u32 time_raw; | ||
396 | int err; | ||
397 | u64 ns; | ||
398 | |||
399 | if (!ps->tx_skb) | ||
400 | return 0; | ||
401 | |||
402 | mutex_lock(&chip->reg_lock); | ||
403 | err = mv88e6xxx_port_ptp_read(chip, ps->port_id, | ||
404 | MV88E6XXX_PORT_PTP_DEP_STS, | ||
405 | departure_block, | ||
406 | ARRAY_SIZE(departure_block)); | ||
407 | mutex_unlock(&chip->reg_lock); | ||
408 | |||
409 | if (err) | ||
410 | goto free_and_clear_skb; | ||
411 | |||
412 | if (!(departure_block[0] & MV88E6XXX_PTP_TS_VALID)) { | ||
413 | if (time_is_before_jiffies(ps->tx_tstamp_start + | ||
414 | TX_TSTAMP_TIMEOUT)) { | ||
415 | dev_warn(chip->dev, "p%d: clearing tx timestamp hang\n", | ||
416 | ps->port_id); | ||
417 | goto free_and_clear_skb; | ||
418 | } | ||
419 | /* The timestamp should be available quickly, while getting it | ||
420 | * is high priority and time bounded to only 10ms. A poll is | ||
421 | * warranted so restart the work. | ||
422 | */ | ||
423 | return 1; | ||
424 | } | ||
425 | |||
426 | /* We have the timestamp; go ahead and clear valid now */ | ||
427 | mutex_lock(&chip->reg_lock); | ||
428 | mv88e6xxx_port_ptp_write(chip, ps->port_id, | ||
429 | MV88E6XXX_PORT_PTP_DEP_STS, 0); | ||
430 | mutex_unlock(&chip->reg_lock); | ||
431 | |||
432 | status = departure_block[0] & MV88E6XXX_PTP_TS_STATUS_MASK; | ||
433 | if (status != MV88E6XXX_PTP_TS_STATUS_NORMAL) { | ||
434 | dev_warn(chip->dev, "p%d: tx timestamp overrun\n", ps->port_id); | ||
435 | goto free_and_clear_skb; | ||
436 | } | ||
437 | |||
438 | if (departure_block[3] != ps->tx_seq_id) { | ||
439 | dev_warn(chip->dev, "p%d: unexpected seq. id\n", ps->port_id); | ||
440 | goto free_and_clear_skb; | ||
441 | } | ||
442 | |||
443 | memset(&shhwtstamps, 0, sizeof(shhwtstamps)); | ||
444 | time_raw = ((u32)departure_block[2] << 16) | departure_block[1]; | ||
445 | mutex_lock(&chip->reg_lock); | ||
446 | ns = timecounter_cyc2time(&chip->tstamp_tc, time_raw); | ||
447 | mutex_unlock(&chip->reg_lock); | ||
448 | shhwtstamps.hwtstamp = ns_to_ktime(ns); | ||
449 | |||
450 | dev_dbg(chip->dev, | ||
451 | "p%d: txtstamp %llx status 0x%04x skb ID 0x%04x hw ID 0x%04x\n", | ||
452 | ps->port_id, ktime_to_ns(shhwtstamps.hwtstamp), | ||
453 | departure_block[0], ps->tx_seq_id, departure_block[3]); | ||
454 | |||
455 | /* skb_complete_tx_timestamp() will free up the client to make | ||
456 | * another timestamp-able transmit. We have to be ready for it | ||
457 | * -- by clearing the ps->tx_skb "flag" -- beforehand. | ||
458 | */ | ||
459 | |||
460 | tmp_skb = ps->tx_skb; | ||
461 | ps->tx_skb = NULL; | ||
462 | clear_bit_unlock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, &ps->state); | ||
463 | skb_complete_tx_timestamp(tmp_skb, &shhwtstamps); | ||
464 | |||
465 | return 0; | ||
466 | |||
467 | free_and_clear_skb: | ||
468 | dev_kfree_skb_any(ps->tx_skb); | ||
469 | ps->tx_skb = NULL; | ||
470 | clear_bit_unlock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, &ps->state); | ||
471 | |||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp) | ||
476 | { | ||
477 | struct mv88e6xxx_chip *chip = ptp_to_chip(ptp); | ||
478 | struct dsa_switch *ds = chip->ds; | ||
479 | struct mv88e6xxx_port_hwtstamp *ps; | ||
480 | int i, restart = 0; | ||
481 | |||
482 | for (i = 0; i < ds->num_ports; i++) { | ||
483 | if (!dsa_is_user_port(ds, i)) | ||
484 | continue; | ||
485 | |||
486 | ps = &chip->port_hwtstamp[i]; | ||
487 | if (test_bit(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, &ps->state)) | ||
488 | restart |= mv88e6xxx_txtstamp_work(chip, ps); | ||
489 | |||
490 | mv88e6xxx_rxtstamp_work(chip, ps); | ||
491 | } | ||
492 | |||
493 | return restart ? 1 : -1; | ||
494 | } | ||
495 | |||
496 | bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, | ||
497 | struct sk_buff *clone, unsigned int type) | ||
498 | { | ||
499 | struct mv88e6xxx_chip *chip = ds->priv; | ||
500 | struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; | ||
501 | __be16 *seq_ptr; | ||
502 | u8 *hdr; | ||
503 | |||
504 | if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP)) | ||
505 | return false; | ||
506 | |||
507 | hdr = mv88e6xxx_should_tstamp(chip, port, clone, type); | ||
508 | if (!hdr) | ||
509 | return false; | ||
510 | |||
511 | seq_ptr = (__be16 *)(hdr + OFF_PTP_SEQUENCE_ID); | ||
512 | |||
513 | if (test_and_set_bit_lock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, | ||
514 | &ps->state)) | ||
515 | return false; | ||
516 | |||
517 | ps->tx_skb = clone; | ||
518 | ps->tx_tstamp_start = jiffies; | ||
519 | ps->tx_seq_id = be16_to_cpup(seq_ptr); | ||
520 | |||
521 | ptp_schedule_worker(chip->ptp_clock, 0); | ||
522 | return true; | ||
523 | } | ||
524 | |||
525 | static int mv88e6xxx_hwtstamp_port_setup(struct mv88e6xxx_chip *chip, int port) | ||
526 | { | ||
527 | struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; | ||
528 | |||
529 | ps->port_id = port; | ||
530 | |||
531 | skb_queue_head_init(&ps->rx_queue); | ||
532 | skb_queue_head_init(&ps->rx_queue2); | ||
533 | |||
534 | return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0, | ||
535 | MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP); | ||
536 | } | ||
537 | |||
538 | int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip) | ||
539 | { | ||
540 | int err; | ||
541 | int i; | ||
542 | |||
543 | /* Disable timestamping on all ports. */ | ||
544 | for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { | ||
545 | err = mv88e6xxx_hwtstamp_port_setup(chip, i); | ||
546 | if (err) | ||
547 | return err; | ||
548 | } | ||
549 | |||
550 | /* MV88E6XXX_PTP_MSG_TYPE is a mask of PTP message types to | ||
551 | * timestamp. This affects all ports that have timestamping enabled, | ||
552 | * but the timestamp config is per-port; thus we configure all events | ||
553 | * here and only support the HWTSTAMP_FILTER_*_EVENT filter types. | ||
554 | */ | ||
555 | err = mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_MSGTYPE, | ||
556 | MV88E6XXX_PTP_MSGTYPE_ALL_EVENT); | ||
557 | if (err) | ||
558 | return err; | ||
559 | |||
560 | /* Use ARRIVAL1 for peer delay response messages. */ | ||
561 | err = mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_TS_ARRIVAL_PTR, | ||
562 | MV88E6XXX_PTP_MSGTYPE_PDLAY_RES); | ||
563 | if (err) | ||
564 | return err; | ||
565 | |||
566 | /* 88E6341 devices default to timestamping at the PHY, but this has | ||
567 | * a hardware issue that results in unreliable timestamps. Force | ||
568 | * these devices to timestamp at the MAC. | ||
569 | */ | ||
570 | if (chip->info->family == MV88E6XXX_FAMILY_6341) { | ||
571 | u16 val = MV88E6341_PTP_CFG_UPDATE | | ||
572 | MV88E6341_PTP_CFG_MODE_IDX | | ||
573 | MV88E6341_PTP_CFG_MODE_TS_AT_MAC; | ||
574 | err = mv88e6xxx_ptp_write(chip, MV88E6341_PTP_CFG, val); | ||
575 | if (err) | ||
576 | return err; | ||
577 | } | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | void mv88e6xxx_hwtstamp_free(struct mv88e6xxx_chip *chip) | ||
583 | { | ||
584 | } | ||
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.h b/drivers/net/dsa/mv88e6xxx/hwtstamp.h new file mode 100644 index 000000000000..bc71c9212a08 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.h | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * Marvell 88E6xxx Switch hardware timestamping support | ||
3 | * | ||
4 | * Copyright (c) 2008 Marvell Semiconductor | ||
5 | * | ||
6 | * Copyright (c) 2017 National Instruments | ||
7 | * Erik Hons <erik.hons@ni.com> | ||
8 | * Brandon Streiff <brandon.streiff@ni.com> | ||
9 | * Dane Wagner <dane.wagner@ni.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | */ | ||
16 | |||
17 | #ifndef _MV88E6XXX_HWTSTAMP_H | ||
18 | #define _MV88E6XXX_HWTSTAMP_H | ||
19 | |||
20 | #include "chip.h" | ||
21 | |||
22 | /* Global PTP registers */ | ||
23 | /* Offset 0x00: PTP EtherType */ | ||
24 | #define MV88E6XXX_PTP_ETHERTYPE 0x00 | ||
25 | |||
26 | /* Offset 0x01: Message Type Timestamp Enables */ | ||
27 | #define MV88E6XXX_PTP_MSGTYPE 0x01 | ||
28 | #define MV88E6XXX_PTP_MSGTYPE_SYNC 0x0001 | ||
29 | #define MV88E6XXX_PTP_MSGTYPE_DELAY_REQ 0x0002 | ||
30 | #define MV88E6XXX_PTP_MSGTYPE_PDLAY_REQ 0x0004 | ||
31 | #define MV88E6XXX_PTP_MSGTYPE_PDLAY_RES 0x0008 | ||
32 | #define MV88E6XXX_PTP_MSGTYPE_ALL_EVENT 0x000f | ||
33 | |||
34 | /* Offset 0x02: Timestamp Arrival Capture Pointers */ | ||
35 | #define MV88E6XXX_PTP_TS_ARRIVAL_PTR 0x02 | ||
36 | |||
37 | /* Offset 0x07: PTP Global Configuration */ | ||
38 | #define MV88E6341_PTP_CFG 0x07 | ||
39 | #define MV88E6341_PTP_CFG_UPDATE 0x8000 | ||
40 | #define MV88E6341_PTP_CFG_IDX_MASK 0x7f00 | ||
41 | #define MV88E6341_PTP_CFG_DATA_MASK 0x00ff | ||
42 | #define MV88E6341_PTP_CFG_MODE_IDX 0x0 | ||
43 | #define MV88E6341_PTP_CFG_MODE_TS_AT_PHY 0x00 | ||
44 | #define MV88E6341_PTP_CFG_MODE_TS_AT_MAC 0x80 | ||
45 | |||
46 | /* Offset 0x08: PTP Interrupt Status */ | ||
47 | #define MV88E6XXX_PTP_IRQ_STATUS 0x08 | ||
48 | |||
49 | /* Per-Port PTP Registers */ | ||
50 | /* Offset 0x00: PTP Configuration 0 */ | ||
51 | #define MV88E6XXX_PORT_PTP_CFG0 0x00 | ||
52 | #define MV88E6XXX_PORT_PTP_CFG0_TSPEC_SHIFT 12 | ||
53 | #define MV88E6XXX_PORT_PTP_CFG0_TSPEC_MASK 0xf000 | ||
54 | #define MV88E6XXX_PORT_PTP_CFG0_TSPEC_1588 0x0000 | ||
55 | #define MV88E6XXX_PORT_PTP_CFG0_TSPEC_8021AS 0x1000 | ||
56 | #define MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH 0x0800 | ||
57 | #define MV88E6XXX_PORT_PTP_CFG0_DISABLE_OVERWRITE 0x0002 | ||
58 | #define MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP 0x0001 | ||
59 | |||
60 | /* Offset 0x01: PTP Configuration 1 */ | ||
61 | #define MV88E6XXX_PORT_PTP_CFG1 0x01 | ||
62 | |||
63 | /* Offset 0x02: PTP Configuration 2 */ | ||
64 | #define MV88E6XXX_PORT_PTP_CFG2 0x02 | ||
65 | #define MV88E6XXX_PORT_PTP_CFG2_EMBED_ARRIVAL 0x1000 | ||
66 | #define MV88E6XXX_PORT_PTP_CFG2_DEP_IRQ_EN 0x0002 | ||
67 | #define MV88E6XXX_PORT_PTP_CFG2_ARR_IRQ_EN 0x0001 | ||
68 | |||
69 | /* Offset 0x03: PTP LED Configuration */ | ||
70 | #define MV88E6XXX_PORT_PTP_LED_CFG 0x03 | ||
71 | |||
72 | /* Offset 0x08: PTP Arrival 0 Status */ | ||
73 | #define MV88E6XXX_PORT_PTP_ARR0_STS 0x08 | ||
74 | |||
75 | /* Offset 0x09/0x0A: PTP Arrival 0 Time */ | ||
76 | #define MV88E6XXX_PORT_PTP_ARR0_TIME_LO 0x09 | ||
77 | #define MV88E6XXX_PORT_PTP_ARR0_TIME_HI 0x0a | ||
78 | |||
79 | /* Offset 0x0B: PTP Arrival 0 Sequence ID */ | ||
80 | #define MV88E6XXX_PORT_PTP_ARR0_SEQID 0x0b | ||
81 | |||
82 | /* Offset 0x0C: PTP Arrival 1 Status */ | ||
83 | #define MV88E6XXX_PORT_PTP_ARR1_STS 0x0c | ||
84 | |||
85 | /* Offset 0x0D/0x0E: PTP Arrival 1 Time */ | ||
86 | #define MV88E6XXX_PORT_PTP_ARR1_TIME_LO 0x0d | ||
87 | #define MV88E6XXX_PORT_PTP_ARR1_TIME_HI 0x0e | ||
88 | |||
89 | /* Offset 0x0F: PTP Arrival 1 Sequence ID */ | ||
90 | #define MV88E6XXX_PORT_PTP_ARR1_SEQID 0x0f | ||
91 | |||
92 | /* Offset 0x10: PTP Departure Status */ | ||
93 | #define MV88E6XXX_PORT_PTP_DEP_STS 0x10 | ||
94 | |||
95 | /* Offset 0x11/0x12: PTP Deperture Time */ | ||
96 | #define MV88E6XXX_PORT_PTP_DEP_TIME_LO 0x11 | ||
97 | #define MV88E6XXX_PORT_PTP_DEP_TIME_HI 0x12 | ||
98 | |||
99 | /* Offset 0x13: PTP Departure Sequence ID */ | ||
100 | #define MV88E6XXX_PORT_PTP_DEP_SEQID 0x13 | ||
101 | |||
102 | /* Status fields for arrival and depature timestamp status registers */ | ||
103 | #define MV88E6XXX_PTP_TS_STATUS_MASK 0x0006 | ||
104 | #define MV88E6XXX_PTP_TS_STATUS_NORMAL 0x0000 | ||
105 | #define MV88E6XXX_PTP_TS_STATUS_OVERWITTEN 0x0002 | ||
106 | #define MV88E6XXX_PTP_TS_STATUS_DISCARDED 0x0004 | ||
107 | #define MV88E6XXX_PTP_TS_VALID 0x0001 | ||
108 | |||
109 | #ifdef CONFIG_NET_DSA_MV88E6XXX_PTP | ||
110 | |||
111 | int mv88e6xxx_port_hwtstamp_set(struct dsa_switch *ds, int port, | ||
112 | struct ifreq *ifr); | ||
113 | int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds, int port, | ||
114 | struct ifreq *ifr); | ||
115 | |||
116 | bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, | ||
117 | struct sk_buff *clone, unsigned int type); | ||
118 | bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, | ||
119 | struct sk_buff *clone, unsigned int type); | ||
120 | |||
121 | int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, | ||
122 | struct ethtool_ts_info *info); | ||
123 | |||
124 | int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip); | ||
125 | void mv88e6xxx_hwtstamp_free(struct mv88e6xxx_chip *chip); | ||
126 | |||
127 | #else /* !CONFIG_NET_DSA_MV88E6XXX_PTP */ | ||
128 | |||
129 | static inline int mv88e6xxx_port_hwtstamp_set(struct dsa_switch *ds, | ||
130 | int port, struct ifreq *ifr) | ||
131 | { | ||
132 | return -EOPNOTSUPP; | ||
133 | } | ||
134 | |||
135 | static inline int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds, | ||
136 | int port, struct ifreq *ifr) | ||
137 | { | ||
138 | return -EOPNOTSUPP; | ||
139 | } | ||
140 | |||
141 | static inline bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, | ||
142 | struct sk_buff *clone, | ||
143 | unsigned int type) | ||
144 | { | ||
145 | return false; | ||
146 | } | ||
147 | |||
148 | static inline bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, | ||
149 | struct sk_buff *clone, | ||
150 | unsigned int type) | ||
151 | { | ||
152 | return false; | ||
153 | } | ||
154 | |||
155 | static inline int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, | ||
156 | struct ethtool_ts_info *info) | ||
157 | { | ||
158 | return -EOPNOTSUPP; | ||
159 | } | ||
160 | |||
161 | static inline int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip) | ||
162 | { | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static inline void mv88e6xxx_hwtstamp_free(struct mv88e6xxx_chip *chip) | ||
167 | { | ||
168 | } | ||
169 | |||
170 | #endif /* CONFIG_NET_DSA_MV88E6XXX_PTP */ | ||
171 | |||
172 | #endif /* _MV88E6XXX_HWTSTAMP_H */ | ||
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.c b/drivers/net/dsa/mv88e6xxx/ptp.c new file mode 100644 index 000000000000..bd85e2c390e1 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/ptp.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | * Marvell 88E6xxx Switch PTP support | ||
3 | * | ||
4 | * Copyright (c) 2008 Marvell Semiconductor | ||
5 | * | ||
6 | * Copyright (c) 2017 National Instruments | ||
7 | * Erik Hons <erik.hons@ni.com> | ||
8 | * Brandon Streiff <brandon.streiff@ni.com> | ||
9 | * Dane Wagner <dane.wagner@ni.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | */ | ||
16 | |||
17 | #include "chip.h" | ||
18 | #include "global2.h" | ||
19 | #include "ptp.h" | ||
20 | |||
21 | /* Raw timestamps are in units of 8-ns clock periods. */ | ||
22 | #define CC_SHIFT 28 | ||
23 | #define CC_MULT (8 << CC_SHIFT) | ||
24 | #define CC_MULT_NUM (1 << 9) | ||
25 | #define CC_MULT_DEM 15625ULL | ||
26 | |||
27 | #define TAI_EVENT_WORK_INTERVAL msecs_to_jiffies(100) | ||
28 | |||
29 | #define cc_to_chip(cc) container_of(cc, struct mv88e6xxx_chip, tstamp_cc) | ||
30 | #define dw_overflow_to_chip(dw) container_of(dw, struct mv88e6xxx_chip, \ | ||
31 | overflow_work) | ||
32 | #define dw_tai_event_to_chip(dw) container_of(dw, struct mv88e6xxx_chip, \ | ||
33 | tai_event_work) | ||
34 | |||
35 | static int mv88e6xxx_tai_read(struct mv88e6xxx_chip *chip, int addr, | ||
36 | u16 *data, int len) | ||
37 | { | ||
38 | if (!chip->info->ops->avb_ops->tai_read) | ||
39 | return -EOPNOTSUPP; | ||
40 | |||
41 | return chip->info->ops->avb_ops->tai_read(chip, addr, data, len); | ||
42 | } | ||
43 | |||
44 | static int mv88e6xxx_tai_write(struct mv88e6xxx_chip *chip, int addr, u16 data) | ||
45 | { | ||
46 | if (!chip->info->ops->avb_ops->tai_write) | ||
47 | return -EOPNOTSUPP; | ||
48 | |||
49 | return chip->info->ops->avb_ops->tai_write(chip, addr, data); | ||
50 | } | ||
51 | |||
52 | /* TODO: places where this are called should be using pinctrl */ | ||
53 | static int mv88e6xxx_set_gpio_func(struct mv88e6xxx_chip *chip, int pin, | ||
54 | int func, int input) | ||
55 | { | ||
56 | int err; | ||
57 | |||
58 | if (!chip->info->ops->gpio_ops) | ||
59 | return -EOPNOTSUPP; | ||
60 | |||
61 | err = chip->info->ops->gpio_ops->set_dir(chip, pin, input); | ||
62 | if (err) | ||
63 | return err; | ||
64 | |||
65 | return chip->info->ops->gpio_ops->set_pctl(chip, pin, func); | ||
66 | } | ||
67 | |||
68 | static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc) | ||
69 | { | ||
70 | struct mv88e6xxx_chip *chip = cc_to_chip(cc); | ||
71 | u16 phc_time[2]; | ||
72 | int err; | ||
73 | |||
74 | err = mv88e6xxx_tai_read(chip, MV88E6XXX_TAI_TIME_LO, phc_time, | ||
75 | ARRAY_SIZE(phc_time)); | ||
76 | if (err) | ||
77 | return 0; | ||
78 | else | ||
79 | return ((u32)phc_time[1] << 16) | phc_time[0]; | ||
80 | } | ||
81 | |||
82 | /* mv88e6xxx_config_eventcap - configure TAI event capture | ||
83 | * @event: PTP_CLOCK_PPS (internal) or PTP_CLOCK_EXTTS (external) | ||
84 | * @rising: zero for falling-edge trigger, else rising-edge trigger | ||
85 | * | ||
86 | * This will also reset the capture sequence counter. | ||
87 | */ | ||
88 | static int mv88e6xxx_config_eventcap(struct mv88e6xxx_chip *chip, int event, | ||
89 | int rising) | ||
90 | { | ||
91 | u16 global_config; | ||
92 | u16 cap_config; | ||
93 | int err; | ||
94 | |||
95 | chip->evcap_config = MV88E6XXX_TAI_CFG_CAP_OVERWRITE | | ||
96 | MV88E6XXX_TAI_CFG_CAP_CTR_START; | ||
97 | if (!rising) | ||
98 | chip->evcap_config |= MV88E6XXX_TAI_CFG_EVREQ_FALLING; | ||
99 | |||
100 | global_config = (chip->evcap_config | chip->trig_config); | ||
101 | err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_CFG, global_config); | ||
102 | if (err) | ||
103 | return err; | ||
104 | |||
105 | if (event == PTP_CLOCK_PPS) { | ||
106 | cap_config = MV88E6XXX_TAI_EVENT_STATUS_CAP_TRIG; | ||
107 | } else if (event == PTP_CLOCK_EXTTS) { | ||
108 | /* if STATUS_CAP_TRIG is unset we capture PTP_EVREQ events */ | ||
109 | cap_config = 0; | ||
110 | } else { | ||
111 | return -EINVAL; | ||
112 | } | ||
113 | |||
114 | /* Write the capture config; this also clears the capture counter */ | ||
115 | err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_EVENT_STATUS, | ||
116 | cap_config); | ||
117 | |||
118 | return err; | ||
119 | } | ||
120 | |||
121 | static void mv88e6xxx_tai_event_work(struct work_struct *ugly) | ||
122 | { | ||
123 | struct delayed_work *dw = to_delayed_work(ugly); | ||
124 | struct mv88e6xxx_chip *chip = dw_tai_event_to_chip(dw); | ||
125 | struct ptp_clock_event ev; | ||
126 | u16 status[4]; | ||
127 | u32 raw_ts; | ||
128 | int err; | ||
129 | |||
130 | mutex_lock(&chip->reg_lock); | ||
131 | err = mv88e6xxx_tai_read(chip, MV88E6XXX_TAI_EVENT_STATUS, | ||
132 | status, ARRAY_SIZE(status)); | ||
133 | mutex_unlock(&chip->reg_lock); | ||
134 | |||
135 | if (err) { | ||
136 | dev_err(chip->dev, "failed to read TAI status register\n"); | ||
137 | return; | ||
138 | } | ||
139 | if (status[0] & MV88E6XXX_TAI_EVENT_STATUS_ERROR) { | ||
140 | dev_warn(chip->dev, "missed event capture\n"); | ||
141 | return; | ||
142 | } | ||
143 | if (!(status[0] & MV88E6XXX_TAI_EVENT_STATUS_VALID)) | ||
144 | goto out; | ||
145 | |||
146 | raw_ts = ((u32)status[2] << 16) | status[1]; | ||
147 | |||
148 | /* Clear the valid bit so the next timestamp can come in */ | ||
149 | status[0] &= ~MV88E6XXX_TAI_EVENT_STATUS_VALID; | ||
150 | mutex_lock(&chip->reg_lock); | ||
151 | err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_EVENT_STATUS, status[0]); | ||
152 | mutex_unlock(&chip->reg_lock); | ||
153 | |||
154 | /* This is an external timestamp */ | ||
155 | ev.type = PTP_CLOCK_EXTTS; | ||
156 | |||
157 | /* We only have one timestamping channel. */ | ||
158 | ev.index = 0; | ||
159 | mutex_lock(&chip->reg_lock); | ||
160 | ev.timestamp = timecounter_cyc2time(&chip->tstamp_tc, raw_ts); | ||
161 | mutex_unlock(&chip->reg_lock); | ||
162 | |||
163 | ptp_clock_event(chip->ptp_clock, &ev); | ||
164 | out: | ||
165 | schedule_delayed_work(&chip->tai_event_work, TAI_EVENT_WORK_INTERVAL); | ||
166 | } | ||
167 | |||
168 | static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) | ||
169 | { | ||
170 | struct mv88e6xxx_chip *chip = ptp_to_chip(ptp); | ||
171 | int neg_adj = 0; | ||
172 | u32 diff, mult; | ||
173 | u64 adj; | ||
174 | |||
175 | if (scaled_ppm < 0) { | ||
176 | neg_adj = 1; | ||
177 | scaled_ppm = -scaled_ppm; | ||
178 | } | ||
179 | mult = CC_MULT; | ||
180 | adj = CC_MULT_NUM; | ||
181 | adj *= scaled_ppm; | ||
182 | diff = div_u64(adj, CC_MULT_DEM); | ||
183 | |||
184 | mutex_lock(&chip->reg_lock); | ||
185 | |||
186 | timecounter_read(&chip->tstamp_tc); | ||
187 | chip->tstamp_cc.mult = neg_adj ? mult - diff : mult + diff; | ||
188 | |||
189 | mutex_unlock(&chip->reg_lock); | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int mv88e6xxx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) | ||
195 | { | ||
196 | struct mv88e6xxx_chip *chip = ptp_to_chip(ptp); | ||
197 | |||
198 | mutex_lock(&chip->reg_lock); | ||
199 | timecounter_adjtime(&chip->tstamp_tc, delta); | ||
200 | mutex_unlock(&chip->reg_lock); | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static int mv88e6xxx_ptp_gettime(struct ptp_clock_info *ptp, | ||
206 | struct timespec64 *ts) | ||
207 | { | ||
208 | struct mv88e6xxx_chip *chip = ptp_to_chip(ptp); | ||
209 | u64 ns; | ||
210 | |||
211 | mutex_lock(&chip->reg_lock); | ||
212 | ns = timecounter_read(&chip->tstamp_tc); | ||
213 | mutex_unlock(&chip->reg_lock); | ||
214 | |||
215 | *ts = ns_to_timespec64(ns); | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int mv88e6xxx_ptp_settime(struct ptp_clock_info *ptp, | ||
221 | const struct timespec64 *ts) | ||
222 | { | ||
223 | struct mv88e6xxx_chip *chip = ptp_to_chip(ptp); | ||
224 | u64 ns; | ||
225 | |||
226 | ns = timespec64_to_ns(ts); | ||
227 | |||
228 | mutex_lock(&chip->reg_lock); | ||
229 | timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc, ns); | ||
230 | mutex_unlock(&chip->reg_lock); | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static int mv88e6xxx_ptp_enable_extts(struct mv88e6xxx_chip *chip, | ||
236 | struct ptp_clock_request *rq, int on) | ||
237 | { | ||
238 | int rising = (rq->extts.flags & PTP_RISING_EDGE); | ||
239 | int func; | ||
240 | int pin; | ||
241 | int err; | ||
242 | |||
243 | pin = ptp_find_pin(chip->ptp_clock, PTP_PF_EXTTS, rq->extts.index); | ||
244 | |||
245 | if (pin < 0) | ||
246 | return -EBUSY; | ||
247 | |||
248 | mutex_lock(&chip->reg_lock); | ||
249 | |||
250 | if (on) { | ||
251 | func = MV88E6352_G2_SCRATCH_GPIO_PCTL_EVREQ; | ||
252 | |||
253 | err = mv88e6xxx_set_gpio_func(chip, pin, func, true); | ||
254 | if (err) | ||
255 | goto out; | ||
256 | |||
257 | schedule_delayed_work(&chip->tai_event_work, | ||
258 | TAI_EVENT_WORK_INTERVAL); | ||
259 | |||
260 | err = mv88e6xxx_config_eventcap(chip, PTP_CLOCK_EXTTS, rising); | ||
261 | } else { | ||
262 | func = MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO; | ||
263 | |||
264 | err = mv88e6xxx_set_gpio_func(chip, pin, func, true); | ||
265 | |||
266 | cancel_delayed_work_sync(&chip->tai_event_work); | ||
267 | } | ||
268 | |||
269 | out: | ||
270 | mutex_unlock(&chip->reg_lock); | ||
271 | |||
272 | return err; | ||
273 | } | ||
274 | |||
275 | static int mv88e6xxx_ptp_enable(struct ptp_clock_info *ptp, | ||
276 | struct ptp_clock_request *rq, int on) | ||
277 | { | ||
278 | struct mv88e6xxx_chip *chip = ptp_to_chip(ptp); | ||
279 | |||
280 | switch (rq->type) { | ||
281 | case PTP_CLK_REQ_EXTTS: | ||
282 | return mv88e6xxx_ptp_enable_extts(chip, rq, on); | ||
283 | default: | ||
284 | return -EOPNOTSUPP; | ||
285 | } | ||
286 | } | ||
287 | |||
288 | static int mv88e6xxx_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, | ||
289 | enum ptp_pin_function func, unsigned int chan) | ||
290 | { | ||
291 | switch (func) { | ||
292 | case PTP_PF_NONE: | ||
293 | case PTP_PF_EXTTS: | ||
294 | break; | ||
295 | case PTP_PF_PEROUT: | ||
296 | case PTP_PF_PHYSYNC: | ||
297 | return -EOPNOTSUPP; | ||
298 | } | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | /* With a 125MHz input clock, the 32-bit timestamp counter overflows in ~34.3 | ||
303 | * seconds; this task forces periodic reads so that we don't miss any. | ||
304 | */ | ||
305 | #define MV88E6XXX_TAI_OVERFLOW_PERIOD (HZ * 16) | ||
306 | static void mv88e6xxx_ptp_overflow_check(struct work_struct *work) | ||
307 | { | ||
308 | struct delayed_work *dw = to_delayed_work(work); | ||
309 | struct mv88e6xxx_chip *chip = dw_overflow_to_chip(dw); | ||
310 | struct timespec64 ts; | ||
311 | |||
312 | mv88e6xxx_ptp_gettime(&chip->ptp_clock_info, &ts); | ||
313 | |||
314 | schedule_delayed_work(&chip->overflow_work, | ||
315 | MV88E6XXX_TAI_OVERFLOW_PERIOD); | ||
316 | } | ||
317 | |||
318 | int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip) | ||
319 | { | ||
320 | int i; | ||
321 | |||
322 | /* Set up the cycle counter */ | ||
323 | memset(&chip->tstamp_cc, 0, sizeof(chip->tstamp_cc)); | ||
324 | chip->tstamp_cc.read = mv88e6xxx_ptp_clock_read; | ||
325 | chip->tstamp_cc.mask = CYCLECOUNTER_MASK(32); | ||
326 | chip->tstamp_cc.mult = CC_MULT; | ||
327 | chip->tstamp_cc.shift = CC_SHIFT; | ||
328 | |||
329 | timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc, | ||
330 | ktime_to_ns(ktime_get_real())); | ||
331 | |||
332 | INIT_DELAYED_WORK(&chip->overflow_work, mv88e6xxx_ptp_overflow_check); | ||
333 | INIT_DELAYED_WORK(&chip->tai_event_work, mv88e6xxx_tai_event_work); | ||
334 | |||
335 | chip->ptp_clock_info.owner = THIS_MODULE; | ||
336 | snprintf(chip->ptp_clock_info.name, sizeof(chip->ptp_clock_info.name), | ||
337 | dev_name(chip->dev)); | ||
338 | chip->ptp_clock_info.max_adj = 1000000; | ||
339 | |||
340 | chip->ptp_clock_info.n_ext_ts = 1; | ||
341 | chip->ptp_clock_info.n_per_out = 0; | ||
342 | chip->ptp_clock_info.n_pins = mv88e6xxx_num_gpio(chip); | ||
343 | chip->ptp_clock_info.pps = 0; | ||
344 | |||
345 | for (i = 0; i < chip->ptp_clock_info.n_pins; ++i) { | ||
346 | struct ptp_pin_desc *ppd = &chip->pin_config[i]; | ||
347 | |||
348 | snprintf(ppd->name, sizeof(ppd->name), "mv88e6xxx_gpio%d", i); | ||
349 | ppd->index = i; | ||
350 | ppd->func = PTP_PF_NONE; | ||
351 | } | ||
352 | chip->ptp_clock_info.pin_config = chip->pin_config; | ||
353 | |||
354 | chip->ptp_clock_info.adjfine = mv88e6xxx_ptp_adjfine; | ||
355 | chip->ptp_clock_info.adjtime = mv88e6xxx_ptp_adjtime; | ||
356 | chip->ptp_clock_info.gettime64 = mv88e6xxx_ptp_gettime; | ||
357 | chip->ptp_clock_info.settime64 = mv88e6xxx_ptp_settime; | ||
358 | chip->ptp_clock_info.enable = mv88e6xxx_ptp_enable; | ||
359 | chip->ptp_clock_info.verify = mv88e6xxx_ptp_verify; | ||
360 | chip->ptp_clock_info.do_aux_work = mv88e6xxx_hwtstamp_work; | ||
361 | |||
362 | chip->ptp_clock = ptp_clock_register(&chip->ptp_clock_info, chip->dev); | ||
363 | if (IS_ERR(chip->ptp_clock)) | ||
364 | return PTR_ERR(chip->ptp_clock); | ||
365 | |||
366 | schedule_delayed_work(&chip->overflow_work, | ||
367 | MV88E6XXX_TAI_OVERFLOW_PERIOD); | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip) | ||
373 | { | ||
374 | if (chip->ptp_clock) { | ||
375 | cancel_delayed_work_sync(&chip->overflow_work); | ||
376 | cancel_delayed_work_sync(&chip->tai_event_work); | ||
377 | |||
378 | ptp_clock_unregister(chip->ptp_clock); | ||
379 | chip->ptp_clock = NULL; | ||
380 | } | ||
381 | } | ||
diff --git a/drivers/net/dsa/mv88e6xxx/ptp.h b/drivers/net/dsa/mv88e6xxx/ptp.h new file mode 100644 index 000000000000..992818ade746 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/ptp.h | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | * Marvell 88E6xxx Switch PTP support | ||
3 | * | ||
4 | * Copyright (c) 2008 Marvell Semiconductor | ||
5 | * | ||
6 | * Copyright (c) 2017 National Instruments | ||
7 | * Erik Hons <erik.hons@ni.com> | ||
8 | * Brandon Streiff <brandon.streiff@ni.com> | ||
9 | * Dane Wagner <dane.wagner@ni.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | */ | ||
16 | |||
17 | #ifndef _MV88E6XXX_PTP_H | ||
18 | #define _MV88E6XXX_PTP_H | ||
19 | |||
20 | #include "chip.h" | ||
21 | |||
22 | /* Offset 0x00: TAI Global Config */ | ||
23 | #define MV88E6XXX_TAI_CFG 0x00 | ||
24 | #define MV88E6XXX_TAI_CFG_CAP_OVERWRITE 0x8000 | ||
25 | #define MV88E6XXX_TAI_CFG_CAP_CTR_START 0x4000 | ||
26 | #define MV88E6XXX_TAI_CFG_EVREQ_FALLING 0x2000 | ||
27 | #define MV88E6XXX_TAI_CFG_TRIG_ACTIVE_LO 0x1000 | ||
28 | #define MV88E6XXX_TAI_CFG_IRL_ENABLE 0x0400 | ||
29 | #define MV88E6XXX_TAI_CFG_TRIG_IRQ_EN 0x0200 | ||
30 | #define MV88E6XXX_TAI_CFG_EVREQ_IRQ_EN 0x0100 | ||
31 | #define MV88E6XXX_TAI_CFG_TRIG_LOCK 0x0080 | ||
32 | #define MV88E6XXX_TAI_CFG_BLOCK_UPDATE 0x0008 | ||
33 | #define MV88E6XXX_TAI_CFG_MULTI_PTP 0x0004 | ||
34 | #define MV88E6XXX_TAI_CFG_TRIG_MODE_ONESHOT 0x0002 | ||
35 | #define MV88E6XXX_TAI_CFG_TRIG_ENABLE 0x0001 | ||
36 | |||
37 | /* Offset 0x01: Timestamp Clock Period (ps) */ | ||
38 | #define MV88E6XXX_TAI_CLOCK_PERIOD 0x01 | ||
39 | |||
40 | /* Offset 0x02/0x03: Trigger Generation Amount */ | ||
41 | #define MV88E6XXX_TAI_TRIG_GEN_AMOUNT_LO 0x02 | ||
42 | #define MV88E6XXX_TAI_TRIG_GEN_AMOUNT_HI 0x03 | ||
43 | |||
44 | /* Offset 0x04: Clock Compensation */ | ||
45 | #define MV88E6XXX_TAI_TRIG_CLOCK_COMP 0x04 | ||
46 | |||
47 | /* Offset 0x05: Trigger Configuration */ | ||
48 | #define MV88E6XXX_TAI_TRIG_CFG 0x05 | ||
49 | |||
50 | /* Offset 0x06: Ingress Rate Limiter Clock Generation Amount */ | ||
51 | #define MV88E6XXX_TAI_IRL_AMOUNT 0x06 | ||
52 | |||
53 | /* Offset 0x07: Ingress Rate Limiter Compensation */ | ||
54 | #define MV88E6XXX_TAI_IRL_COMP 0x07 | ||
55 | |||
56 | /* Offset 0x08: Ingress Rate Limiter Compensation */ | ||
57 | #define MV88E6XXX_TAI_IRL_COMP_PS 0x08 | ||
58 | |||
59 | /* Offset 0x09: Event Status */ | ||
60 | #define MV88E6XXX_TAI_EVENT_STATUS 0x09 | ||
61 | #define MV88E6XXX_TAI_EVENT_STATUS_CAP_TRIG 0x4000 | ||
62 | #define MV88E6XXX_TAI_EVENT_STATUS_ERROR 0x0200 | ||
63 | #define MV88E6XXX_TAI_EVENT_STATUS_VALID 0x0100 | ||
64 | #define MV88E6XXX_TAI_EVENT_STATUS_CTR_MASK 0x00ff | ||
65 | |||
66 | /* Offset 0x0A/0x0B: Event Time */ | ||
67 | #define MV88E6XXX_TAI_EVENT_TIME_LO 0x0a | ||
68 | #define MV88E6XXX_TAI_EVENT_TYPE_HI 0x0b | ||
69 | |||
70 | /* Offset 0x0E/0x0F: PTP Global Time */ | ||
71 | #define MV88E6XXX_TAI_TIME_LO 0x0e | ||
72 | #define MV88E6XXX_TAI_TIME_HI 0x0f | ||
73 | |||
74 | /* Offset 0x10/0x11: Trig Generation Time */ | ||
75 | #define MV88E6XXX_TAI_TRIG_TIME_LO 0x10 | ||
76 | #define MV88E6XXX_TAI_TRIG_TIME_HI 0x11 | ||
77 | |||
78 | /* Offset 0x12: Lock Status */ | ||
79 | #define MV88E6XXX_TAI_LOCK_STATUS 0x12 | ||
80 | |||
81 | #ifdef CONFIG_NET_DSA_MV88E6XXX_PTP | ||
82 | |||
83 | long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp); | ||
84 | int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip); | ||
85 | void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip); | ||
86 | |||
87 | #define ptp_to_chip(ptp) container_of(ptp, struct mv88e6xxx_chip, \ | ||
88 | ptp_clock_info) | ||
89 | |||
90 | #else /* !CONFIG_NET_DSA_MV88E6XXX_PTP */ | ||
91 | |||
92 | static long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp) | ||
93 | { | ||
94 | return -1; | ||
95 | } | ||
96 | |||
97 | static inline int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip) | ||
98 | { | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip) | ||
103 | { | ||
104 | } | ||
105 | |||
106 | #endif /* CONFIG_NET_DSA_MV88E6XXX_PTP */ | ||
107 | |||
108 | #endif /* _MV88E6XXX_PTP_H */ | ||
diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h index a079656b614c..059242030631 100644 --- a/include/linux/ptp_classify.h +++ b/include/linux/ptp_classify.h | |||
@@ -75,5 +75,9 @@ void __init ptp_classifier_init(void); | |||
75 | static inline void ptp_classifier_init(void) | 75 | static inline void ptp_classifier_init(void) |
76 | { | 76 | { |
77 | } | 77 | } |
78 | static inline unsigned int ptp_classify_raw(struct sk_buff *skb) | ||
79 | { | ||
80 | return PTP_CLASS_NONE; | ||
81 | } | ||
78 | #endif | 82 | #endif |
79 | #endif /* _PTP_CLASSIFY_H_ */ | 83 | #endif /* _PTP_CLASSIFY_H_ */ |
diff --git a/include/net/dsa.h b/include/net/dsa.h index 6cb602dd970c..0ad17b63684d 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/workqueue.h> | 19 | #include <linux/workqueue.h> |
20 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/ethtool.h> | 21 | #include <linux/ethtool.h> |
22 | #include <linux/net_tstamp.h> | ||
22 | #include <net/devlink.h> | 23 | #include <net/devlink.h> |
23 | #include <net/switchdev.h> | 24 | #include <net/switchdev.h> |
24 | 25 | ||
@@ -101,6 +102,7 @@ struct dsa_platform_data { | |||
101 | }; | 102 | }; |
102 | 103 | ||
103 | struct packet_type; | 104 | struct packet_type; |
105 | struct dsa_switch; | ||
104 | 106 | ||
105 | struct dsa_device_ops { | 107 | struct dsa_device_ops { |
106 | struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); | 108 | struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); |
@@ -368,6 +370,12 @@ struct dsa_switch_ops { | |||
368 | struct ethtool_wolinfo *w); | 370 | struct ethtool_wolinfo *w); |
369 | 371 | ||
370 | /* | 372 | /* |
373 | * ethtool timestamp info | ||
374 | */ | ||
375 | int (*get_ts_info)(struct dsa_switch *ds, int port, | ||
376 | struct ethtool_ts_info *ts); | ||
377 | |||
378 | /* | ||
371 | * Suspend and resume | 379 | * Suspend and resume |
372 | */ | 380 | */ |
373 | int (*suspend)(struct dsa_switch *ds); | 381 | int (*suspend)(struct dsa_switch *ds); |
@@ -469,6 +477,18 @@ struct dsa_switch_ops { | |||
469 | int port, struct net_device *br); | 477 | int port, struct net_device *br); |
470 | void (*crosschip_bridge_leave)(struct dsa_switch *ds, int sw_index, | 478 | void (*crosschip_bridge_leave)(struct dsa_switch *ds, int sw_index, |
471 | int port, struct net_device *br); | 479 | int port, struct net_device *br); |
480 | |||
481 | /* | ||
482 | * PTP functionality | ||
483 | */ | ||
484 | int (*port_hwtstamp_get)(struct dsa_switch *ds, int port, | ||
485 | struct ifreq *ifr); | ||
486 | int (*port_hwtstamp_set)(struct dsa_switch *ds, int port, | ||
487 | struct ifreq *ifr); | ||
488 | bool (*port_txtstamp)(struct dsa_switch *ds, int port, | ||
489 | struct sk_buff *clone, unsigned int type); | ||
490 | bool (*port_rxtstamp)(struct dsa_switch *ds, int port, | ||
491 | struct sk_buff *skb, unsigned int type); | ||
472 | }; | 492 | }; |
473 | 493 | ||
474 | struct dsa_switch_driver { | 494 | struct dsa_switch_driver { |
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 6a9d0f50fbee..e63c554e0623 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/netdevice.h> | 23 | #include <linux/netdevice.h> |
24 | #include <linux/sysfs.h> | 24 | #include <linux/sysfs.h> |
25 | #include <linux/phy_fixed.h> | 25 | #include <linux/phy_fixed.h> |
26 | #include <linux/ptp_classify.h> | ||
26 | #include <linux/gpio/consumer.h> | 27 | #include <linux/gpio/consumer.h> |
27 | #include <linux/etherdevice.h> | 28 | #include <linux/etherdevice.h> |
28 | 29 | ||
@@ -122,6 +123,38 @@ struct net_device *dsa_dev_to_net_device(struct device *dev) | |||
122 | } | 123 | } |
123 | EXPORT_SYMBOL_GPL(dsa_dev_to_net_device); | 124 | EXPORT_SYMBOL_GPL(dsa_dev_to_net_device); |
124 | 125 | ||
126 | /* Determine if we should defer delivery of skb until we have a rx timestamp. | ||
127 | * | ||
128 | * Called from dsa_switch_rcv. For now, this will only work if tagging is | ||
129 | * enabled on the switch. Normally the MAC driver would retrieve the hardware | ||
130 | * timestamp when it reads the packet out of the hardware. However in a DSA | ||
131 | * switch, the DSA driver owning the interface to which the packet is | ||
132 | * delivered is never notified unless we do so here. | ||
133 | */ | ||
134 | static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p, | ||
135 | struct sk_buff *skb) | ||
136 | { | ||
137 | struct dsa_switch *ds = p->dp->ds; | ||
138 | unsigned int type; | ||
139 | |||
140 | if (skb_headroom(skb) < ETH_HLEN) | ||
141 | return false; | ||
142 | |||
143 | __skb_push(skb, ETH_HLEN); | ||
144 | |||
145 | type = ptp_classify_raw(skb); | ||
146 | |||
147 | __skb_pull(skb, ETH_HLEN); | ||
148 | |||
149 | if (type == PTP_CLASS_NONE) | ||
150 | return false; | ||
151 | |||
152 | if (likely(ds->ops->port_rxtstamp)) | ||
153 | return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type); | ||
154 | |||
155 | return false; | ||
156 | } | ||
157 | |||
125 | static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, | 158 | static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, |
126 | struct packet_type *pt, struct net_device *unused) | 159 | struct packet_type *pt, struct net_device *unused) |
127 | { | 160 | { |
@@ -157,6 +190,9 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, | |||
157 | s->rx_bytes += skb->len; | 190 | s->rx_bytes += skb->len; |
158 | u64_stats_update_end(&s->syncp); | 191 | u64_stats_update_end(&s->syncp); |
159 | 192 | ||
193 | if (dsa_skb_defer_rx_timestamp(p, skb)) | ||
194 | return 0; | ||
195 | |||
160 | netif_receive_skb(skb); | 196 | netif_receive_skb(skb); |
161 | 197 | ||
162 | return 0; | 198 | return 0; |
diff --git a/net/dsa/slave.c b/net/dsa/slave.c index f52307296de4..3376dad6dcfd 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <net/tc_act/tc_mirred.h> | 21 | #include <net/tc_act/tc_mirred.h> |
22 | #include <linux/if_bridge.h> | 22 | #include <linux/if_bridge.h> |
23 | #include <linux/netpoll.h> | 23 | #include <linux/netpoll.h> |
24 | #include <linux/ptp_classify.h> | ||
24 | 25 | ||
25 | #include "dsa_priv.h" | 26 | #include "dsa_priv.h" |
26 | 27 | ||
@@ -255,6 +256,22 @@ dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, | |||
255 | 256 | ||
256 | static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | 257 | static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
257 | { | 258 | { |
259 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
260 | struct dsa_switch *ds = p->dp->ds; | ||
261 | int port = p->dp->index; | ||
262 | |||
263 | /* Pass through to switch driver if it supports timestamping */ | ||
264 | switch (cmd) { | ||
265 | case SIOCGHWTSTAMP: | ||
266 | if (ds->ops->port_hwtstamp_get) | ||
267 | return ds->ops->port_hwtstamp_get(ds, port, ifr); | ||
268 | break; | ||
269 | case SIOCSHWTSTAMP: | ||
270 | if (ds->ops->port_hwtstamp_set) | ||
271 | return ds->ops->port_hwtstamp_set(ds, port, ifr); | ||
272 | break; | ||
273 | } | ||
274 | |||
258 | if (!dev->phydev) | 275 | if (!dev->phydev) |
259 | return -ENODEV; | 276 | return -ENODEV; |
260 | 277 | ||
@@ -385,6 +402,30 @@ static inline netdev_tx_t dsa_slave_netpoll_send_skb(struct net_device *dev, | |||
385 | return NETDEV_TX_OK; | 402 | return NETDEV_TX_OK; |
386 | } | 403 | } |
387 | 404 | ||
405 | static void dsa_skb_tx_timestamp(struct dsa_slave_priv *p, | ||
406 | struct sk_buff *skb) | ||
407 | { | ||
408 | struct dsa_switch *ds = p->dp->ds; | ||
409 | struct sk_buff *clone; | ||
410 | unsigned int type; | ||
411 | |||
412 | type = ptp_classify_raw(skb); | ||
413 | if (type == PTP_CLASS_NONE) | ||
414 | return; | ||
415 | |||
416 | if (!ds->ops->port_txtstamp) | ||
417 | return; | ||
418 | |||
419 | clone = skb_clone_sk(skb); | ||
420 | if (!clone) | ||
421 | return; | ||
422 | |||
423 | if (ds->ops->port_txtstamp(ds, p->dp->index, clone, type)) | ||
424 | return; | ||
425 | |||
426 | kfree_skb(clone); | ||
427 | } | ||
428 | |||
388 | static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) | 429 | static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) |
389 | { | 430 | { |
390 | struct dsa_slave_priv *p = netdev_priv(dev); | 431 | struct dsa_slave_priv *p = netdev_priv(dev); |
@@ -397,6 +438,11 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) | |||
397 | s->tx_bytes += skb->len; | 438 | s->tx_bytes += skb->len; |
398 | u64_stats_update_end(&s->syncp); | 439 | u64_stats_update_end(&s->syncp); |
399 | 440 | ||
441 | /* Identify PTP protocol packets, clone them, and pass them to the | ||
442 | * switch driver | ||
443 | */ | ||
444 | dsa_skb_tx_timestamp(p, skb); | ||
445 | |||
400 | /* Transmit function may have to reallocate the original SKB, | 446 | /* Transmit function may have to reallocate the original SKB, |
401 | * in which case it must have freed it. Only free it here on error. | 447 | * in which case it must have freed it. Only free it here on error. |
402 | */ | 448 | */ |
@@ -918,6 +964,18 @@ static int dsa_slave_set_rxnfc(struct net_device *dev, | |||
918 | return ds->ops->set_rxnfc(ds, dp->index, nfc); | 964 | return ds->ops->set_rxnfc(ds, dp->index, nfc); |
919 | } | 965 | } |
920 | 966 | ||
967 | static int dsa_slave_get_ts_info(struct net_device *dev, | ||
968 | struct ethtool_ts_info *ts) | ||
969 | { | ||
970 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
971 | struct dsa_switch *ds = p->dp->ds; | ||
972 | |||
973 | if (!ds->ops->get_ts_info) | ||
974 | return -EOPNOTSUPP; | ||
975 | |||
976 | return ds->ops->get_ts_info(ds, p->dp->index, ts); | ||
977 | } | ||
978 | |||
921 | static const struct ethtool_ops dsa_slave_ethtool_ops = { | 979 | static const struct ethtool_ops dsa_slave_ethtool_ops = { |
922 | .get_drvinfo = dsa_slave_get_drvinfo, | 980 | .get_drvinfo = dsa_slave_get_drvinfo, |
923 | .get_regs_len = dsa_slave_get_regs_len, | 981 | .get_regs_len = dsa_slave_get_regs_len, |
@@ -938,6 +996,7 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = { | |||
938 | .set_link_ksettings = phy_ethtool_set_link_ksettings, | 996 | .set_link_ksettings = phy_ethtool_set_link_ksettings, |
939 | .get_rxnfc = dsa_slave_get_rxnfc, | 997 | .get_rxnfc = dsa_slave_get_rxnfc, |
940 | .set_rxnfc = dsa_slave_set_rxnfc, | 998 | .set_rxnfc = dsa_slave_set_rxnfc, |
999 | .get_ts_info = dsa_slave_get_ts_info, | ||
941 | }; | 1000 | }; |
942 | 1001 | ||
943 | /* legacy way, bypassing the bridge *****************************************/ | 1002 | /* legacy way, bypassing the bridge *****************************************/ |