/* * AD9361 Agile RF Transceiver * SPDX-FileCopyrightText: Copyright 2013-2015 Analog Devices Inc. * Modified by Xianjun jiao * SPDX-License-Identifier: GPL-2.0-or-later */ //#define DEBUG //#define _DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IIO_AD9361_USE_PRIVATE_H_ #include "ad9361.h" #include "ad9361_private.h" static const struct SynthLUT SynthLUT_FDD[LUT_FTDD_ENT][SYNTH_LUT_SIZE] = { { {12605, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 5, 15}, /* 40 MHz */ {12245, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 5, 15}, {11906, 13, 1, 4, 2, 15, 12, 7, 15, 6, 14, 5, 15}, {11588, 13, 1, 4, 2, 15, 12, 8, 15, 6, 14, 5, 15}, {11288, 13, 1, 4, 2, 15, 12, 8, 15, 6, 14, 5, 15}, {11007, 13, 1, 4, 2, 15, 12, 9, 15, 6, 14, 5, 15}, {10742, 13, 1, 4, 2, 15, 12, 9, 15, 6, 14, 5, 15}, {10492, 13, 1, 6, 2, 15, 12, 10, 15, 6, 14, 5, 15}, {10258, 13, 1, 6, 2, 15, 12, 10, 15, 6, 14, 5, 15}, {10036, 13, 1, 6, 2, 15, 12, 11, 15, 6, 14, 5, 15}, {9827, 13, 1, 6, 2, 14, 12, 11, 15, 6, 14, 5, 15}, {9631, 13, 1, 6, 2, 13, 12, 12, 15, 6, 14, 5, 15}, {9445, 13, 1, 6, 2, 12, 12, 12, 15, 6, 14, 5, 15}, {9269, 13, 1, 6, 2, 12, 12, 13, 15, 6, 14, 5, 15}, {9103, 13, 1, 6, 2, 12, 12, 13, 15, 6, 14, 5, 15}, {8946, 13, 1, 6, 2, 12, 12, 14, 15, 6, 14, 5, 15}, {8797, 12, 1, 7, 2, 12, 12, 13, 15, 6, 14, 5, 15}, {8655, 12, 1, 7, 2, 12, 12, 14, 15, 6, 14, 5, 15}, {8520, 12, 1, 7, 2, 12, 12, 14, 15, 6, 14, 5, 15}, {8392, 12, 1, 7, 2, 12, 12, 15, 15, 6, 14, 5, 15}, {8269, 12, 1, 7, 2, 12, 12, 15, 15, 6, 14, 5, 15}, {8153, 12, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, {8041, 12, 1, 7, 2, 13, 12, 16, 15, 6, 14, 5, 15}, {7934, 11, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, {7831, 11, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, {7733, 10, 1, 7, 3, 13, 12, 16, 15, 6, 14, 5, 15}, {7638, 10, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, {7547, 10, 1, 7, 2, 12, 12, 17, 15, 6, 14, 5, 15}, {7459, 10, 1, 7, 2, 12, 12, 17, 15, 6, 14, 5, 15}, {7374, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15}, {7291, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15}, {7212, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15}, {7135, 10, 2, 7, 3, 14, 13, 15, 15, 7, 14, 5, 15}, {7061, 10, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, {6988, 10, 1, 7, 3, 12, 14, 20, 15, 6, 14, 5, 15}, {6918, 9, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, {6850, 9, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, {6784, 9, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15}, {6720, 9, 2, 7, 2, 13, 13, 16, 15, 6, 14, 5, 15}, {6658, 8, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, {6597, 8, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15}, {6539, 8, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15}, {6482, 8, 2, 7, 2, 13, 13, 16, 15, 6, 14, 5, 15}, {6427, 7, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, {6373, 7, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, {6321, 7, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, {6270, 7, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, {6222, 7, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, {6174, 6, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, {6128, 6, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, {6083, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, {6040, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, {5997, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, }, { {12605, 13, 1, 4, 2, 15, 12, 13, 15, 12, 12, 5, 14}, /* 60 MHz */ {12245, 13, 1, 4, 2, 15, 12, 13, 15, 12, 12, 5, 14}, {11906, 13, 1, 4, 2, 15, 12, 13, 15, 13, 12, 5, 13}, {11588, 13, 1, 4, 2, 15, 12, 14, 15, 13, 12, 5, 13}, {11288, 13, 1, 5, 2, 15, 12, 15, 15, 13, 12, 5, 13}, {11007, 13, 1, 5, 2, 15, 12, 16, 15, 13, 12, 5, 13}, {10742, 13, 1, 5, 2, 15, 12, 16, 15, 12, 12, 5, 14}, {10492, 13, 1, 6, 2, 15, 12, 17, 15, 12, 12, 5, 14}, {10258, 13, 1, 6, 2, 15, 12, 18, 15, 13, 12, 5, 13}, {10036, 13, 1, 6, 2, 15, 12, 19, 15, 13, 12, 5, 13}, {9827, 13, 1, 6, 2, 14, 12, 20, 15, 13, 12, 5, 13}, {9631, 13, 1, 6, 2, 13, 12, 21, 15, 13, 12, 5, 13}, {9445, 13, 1, 6, 2, 12, 12, 22, 15, 13, 12, 5, 13}, {9269, 13, 1, 6, 2, 12, 12, 22, 15, 12, 12, 5, 14}, {9103, 13, 1, 6, 2, 12, 12, 23, 15, 13, 12, 5, 13}, {8946, 13, 1, 6, 2, 12, 12, 24, 15, 13, 12, 5, 13}, {8797, 12, 1, 7, 2, 12, 12, 24, 15, 13, 12, 5, 13}, {8655, 12, 1, 7, 2, 12, 12, 25, 15, 13, 12, 5, 13}, {8520, 12, 1, 7, 2, 12, 12, 25, 15, 13, 12, 5, 13}, {8392, 12, 1, 7, 2, 12, 12, 26, 15, 13, 12, 5, 13}, {8269, 12, 1, 7, 2, 12, 12, 27, 15, 13, 12, 5, 13}, {8153, 12, 1, 7, 2, 12, 12, 28, 15, 13, 12, 5, 13}, {8041, 12, 1, 7, 2, 13, 12, 29, 15, 13, 12, 5, 13}, {7934, 11, 1, 7, 2, 12, 12, 28, 15, 13, 12, 5, 13}, {7831, 11, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13}, {7733, 10, 1, 7, 3, 13, 12, 28, 15, 13, 12, 5, 13}, {7638, 10, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13}, {7547, 10, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13}, {7459, 10, 1, 7, 2, 12, 12, 30, 15, 13, 12, 5, 13}, {7374, 10, 2, 7, 3, 14, 13, 24, 15, 13, 12, 5, 13}, {7291, 10, 2, 7, 3, 14, 13, 25, 15, 13, 12, 5, 13}, {7212, 10, 2, 7, 3, 14, 13, 25, 15, 13, 12, 5, 13}, {7135, 10, 2, 7, 3, 14, 13, 26, 15, 13, 12, 5, 13}, {7061, 10, 2, 7, 3, 14, 13, 26, 15, 13, 12, 5, 13}, {6988, 10, 1, 7, 3, 12, 14, 35, 15, 13, 12, 5, 13}, {6918, 9, 1, 7, 3, 12, 14, 33, 15, 13, 12, 5, 13}, {6850, 9, 1, 7, 3, 12, 14, 34, 15, 13, 12, 5, 13}, {6784, 9, 1, 7, 2, 11, 14, 35, 15, 13, 12, 5, 13}, {6720, 9, 1, 7, 2, 11, 14, 35, 15, 13, 12, 5, 13}, {6658, 8, 2, 7, 3, 15, 13, 26, 15, 13, 12, 5, 13}, {6597, 8, 2, 7, 2, 15, 13, 27, 15, 13, 12, 5, 13}, {6539, 8, 2, 7, 2, 15, 13, 27, 15, 13, 12, 5, 13}, {6482, 8, 2, 7, 2, 15, 13, 28, 15, 13, 12, 5, 13}, {6427, 7, 2, 7, 3, 14, 13, 27, 15, 13, 12, 5, 13}, {6373, 7, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, {6321, 7, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, {6270, 7, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, {6222, 7, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, {6174, 6, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, {6128, 6, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, {6083, 6, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, {6040, 6, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, {5997, 6, 2, 7, 3, 15, 13, 29, 15, 13, 12, 5, 13}, }, { {12605, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14}, /* 80 MHz */ {12245, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14}, {11906, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14}, {11588, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 4, 14}, {11288, 13, 1, 4, 2, 15, 12, 8, 15, 6, 13, 5, 14}, {11007, 13, 1, 4, 2, 15, 12, 8, 14, 6, 13, 5, 14}, {10742, 13, 1, 4, 2, 15, 12, 9, 15, 6, 13, 5, 14}, {10492, 13, 1, 6, 2, 15, 12, 9, 14, 6, 13, 5, 14}, {10258, 13, 1, 6, 2, 15, 12, 10, 15, 6, 13, 5, 14}, {10036, 13, 1, 6, 2, 15, 12, 10, 15, 6, 13, 5, 14}, {9827, 13, 1, 6, 2, 14, 12, 11, 15, 6, 13, 5, 14}, {9631, 13, 1, 6, 2, 13, 12, 11, 15, 6, 13, 5, 14}, {9445, 13, 1, 6, 2, 12, 12, 12, 15, 6, 13, 5, 14}, {9269, 13, 1, 6, 2, 12, 12, 12, 15, 6, 13, 5, 14}, {9103, 13, 1, 6, 2, 12, 12, 13, 15, 6, 13, 5, 14}, {8946, 13, 1, 6, 2, 12, 12, 13, 15, 6, 13, 5, 14}, {8797, 12, 1, 7, 2, 12, 12, 13, 15, 6, 13, 5, 14}, {8655, 12, 1, 7, 2, 12, 12, 14, 15, 6, 13, 5, 14}, {8520, 12, 1, 7, 2, 12, 12, 14, 15, 6, 13, 5, 14}, {8392, 12, 1, 7, 2, 12, 12, 15, 15, 7, 13, 5, 14}, {8269, 12, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14}, {8153, 12, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14}, {8041, 12, 1, 7, 2, 13, 12, 16, 15, 6, 13, 5, 14}, {7934, 11, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14}, {7831, 11, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14}, {7733, 10, 1, 7, 3, 13, 12, 15, 15, 6, 13, 5, 14}, {7638, 10, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14}, {7547, 10, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14}, {7459, 10, 1, 7, 2, 12, 12, 17, 15, 6, 13, 5, 14}, {7374, 10, 2, 7, 3, 14, 13, 13, 15, 6, 13, 5, 14}, {7291, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, {7212, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, {7135, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, {7061, 10, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, {6988, 10, 1, 7, 3, 12, 14, 19, 15, 6, 13, 5, 14}, {6918, 9, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, {6850, 9, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, {6784, 9, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, {6720, 9, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, {6658, 8, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, {6597, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, {6539, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, {6482, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, {6427, 7, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, {6373, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, {6321, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, {6270, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, {6222, 7, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14}, {6174, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, {6128, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, {6083, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, {6040, 6, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14}, {5997, 6, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14}, }}; static struct SynthLUT SynthLUT_TDD[LUT_FTDD_ENT][SYNTH_LUT_SIZE] = { { {12605, 13, 1, 4, 2, 15, 12, 27, 12, 15, 12, 4, 13}, /* 40 MHz */ {12245, 13, 1, 4, 2, 15, 12, 27, 12, 15, 12, 4, 13}, {11906, 13, 1, 4, 2, 15, 12, 26, 11, 15, 12, 4, 13}, {11588, 13, 1, 4, 2, 15, 12, 28, 12, 15, 12, 4, 13}, {11288, 13, 1, 4, 2, 15, 12, 30, 12, 15, 12, 4, 13}, {11007, 13, 1, 4, 2, 15, 12, 32, 12, 15, 12, 4, 13}, {10742, 13, 1, 4, 2, 15, 12, 33, 12, 15, 12, 4, 13}, {10492, 13, 1, 6, 2, 15, 12, 35, 12, 15, 12, 4, 13}, {10258, 13, 1, 6, 2, 15, 12, 37, 12, 15, 12, 4, 13}, {10036, 13, 1, 6, 2, 15, 12, 38, 12, 15, 12, 4, 13}, {9827, 13, 1, 6, 2, 14, 12, 40, 12, 15, 12, 4, 13}, {9631, 13, 1, 6, 2, 13, 12, 42, 12, 15, 12, 4, 13}, {9445, 13, 1, 6, 2, 12, 12, 44, 12, 15, 12, 4, 13}, {9269, 13, 1, 6, 2, 12, 12, 45, 12, 15, 12, 4, 13}, {9103, 13, 1, 6, 2, 12, 12, 47, 12, 15, 12, 4, 13}, {8946, 13, 1, 6, 2, 12, 12, 49, 12, 15, 12, 4, 13}, {8797, 12, 1, 7, 2, 12, 12, 48, 12, 15, 12, 4, 13}, {8655, 12, 1, 7, 2, 12, 12, 50, 12, 15, 12, 4, 13}, {8520, 12, 1, 7, 2, 12, 12, 51, 12, 15, 12, 4, 13}, {8392, 12, 1, 7, 2, 12, 12, 53, 12, 15, 12, 4, 13}, {8269, 12, 1, 7, 2, 12, 12, 55, 12, 15, 12, 4, 13}, {8153, 12, 1, 7, 2, 12, 12, 56, 12, 15, 12, 4, 13}, {8041, 12, 1, 7, 2, 13, 12, 58, 12, 15, 12, 4, 13}, {7934, 11, 1, 7, 2, 12, 12, 57, 12, 15, 12, 4, 13}, {7831, 11, 1, 7, 2, 12, 12, 58, 12, 15, 12, 4, 13}, {7733, 10, 1, 7, 3, 13, 12, 56, 12, 15, 12, 4, 13}, {7638, 10, 1, 7, 2, 12, 12, 58, 12, 15, 12, 4, 13}, {7547, 10, 1, 7, 2, 12, 12, 59, 12, 15, 12, 4, 13}, {7459, 10, 1, 7, 2, 12, 12, 61, 12, 15, 12, 4, 13}, {7374, 10, 2, 7, 3, 14, 13, 49, 12, 15, 12, 4, 13}, {7291, 10, 2, 7, 3, 14, 13, 50, 12, 15, 12, 4, 13}, {7212, 10, 2, 7, 3, 14, 13, 51, 12, 15, 12, 4, 13}, {7135, 10, 2, 7, 3, 14, 13, 52, 12, 15, 12, 4, 13}, {7061, 10, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13}, {6988, 10, 1, 7, 3, 12, 14, 63, 11, 14, 12, 3, 13}, {6918, 9, 2, 7, 3, 14, 13, 52, 12, 15, 12, 4, 13}, {6850, 9, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13}, {6784, 9, 2, 7, 2, 13, 13, 54, 12, 15, 12, 4, 13}, {6720, 9, 2, 7, 2, 13, 13, 56, 12, 15, 12, 4, 13}, {6658, 8, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13}, {6597, 8, 2, 7, 2, 13, 13, 54, 12, 15, 12, 4, 13}, {6539, 8, 2, 7, 2, 13, 13, 55, 12, 15, 12, 4, 13}, {6482, 8, 2, 7, 2, 13, 13, 56, 12, 15, 12, 4, 13}, {6427, 7, 2, 7, 3, 14, 13, 54, 12, 15, 12, 4, 13}, {6373, 7, 2, 7, 3, 15, 13, 54, 12, 15, 12, 4, 13}, {6321, 7, 2, 7, 3, 15, 13, 55, 12, 15, 12, 4, 13}, {6270, 7, 2, 7, 3, 15, 13, 56, 12, 15, 12, 4, 13}, {6222, 7, 2, 7, 3, 15, 13, 57, 12, 15, 12, 4, 13}, {6174, 6, 2, 7, 3, 15, 13, 54, 12, 15, 12, 4, 13}, {6128, 6, 2, 7, 3, 15, 13, 55, 12, 15, 12, 4, 13}, {6083, 6, 2, 7, 3, 15, 13, 56, 12, 15, 12, 4, 13}, {6040, 6, 2, 7, 3, 15, 13, 57, 12, 15, 12, 4, 13}, {5997, 6, 2, 7, 3, 15, 13, 58, 12, 15, 12, 4, 13}, }, { {12605, 13, 1, 4, 2, 15, 12, 26, 11, 15, 11, 4, 13}, /* 60 MHz */ {12245, 13, 1, 4, 2, 15, 12, 26, 11, 15, 11, 4, 13}, {11906, 13, 1, 4, 2, 15, 12, 26, 12, 15, 11, 4, 12}, {11588, 13, 1, 4, 2, 15, 12, 30, 12, 15, 11, 4, 12}, {11288, 13, 1, 4, 2, 15, 12, 32, 12, 15, 10, 4, 12}, {11007, 13, 1, 4, 2, 15, 12, 31, 12, 15, 11, 4, 12}, {10742, 13, 1, 4, 2, 15, 12, 33, 12, 15, 10, 4, 12}, {10492, 13, 1, 6, 2, 15, 12, 37, 12, 15, 10, 4, 12}, {10258, 13, 1, 6, 2, 15, 12, 38, 12, 15, 11, 4, 13}, {10036, 13, 1, 6, 2, 15, 12, 38, 12, 15, 10, 4, 12}, {9827, 13, 1, 6, 2, 14, 12, 42, 12, 15, 11, 4, 12}, {9631, 13, 1, 6, 2, 13, 12, 41, 12, 15, 11, 4, 12}, {9445, 13, 1, 6, 2, 12, 12, 45, 12, 15, 11, 4, 12}, {9269, 13, 1, 6, 2, 12, 12, 47, 12, 15, 11, 4, 12}, {9103, 13, 1, 6, 2, 12, 12, 46, 12, 15, 11, 4, 12}, {8946, 13, 1, 6, 2, 12, 12, 48, 12, 15, 10, 4, 12}, {8797, 12, 1, 7, 2, 12, 12, 49, 12, 15, 11, 4, 13}, {8655, 12, 1, 7, 2, 12, 12, 51, 12, 15, 11, 4, 12}, {8520, 12, 1, 7, 2, 12, 12, 50, 12, 15, 11, 4, 12}, {8392, 12, 1, 7, 2, 12, 12, 52, 12, 15, 10, 4, 12}, {8269, 12, 1, 7, 2, 12, 12, 56, 12, 15, 10, 4, 12}, {8153, 12, 1, 7, 2, 12, 12, 55, 12, 15, 11, 4, 12}, {8041, 12, 1, 7, 2, 13, 12, 57, 12, 15, 10, 4, 12}, {7934, 11, 1, 7, 2, 12, 12, 55, 12, 15, 11, 4, 12}, {7831, 11, 1, 7, 2, 12, 12, 57, 12, 15, 10, 4, 12}, {7733, 10, 1, 7, 3, 13, 12, 55, 12, 15, 11, 4, 12}, {7638, 10, 1, 7, 2, 12, 12, 59, 12, 15, 10, 4, 12}, {7547, 10, 1, 7, 2, 12, 12, 60, 12, 15, 11, 4, 12}, {7459, 10, 1, 7, 2, 12, 12, 48, 12, 15, 11, 4, 12}, {7374, 10, 2, 7, 3, 14, 13, 47, 12, 15, 11, 4, 13}, {7291, 10, 2, 7, 3, 14, 13, 49, 12, 15, 10, 4, 12}, {7212, 10, 2, 7, 3, 14, 13, 50, 12, 15, 10, 4, 12}, {7135, 10, 2, 7, 3, 14, 13, 52, 12, 15, 11, 4, 13}, {7061, 10, 2, 7, 3, 14, 13, 52, 12, 15, 11, 4, 12}, {6988, 10, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, {6918, 9, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, {6850, 9, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, {6784, 9, 1, 7, 2, 11, 14, 63, 11, 15, 11, 4, 13}, {6720, 9, 1, 7, 2, 11, 14, 63, 11, 14, 11, 3, 13}, {6658, 8, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, {6597, 8, 1, 7, 2, 11, 14, 63, 11, 14, 11, 3, 13}, {6539, 8, 1, 7, 2, 11, 14, 63, 10, 14, 11, 3, 13}, {6482, 8, 1, 7, 2, 11, 14, 63, 10, 14, 11, 3, 13}, {6427, 7, 2, 7, 3, 14, 13, 54, 12, 15, 10, 4, 12}, {6373, 7, 2, 7, 3, 15, 13, 53, 12, 15, 11, 4, 12}, {6321, 7, 2, 7, 3, 15, 13, 54, 12, 15, 11, 4, 12}, {6270, 7, 2, 7, 3, 15, 13, 55, 12, 15, 11, 4, 12}, {6222, 7, 2, 7, 3, 15, 13, 56, 12, 15, 11, 4, 12}, {6174, 6, 2, 7, 3, 15, 13, 53, 12, 15, 11, 4, 12}, {6128, 6, 2, 7, 3, 15, 13, 55, 12, 15, 11, 4, 12}, {6083, 6, 2, 7, 3, 15, 13, 55, 12, 15, 10, 4, 12}, {6040, 6, 2, 7, 3, 15, 13, 56, 12, 15, 10, 4, 12}, {5997, 6, 2, 7, 3, 15, 13, 57, 12, 15, 10, 4, 12}, }, { {12605, 13, 1, 4, 2, 15, 12, 21, 12, 15, 11, 4, 13}, /* 80 MHz */ {12245, 13, 1, 4, 2, 15, 12, 21, 12, 15, 11, 4, 13}, {11906, 13, 1, 4, 2, 15, 12, 20, 11, 15, 11, 4, 13}, {11588, 13, 1, 4, 2, 15, 12, 22, 12, 15, 11, 4, 12}, {11288, 13, 1, 5, 2, 15, 12, 23, 12, 15, 11, 4, 13}, {11007, 13, 1, 5, 2, 15, 12, 25, 12, 15, 10, 4, 12}, {10742, 13, 1, 5, 2, 15, 12, 26, 12, 15, 11, 4, 13}, {10492, 13, 1, 6, 2, 15, 12, 27, 11, 15, 11, 4, 13}, {10258, 13, 1, 6, 2, 15, 12, 29, 12, 15, 10, 4, 12}, {10036, 13, 1, 6, 2, 15, 12, 30, 12, 15, 11, 4, 12}, {9827, 13, 1, 6, 2, 14, 12, 31, 12, 15, 11, 4, 13}, {9631, 13, 1, 6, 2, 13, 12, 33, 12, 15, 10, 4, 12}, {9445, 13, 1, 6, 2, 12, 12, 34, 12, 15, 11, 4, 12}, {9269, 13, 1, 6, 2, 12, 12, 35, 12, 15, 11, 4, 13}, {9103, 13, 1, 6, 2, 12, 12, 37, 12, 15, 10, 4, 12}, {8946, 13, 1, 6, 2, 12, 12, 38, 12, 15, 11, 4, 12}, {8797, 12, 1, 7, 2, 12, 12, 37, 12, 15, 11, 4, 13}, {8655, 12, 1, 7, 2, 12, 12, 39, 12, 15, 11, 4, 12}, {8520, 12, 1, 7, 2, 12, 12, 40, 12, 15, 11, 4, 12}, {8392, 12, 1, 7, 2, 12, 12, 41, 12, 15, 11, 4, 13}, {8269, 12, 1, 7, 2, 12, 12, 43, 12, 15, 10, 4, 12}, {8153, 12, 1, 7, 2, 12, 12, 44, 12, 15, 11, 4, 12}, {8041, 12, 1, 7, 2, 13, 12, 45, 12, 15, 11, 4, 12}, {7934, 11, 1, 7, 2, 12, 12, 44, 12, 15, 11, 4, 12}, {7831, 11, 1, 7, 2, 12, 12, 45, 12, 15, 11, 4, 13}, {7733, 10, 1, 7, 3, 13, 12, 44, 12, 15, 11, 4, 12}, {7638, 10, 1, 7, 2, 12, 12, 45, 12, 15, 11, 4, 12}, {7547, 10, 1, 7, 2, 12, 12, 46, 12, 15, 11, 4, 12}, {7459, 10, 1, 7, 2, 12, 12, 47, 12, 15, 11, 4, 13}, {7374, 10, 2, 7, 3, 14, 13, 38, 12, 15, 11, 4, 12}, {7291, 10, 2, 7, 3, 14, 13, 39, 12, 15, 10, 4, 12}, {7212, 10, 2, 7, 3, 14, 13, 40, 12, 15, 10, 4, 12}, {7135, 10, 2, 7, 3, 14, 13, 41, 12, 15, 10, 4, 12}, {7061, 10, 2, 7, 3, 14, 13, 41, 12, 15, 11, 4, 13}, {6988, 10, 1, 7, 3, 12, 14, 54, 12, 15, 11, 4, 12}, {6918, 9, 2, 7, 3, 14, 13, 41, 12, 15, 10, 4, 12}, {6850, 9, 2, 7, 3, 14, 13, 42, 12, 15, 10, 4, 12}, {6784, 9, 2, 7, 2, 13, 13, 42, 12, 15, 11, 4, 13}, {6720, 9, 2, 7, 2, 13, 13, 43, 12, 15, 11, 4, 13}, {6658, 8, 2, 7, 3, 14, 13, 41, 12, 15, 11, 4, 13}, {6597, 8, 2, 7, 2, 13, 13, 42, 12, 15, 11, 4, 12}, {6539, 8, 2, 7, 2, 13, 13, 43, 12, 15, 11, 4, 12}, {6482, 8, 2, 7, 2, 13, 13, 44, 12, 15, 11, 4, 12}, {6427, 7, 2, 7, 3, 14, 13, 42, 12, 15, 10, 4, 12}, {6373, 7, 2, 7, 3, 15, 13, 42, 12, 15, 11, 4, 13}, {6321, 7, 2, 7, 3, 15, 13, 43, 12, 15, 11, 4, 12}, {6270, 7, 2, 7, 3, 15, 13, 44, 12, 15, 11, 4, 12}, {6222, 7, 2, 7, 3, 15, 13, 45, 12, 15, 10, 4, 12}, {6174, 6, 2, 7, 3, 15, 13, 42, 12, 15, 11, 4, 13}, {6128, 6, 2, 7, 3, 15, 13, 43, 12, 15, 11, 4, 12}, {6083, 6, 2, 7, 3, 15, 13, 44, 12, 15, 10, 4, 12}, {6040, 6, 2, 7, 3, 15, 13, 44, 12, 15, 11, 4, 13}, {5997, 6, 2, 7, 3, 15, 13, 45, 12, 15, 11, 4, 12}, }}; /* Rx Gain Tables */ #define SIZE_FULL_TABLE 77 static const u8 full_gain_table[RXGAIN_TBLS_END][SIZE_FULL_TABLE][3] = {{ /* 800 MHz */ {0x00, 0x00, 0x20}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x01, 0x00}, {0x00, 0x02, 0x00}, {0x00, 0x03, 0x00}, {0x00, 0x04, 0x00}, {0x00, 0x05, 0x00}, {0x01, 0x03, 0x20}, {0x01, 0x04, 0x00}, {0x01, 0x05, 0x00}, {0x01, 0x06, 0x00}, {0x01, 0x07, 0x00}, {0x01, 0x08, 0x00}, {0x01, 0x09, 0x00}, {0x01, 0x0A, 0x00}, {0x01, 0x0B, 0x00}, {0x01, 0x0C, 0x00}, {0x01, 0x0D, 0x00}, {0x01, 0x0E, 0x00}, {0x02, 0x09, 0x20}, {0x02, 0x0A, 0x00}, {0x02, 0x0B, 0x00}, {0x02, 0x0C, 0x00}, {0x02, 0x0D, 0x00}, {0x02, 0x0E, 0x00}, {0x02, 0x0F, 0x00}, {0x02, 0x10, 0x00}, {0x02, 0x2B, 0x20}, {0x02, 0x2C, 0x00}, {0x04, 0x28, 0x20}, {0x04, 0x29, 0x00}, {0x04, 0x2A, 0x00}, {0x04, 0x2B, 0x00}, {0x24, 0x20, 0x20}, {0x24, 0x21, 0x00}, {0x44, 0x20, 0x20}, {0x44, 0x21, 0x00}, {0x44, 0x22, 0x00}, {0x44, 0x23, 0x00}, {0x44, 0x24, 0x00}, {0x44, 0x25, 0x00}, {0x44, 0x26, 0x00}, {0x44, 0x27, 0x00}, {0x44, 0x28, 0x00}, {0x44, 0x29, 0x00}, {0x44, 0x2A, 0x00}, {0x44, 0x2B, 0x00}, {0x44, 0x2C, 0x00}, {0x44, 0x2D, 0x00}, {0x44, 0x2E, 0x00}, {0x44, 0x2F, 0x00}, {0x44, 0x30, 0x00}, {0x44, 0x31, 0x00}, {0x44, 0x32, 0x00}, {0x64, 0x2E, 0x20}, {0x64, 0x2F, 0x00}, {0x64, 0x30, 0x00}, {0x64, 0x31, 0x00}, {0x64, 0x32, 0x00}, {0x64, 0x33, 0x00}, {0x64, 0x34, 0x00}, {0x64, 0x35, 0x00}, {0x64, 0x36, 0x00}, {0x64, 0x37, 0x00}, {0x64, 0x38, 0x00}, {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20} },{ /* 2300 MHz */ {0x00, 0x00, 0x20}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x01, 0x00}, {0x00, 0x02, 0x00}, {0x00, 0x03, 0x00}, {0x00, 0x04, 0x00}, {0x00, 0x05, 0x00}, {0x01, 0x03, 0x20}, {0x01, 0x04, 0x00}, {0x01, 0x05, 0x00}, {0x01, 0x06, 0x00}, {0x01, 0x07, 0x00}, {0x01, 0x08, 0x00}, {0x01, 0x09, 0x00}, {0x01, 0x0A, 0x00}, {0x01, 0x0B, 0x00}, {0x01, 0x0C, 0x00}, {0x01, 0x0D, 0x00}, {0x01, 0x0E, 0x00}, {0x02, 0x09, 0x20}, {0x02, 0x0A, 0x00}, {0x02, 0x0B, 0x00}, {0x02, 0x0C, 0x00}, {0x02, 0x0D, 0x00}, {0x02, 0x0E, 0x00}, {0x02, 0x0F, 0x00}, {0x02, 0x10, 0x00}, {0x02, 0x2B, 0x20}, {0x02, 0x2C, 0x00}, {0x04, 0x27, 0x20}, {0x04, 0x28, 0x00}, {0x04, 0x29, 0x00}, {0x04, 0x2A, 0x00}, {0x04, 0x2B, 0x00}, {0x24, 0x21, 0x20}, {0x24, 0x22, 0x00}, {0x44, 0x20, 0x20}, {0x44, 0x21, 0x00}, {0x44, 0x22, 0x00}, {0x44, 0x23, 0x00}, {0x44, 0x24, 0x00}, {0x44, 0x25, 0x00}, {0x44, 0x26, 0x00}, {0x44, 0x27, 0x00}, {0x44, 0x28, 0x00}, {0x44, 0x29, 0x00}, {0x44, 0x2A, 0x00}, {0x44, 0x2B, 0x00}, {0x44, 0x2C, 0x00}, {0x44, 0x2D, 0x00}, {0x44, 0x2E, 0x00}, {0x44, 0x2F, 0x00}, {0x44, 0x30, 0x00}, {0x44, 0x31, 0x00}, {0x64, 0x2E, 0x20}, {0x64, 0x2F, 0x00}, {0x64, 0x30, 0x00}, {0x64, 0x31, 0x00}, {0x64, 0x32, 0x00}, {0x64, 0x33, 0x00}, {0x64, 0x34, 0x00}, {0x64, 0x35, 0x00}, {0x64, 0x36, 0x00}, {0x64, 0x37, 0x00}, {0x64, 0x38, 0x00}, {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20}, },{ /* 5500 MHz */ {0x00, 0x00, 0x20}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x01, 0x00}, {0x00, 0x02, 0x00}, {0x00, 0x03, 0x00}, {0x01, 0x01, 0x20}, {0x01, 0x02, 0x00}, {0x01, 0x03, 0x00}, {0x01, 0x04, 0x20}, {0x01, 0x05, 0x00}, {0x01, 0x06, 0x00}, {0x01, 0x07, 0x00}, {0x01, 0x08, 0x00}, {0x01, 0x09, 0x00}, {0x01, 0x0A, 0x00}, {0x01, 0x0B, 0x00}, {0x01, 0x0C, 0x00}, {0x02, 0x08, 0x20}, {0x02, 0x09, 0x00}, {0x02, 0x0A, 0x00}, {0x02, 0x0B, 0x20}, {0x02, 0x0C, 0x00}, {0x02, 0x0D, 0x00}, {0x02, 0x0E, 0x00}, {0x02, 0x0F, 0x00}, {0x02, 0x2A, 0x20}, {0x02, 0x2B, 0x00}, {0x04, 0x27, 0x20}, {0x04, 0x28, 0x00}, {0x04, 0x29, 0x00}, {0x04, 0x2A, 0x00}, {0x04, 0x2B, 0x00}, {0x04, 0x2C, 0x00}, {0x04, 0x2D, 0x00}, {0x24, 0x20, 0x20}, {0x24, 0x21, 0x00}, {0x24, 0x22, 0x00}, {0x44, 0x20, 0x20}, {0x44, 0x21, 0x00}, {0x44, 0x22, 0x00}, {0x44, 0x23, 0x00}, {0x44, 0x24, 0x00}, {0x44, 0x25, 0x00}, {0x44, 0x26, 0x00}, {0x44, 0x27, 0x00}, {0x44, 0x28, 0x00}, {0x44, 0x29, 0x00}, {0x44, 0x2A, 0x00}, {0x44, 0x2B, 0x00}, {0x44, 0x2C, 0x00}, {0x44, 0x2D, 0x00}, {0x44, 0x2E, 0x00}, {0x64, 0x2E, 0x20}, {0x64, 0x2F, 0x00}, {0x64, 0x30, 0x00}, {0x64, 0x31, 0x00}, {0x64, 0x32, 0x00}, {0x64, 0x33, 0x00}, {0x64, 0x34, 0x00}, {0x64, 0x35, 0x00}, {0x64, 0x36, 0x00}, {0x64, 0x37, 0x00}, {0x64, 0x38, 0x00}, {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20} }}; static const s8 full_gain_table_abs_gain[RXGAIN_TBLS_END][SIZE_FULL_TABLE] = {{ /* 800 MHz */ -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73 }, { /* 2300 MHz */ -3, -3, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71 }, { /* 5500 MHz */ -10, -10, -10, -10, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62 }}; #define SIZE_SPLIT_TABLE 41 static const u8 split_gain_table[RXGAIN_TBLS_END][SIZE_SPLIT_TABLE][3] = {{ /* 800 MHz */ {0x00, 0x18, 0x20}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x20}, {0x01, 0x18, 0x20}, {0x02, 0x18, 0x20}, {0x04, 0x18, 0x20}, {0x04, 0x38, 0x20}, {0x05, 0x38, 0x20}, {0x06, 0x38, 0x20}, {0x07, 0x38, 0x20}, {0x08, 0x38, 0x20}, {0x09, 0x38, 0x20}, {0x0A, 0x38, 0x20}, {0x0B, 0x38, 0x20}, {0x0C, 0x38, 0x20}, {0x0D, 0x38, 0x20}, {0x0E, 0x38, 0x20}, {0x0F, 0x38, 0x20}, {0x24, 0x38, 0x20}, {0x25, 0x38, 0x20}, {0x44, 0x38, 0x20}, {0x45, 0x38, 0x20}, {0x46, 0x38, 0x20}, {0x47, 0x38, 0x20}, {0x48, 0x38, 0x20}, {0x64, 0x38, 0x20}, {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20}, },{ /* 2300 MHz */ {0x00, 0x18, 0x20}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x20}, {0x01, 0x18, 0x20}, {0x02, 0x18, 0x20}, {0x04, 0x18, 0x20}, {0x04, 0x38, 0x20}, {0x05, 0x38, 0x20}, {0x06, 0x38, 0x20}, {0x07, 0x38, 0x20}, {0x08, 0x38, 0x20}, {0x09, 0x38, 0x20}, {0x0A, 0x38, 0x20}, {0x0B, 0x38, 0x20}, {0x0C, 0x38, 0x20}, {0x0D, 0x38, 0x20}, {0x0E, 0x38, 0x20}, {0x0F, 0x38, 0x20}, {0x25, 0x38, 0x20}, {0x26, 0x38, 0x20}, {0x44, 0x38, 0x20}, {0x45, 0x38, 0x20}, {0x46, 0x38, 0x20}, {0x47, 0x38, 0x20}, {0x64, 0x38, 0x20}, {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20}, },{ /* 5500 MHz */ {0x00, 0x18, 0x20}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x01, 0x18, 0x20}, {0x02, 0x18, 0x20}, {0x04, 0x18, 0x20}, {0x04, 0x38, 0x20}, {0x05, 0x38, 0x20}, {0x06, 0x38, 0x20}, {0x07, 0x38, 0x20}, {0x08, 0x38, 0x20}, {0x09, 0x38, 0x20}, {0x0A, 0x38, 0x20}, {0x0B, 0x38, 0x20}, {0x0C, 0x38, 0x20}, {0x0D, 0x38, 0x20}, {0x0E, 0x38, 0x20}, {0x0F, 0x38, 0x20}, {0x62, 0x38, 0x20}, {0x25, 0x38, 0x20}, {0x26, 0x38, 0x20}, {0x44, 0x38, 0x20}, {0x64, 0x38, 0x20}, {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20}, }}; static const u8 split_gain_table_abs_gain[RXGAIN_TBLS_END][SIZE_SPLIT_TABLE] = {{ /* 800 MHz */ -1, -1, -1, -1, -1, -1, -1, 2, 8, 13, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49 }, { /* 2300 MHz */ -3, -3, -3, -3, -3, -3, -3, -3, 0, 6, 12, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 }, { /* 5500 MHz */ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -7, -2, 3, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38 }}; struct gain_table_info { u64 start; u64 end; u8 max_index; u8 split_table; s8 *abs_gain_tbl; u8 (*tab)[3]; }; static struct gain_table_info ad9361_adi_gt_info[] = { { .start = 0, .end = 1300000000ULL, .max_index = SIZE_FULL_TABLE, .abs_gain_tbl = (s8 *) &full_gain_table_abs_gain[TBL_200_1300_MHZ], .tab = (u8 (*)[3]) full_gain_table[TBL_200_1300_MHZ], },{ .start = 1300000000ULL, .end = 4000000000ULL, .max_index = SIZE_FULL_TABLE, .abs_gain_tbl = (s8 *) &full_gain_table_abs_gain[TBL_1300_4000_MHZ], .tab = (u8 (*)[3]) full_gain_table[TBL_1300_4000_MHZ], },{ .start = 4000000000ULL, .end = 6000000000ULL, .max_index = SIZE_FULL_TABLE, .abs_gain_tbl = (s8 *) &full_gain_table_abs_gain[TBL_4000_6000_MHZ], .tab = (u8 (*)[3]) full_gain_table[TBL_4000_6000_MHZ], },{ .start = 0, .end = 1300000000ULL, .max_index = SIZE_SPLIT_TABLE, .split_table = 1, .abs_gain_tbl = (s8 *) &split_gain_table_abs_gain[TBL_200_1300_MHZ], .tab = (u8 (*)[3]) split_gain_table[TBL_200_1300_MHZ], },{ .start = 1300000000ULL, .end = 4000000000ULL, .max_index = SIZE_SPLIT_TABLE, .split_table = 1, .abs_gain_tbl = (s8 *) &split_gain_table_abs_gain[TBL_1300_4000_MHZ], .tab = (u8 (*)[3]) split_gain_table[TBL_1300_4000_MHZ], },{ .start = 4000000000ULL, .end = 6000000000ULL, .max_index = SIZE_SPLIT_TABLE, .split_table = 1, .abs_gain_tbl = (s8 *) &split_gain_table_abs_gain[TBL_4000_6000_MHZ], .tab = (u8 (*)[3]) split_gain_table[TBL_4000_6000_MHZ], },{ }, /* Don't Remove */ }; /* Mixer GM Sub-table */ static const u8 gm_st_gain[16]= {0x78, 0x74, 0x70, 0x6C, 0x68, 0x64, 0x60, 0x5C, 0x58, 0x54, 0x50, 0x4C, 0x48, 0x30, 0x18, 0x0}; static const u8 gm_st_ctrl[16]= {0x0, 0xD, 0x15, 0x1B, 0x21, 0x25, 0x29, 0x2C, 0x2F, 0x31, 0x33, 0x34, 0x35, 0x3A, 0x3D, 0x3E}; static const s8 lna_table[RXGAIN_TBLS_END][4] = { {5, 17, 19, 24}, {3, 14, 17, 21}, {-4, 10, 13, 14}}; static const s8 tia_table[] = {-6, 0}; static const s8 mixer_table[RXGAIN_TBLS_END][16] = { {0, 3, 9, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}, {0, 3, 9, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}, {0, 3, 8, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}}; static const u32 gain_step_calib_reg_val[4][5] = { {0xC0, 0x2E, 0x10, 0x06, 0x00}, // LO Frequency Range: 600 to 1300 MHz {0xC0, 0x2C, 0x10, 0x06, 0x00}, // LO Frequency Range: 1300 to 3300 MHz {0xB8, 0x2C, 0x10, 0x06, 0x00}, // LO Frequency Range: 2700 to 4100 MHz {0xA0, 0x24, 0x10, 0x06, 0x00}, // LO Frequency Range: 4000 to 6000 MHz }; #ifdef _DEBUG struct ad9361_trace { s64 time; unsigned reg; unsigned read; }; static struct ad9361_trace timestamps[5000]; static int timestamp_cnt = 0; static bool timestamp_en = 0; static inline void ad9361_timestamp_en(void) { timestamp_en = true; } static inline void ad9361_timestamp_dis(void) { timestamp_en = false; } static inline void ad9361_add_timestamp(struct iio_dev *indio_dev, unsigned reg, unsigned read) { if (timestamp_en && (timestamp_cnt < 5000)) { timestamps[timestamp_cnt].time = iio_get_time_ns(indio_dev); timestamps[timestamp_cnt].reg = reg; timestamps[timestamp_cnt].read = read; timestamp_cnt++; } } static inline void ad9361_print_timestamp(void) { int i; pr_debug("\n--- TRACE START / Points (%d) --- \n", timestamp_cnt); for (i = 0; i < timestamp_cnt; i++) { if (i == 0) pr_debug("[%lld] [%lld] \t%s\t 0x%X\n", timestamps[i].time, 0LL, timestamps[i].read ? "REG_RD" : "REG_WR", timestamps[i].reg); else pr_debug("[%lld] [%12lld] \t%s\t 0x%X\n", timestamps[i].time, timestamps[i].time - timestamps[i - 1].time, timestamps[i].read ? "REG_RD" : "REG_WR", timestamps[i].reg); } pr_debug("\n--- TRACE END / Time %lld ns --- \n", timestamps[timestamp_cnt - 1].time - timestamps[0].time); timestamp_cnt = 0; } #endif static const char *ad9361_ensm_states[] = { "sleep", NULL, NULL, NULL, NULL, "alert", "tx", "tx flush", "rx", "rx_flush", "fdd", "fdd_flush" }; static int ad9361_spi_readm(struct spi_device *spi, u32 reg, u8 *rbuf, u32 num) { u8 buf[2]; int ret; u16 cmd; if (num > MAX_MBYTE_SPI) return -EINVAL; cmd = AD_READ | AD_CNT(num) | AD_ADDR(reg); buf[0] = cmd >> 8; buf[1] = cmd & 0xFF; ret = spi_write_then_read(spi, &buf[0], 2, rbuf, num); if (ret < 0) { dev_err(&spi->dev, "Read Error %d", ret); return ret; } #ifdef _DEBUG { int i; for (i = 0; i < num; i++) dev_dbg(&spi->dev, "%s: reg 0x%X val 0x%X\n", __func__, reg--, rbuf[i]); } #endif return 0; } int ad9361_spi_read(struct spi_device *spi, u32 reg) { u8 buf; int ret; ret = ad9361_spi_readm(spi, reg, &buf, 1); if (ret < 0) return ret; return buf; } EXPORT_SYMBOL(ad9361_spi_read); static int __ad9361_spi_readf(struct spi_device *spi, u32 reg, u32 mask, u32 offset) { u8 buf; int ret; if (!mask) return -EINVAL; ret = ad9361_spi_readm(spi, reg, &buf, 1); if (ret < 0) return ret; buf &= mask; buf >>= offset; return buf; } #define ad9361_spi_readf(spi, reg, mask) \ __ad9361_spi_readf(spi, reg, mask, __ffs(mask)) int ad9361_spi_write(struct spi_device *spi, u32 reg, u32 val) { u8 buf[3]; int ret; u16 cmd; cmd = AD_WRITE | AD_CNT(1) | AD_ADDR(reg); buf[0] = cmd >> 8; buf[1] = cmd & 0xFF; buf[2] = val; ret = spi_write_then_read(spi, buf, 3, NULL, 0); if (ret < 0) { dev_err(&spi->dev, "Write Error %d", ret); return ret; } #ifdef _DEBUG dev_dbg(&spi->dev, "%s: reg 0x%X val 0x%X\n", __func__, reg, buf[2]); #endif return 0; } EXPORT_SYMBOL(ad9361_spi_write); static int __ad9361_spi_writef(struct spi_device *spi, u32 reg, u32 mask, u32 offset, u32 val) { u8 buf; int ret; if (!mask) return -EINVAL; ret = ad9361_spi_readm(spi, reg, &buf, 1); if (ret < 0) return ret; buf &= ~mask; buf |= ((val << offset) & mask); return ad9361_spi_write(spi, reg, buf); } #define ad9361_spi_writef(spi, reg, mask, val) \ __ad9361_spi_writef(spi,reg, mask, __ffs(mask), val) static int ad9361_spi_writem(struct spi_device *spi, u32 reg, u8 *tbuf, u32 num) { u8 buf[10]; int ret; u16 cmd; if (num > MAX_MBYTE_SPI) return -EINVAL; cmd = AD_WRITE | AD_CNT(num) | AD_ADDR(reg); buf[0] = cmd >> 8; buf[1] = cmd & 0xFF; memcpy(&buf[2], tbuf, num); ret = spi_write_then_read(spi, buf, num + 2, NULL, 0); if (ret < 0) { dev_err(&spi->dev, "Write Error %d", ret); return ret; } #ifdef _DEBUG { int i; for (i = 0; i < num; i++) pr_err("%s: reg 0x%X val 0x%X\n", __func__, reg--, tbuf[i]); } #endif return 0; } u32 ad9361_validate_rf_bw(struct ad9361_rf_phy *phy, u32 bw) { switch(spi_get_device_id(phy->spi)->driver_data) { case ID_AD9363A: return clamp_t(u32, bw, 200000UL, 20000000UL); default: return clamp_t(u32, bw, 200000UL, 56000000UL); } } int ad9361_validate_rfpll(struct ad9361_rf_phy *phy, bool is_tx, u64 freq) { switch(spi_get_device_id(phy->spi)->driver_data) { case ID_AD9363A: if (freq > AD9363A_MAX_CARRIER_FREQ_HZ || freq < AD9363A_MIN_CARRIER_FREQ_HZ) return -EINVAL; break; default: if (freq > MAX_CARRIER_FREQ_HZ || freq < (is_tx ? MIN_TX_CARRIER_FREQ_HZ : MIN_RX_CARRIER_FREQ_HZ)) return -EINVAL; } return 0; } int ad9361_find_opt(u8 *field, u32 size, u32 *ret_start) { int i, cnt = 0, max_cnt = 0, start, max_start = 0; for(i = 0, start = -1; i < size; i++) { if (field[i] == 0) { if (start == -1) start = i; cnt++; } else { if (cnt > max_cnt) { max_cnt = cnt; max_start = start; } start = -1; cnt = 0; } } if (cnt > max_cnt) { max_cnt = cnt; max_start = start; } *ret_start = max_start; return max_cnt; } EXPORT_SYMBOL(ad9361_find_opt); static int ad9361_1rx1tx_channel_map(struct ad9361_rf_phy *phy, bool tx, int channel) { u32 map; if (phy->pdata->rx2tx2) return channel; if (tx) map = phy->pdata->rx1tx1_mode_use_tx_num; else map = phy->pdata->rx1tx1_mode_use_rx_num; if (map == 2) return channel + 1; return channel; } static int ad9361_reset(struct ad9361_rf_phy *phy) { if (phy->pdata->reset_gpio) { gpiod_set_value_cansleep(phy->pdata->reset_gpio, 0); mdelay(1); gpiod_set_value_cansleep(phy->pdata->reset_gpio, 1); mdelay(1); dev_dbg(&phy->spi->dev, "%s: by GPIO", __func__); return 0; } /* SPI Soft Reset was removed from the register map, since it doesn't * work reliably. Without a prober HW reset randomness may happen. * Please specify a RESET GPIO. */ ad9361_spi_write(phy->spi, REG_SPI_CONF, SOFT_RESET | _SOFT_RESET); ad9361_spi_write(phy->spi, REG_SPI_CONF, 0x0); dev_err(&phy->spi->dev, "%s: by SPI, this may cause unpredicted behavior!", __func__); return -ENODEV; } static int ad9361_en_dis_tx(struct ad9361_rf_phy *phy, u32 tx_if, u32 enable) { if ((tx_if & enable) > 1 && spi_get_device_id(phy->spi)->driver_data == ID_AD9364 && enable) return -EINVAL; return ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, TX_CHANNEL_ENABLE(tx_if), enable); } static int ad9361_en_dis_rx(struct ad9361_rf_phy *phy, u32 rx_if, u32 enable) { if ((rx_if & enable) > 1 && spi_get_device_id(phy->spi)->driver_data == ID_AD9364 && enable) return -EINVAL; return ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, RX_CHANNEL_ENABLE(rx_if), enable); } static int ad9361_int_loopback_fix_ch_cross(struct ad9361_rf_phy *phy, bool enable) { /* Loopback works only TX1->RX1 or RX2->RX2 */ if (!phy->pdata->rx2tx2 && phy->pdata->rx1tx1_mode_use_rx_num != phy->pdata->rx1tx1_mode_use_tx_num) return ad9361_en_dis_tx(phy, TX_1 | TX_2, enable ? phy->pdata->rx1tx1_mode_use_rx_num : phy->pdata->rx1tx1_mode_use_tx_num); return 0; } int ad9361_bist_loopback(struct ad9361_rf_phy *phy, unsigned mode) { struct ad9361_rf_phy_state *st = phy->state; u32 sp_hd, reg; dev_dbg(&phy->spi->dev, "%s: mode %d", __func__, mode); reg = ad9361_spi_read(phy->spi, REG_OBSERVE_CONFIG); st->bist_loopback_mode = mode; switch (mode) { case 0: ad9361_hdl_loopback(phy, false); ad9361_int_loopback_fix_ch_cross(phy, false); reg &= ~(DATA_PORT_SP_HD_LOOP_TEST_OE | DATA_PORT_LOOP_TEST_ENABLE); return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg); case 1: /* loopback (AD9361 internal) TX->RX */ ad9361_hdl_loopback(phy, false); ad9361_int_loopback_fix_ch_cross(phy, true); sp_hd = ad9361_spi_read(phy->spi, REG_PARALLEL_PORT_CONF_3); if ((sp_hd & SINGLE_PORT_MODE) && (sp_hd & HALF_DUPLEX_MODE)) reg |= DATA_PORT_SP_HD_LOOP_TEST_OE; else reg &= ~DATA_PORT_SP_HD_LOOP_TEST_OE; reg |= DATA_PORT_LOOP_TEST_ENABLE; return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg); case 2: /* loopback (FPGA internal) RX->TX */ ad9361_hdl_loopback(phy, true); ad9361_int_loopback_fix_ch_cross(phy, false); reg &= ~(DATA_PORT_SP_HD_LOOP_TEST_OE | DATA_PORT_LOOP_TEST_ENABLE); return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg); default: return -EINVAL; } } EXPORT_SYMBOL(ad9361_bist_loopback); int ad9361_write_bist_reg(struct ad9361_rf_phy *phy, u32 val) { if (!phy || !phy->state) return -EINVAL; phy->state->bist_config = val; return ad9361_spi_write(phy->spi, REG_BIST_CONFIG, val); } EXPORT_SYMBOL(ad9361_write_bist_reg); int ad9361_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode mode) { u32 reg = 0; dev_dbg(&phy->spi->dev, "%s: mode %d", __func__, mode); switch (mode) { case BIST_DISABLE: reg = 0; break; case BIST_INJ_TX: reg = BIST_CTRL_POINT(0) | BIST_ENABLE; break; case BIST_INJ_RX: reg = BIST_CTRL_POINT(2) | BIST_ENABLE; break; }; return ad9361_write_bist_reg(phy, reg); } EXPORT_SYMBOL(ad9361_bist_prbs); static int ad9361_bist_tone(struct ad9361_rf_phy *phy, enum ad9361_bist_mode mode, u32 freq_Hz, u32 level_dB, u32 mask) { unsigned long clk = 0; u32 reg = 0, reg1, reg_mask; dev_dbg(&phy->spi->dev, "%s: mode %d", __func__, mode); switch (mode) { case BIST_DISABLE: reg = 0; break; case BIST_INJ_TX: clk = clk_get_rate(phy->clks[TX_SAMPL_CLK]); reg = BIST_CTRL_POINT(0) | BIST_ENABLE; break; case BIST_INJ_RX: clk = clk_get_rate(phy->clks[RX_SAMPL_CLK]); reg = BIST_CTRL_POINT(2) | BIST_ENABLE; break; }; reg |= TONE_PRBS; reg |= TONE_LEVEL(level_dB / 6); if (freq_Hz < 4) { reg |= TONE_FREQ(freq_Hz); } else { if (clk) reg |= TONE_FREQ(DIV_ROUND_CLOSEST(freq_Hz * 32, clk) - 1); } reg_mask = BIST_MASK_CHANNEL_1_I_DATA | BIST_MASK_CHANNEL_1_Q_DATA | BIST_MASK_CHANNEL_2_I_DATA | BIST_MASK_CHANNEL_2_Q_DATA; reg1 = ((mask << 2) & reg_mask); ad9361_spi_write(phy->spi, REG_BIST_AND_DATA_PORT_TEST_CONFIG, reg1); return ad9361_write_bist_reg(phy, reg); } static int ad9361_check_cal_done(struct ad9361_rf_phy *phy, u32 reg, u32 mask, u32 done_state) { u32 timeout = 5000; /* RFDC_CAL can take long */ u32 state; do { state = ad9361_spi_readf(phy->spi, reg, mask); if (state == done_state) return 0; if (reg == REG_CALIBRATION_CTRL) usleep_range(800, 1200); else usleep_range(80, 120); } while (timeout--); dev_err(&phy->spi->dev, "Calibration TIMEOUT (0x%X, 0x%X)", reg, mask); return -ETIMEDOUT; } static int ad9361_run_calibration(struct ad9361_rf_phy *phy, u32 mask) { int ret = ad9361_spi_write(phy->spi, REG_CALIBRATION_CTRL, mask); if (ret < 0) return ret; dev_dbg(&phy->spi->dev, "%s: CAL Mask 0x%X", __func__, mask); return ad9361_check_cal_done(phy, REG_CALIBRATION_CTRL, mask, 0); } static int ad9361_gt_tableindex(struct ad9361_rf_phy *phy, u64 freq) { int i; for (i = 0; phy->gt_info[i].tab != NULL; i++) { if ((phy->pdata->split_gt == phy->gt_info[i].split_table) && (phy->gt_info[i].start < freq) && (freq <= phy->gt_info[i].end)) { return i; } } dev_err(&phy->spi->dev, "%s: Failed to find suitable gain table (%llu)", __func__, freq); return 0; } static int ad9361_gt(struct ad9361_rf_phy *phy) { struct ad9361_rf_phy_state *st = phy->state; if (st->current_table == -1) { dev_err(&phy->spi->dev, "%s: ERROR", __func__); return 0; } return st->current_table; } /* PLL operates between 47 .. 6000 MHz which is > 2^32 */ static unsigned long ad9361_to_clk(u64 freq) { return (unsigned long) min(freq >> 1, (u64) ULONG_MAX); } static u64 ad9361_from_clk(unsigned long freq) { return ((u64)freq << 1); } static int ad9361_setup_ext_lna(struct ad9361_rf_phy *phy, struct elna_control *ctrl) { ad9361_spi_writef(phy->spi, REG_EXTERNAL_LNA_CTRL, EXTERNAL_LNA1_CTRL, ctrl->elna_1_control_en); ad9361_spi_writef(phy->spi, REG_EXTERNAL_LNA_CTRL, EXTERNAL_LNA2_CTRL, ctrl->elna_2_control_en); ad9361_spi_write(phy->spi, REG_EXT_LNA_HIGH_GAIN, EXT_LNA_HIGH_GAIN(ctrl->gain_mdB / 500)); return ad9361_spi_write(phy->spi, REG_EXT_LNA_LOW_GAIN, EXT_LNA_LOW_GAIN(ctrl->bypass_loss_mdB / 500)); } static int ad9361_clkout_control(struct ad9361_rf_phy *phy, enum ad9361_clkout mode) { if (mode == CLKOUT_DISABLE) return ad9361_spi_writef(phy->spi, REG_BBPLL, CLKOUT_ENABLE, 0); return ad9361_spi_writef(phy->spi, REG_BBPLL, CLKOUT_ENABLE | CLKOUT_SELECT(~0), ((mode - 1) << 1) | 0x1); } static int ad9361_load_mixer_gm_subtable(struct ad9361_rf_phy *phy) { int i, addr; dev_dbg(&phy->spi->dev, "%s", __func__); ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, START_GM_SUB_TABLE_CLOCK); /* Start Clock */ for (i = 0, addr = ARRAY_SIZE(gm_st_ctrl); i < ARRAY_SIZE(gm_st_ctrl); i++) { ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_ADDRESS, --addr); /* Gain Table Index */ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_BIAS_WRITE, 0); /* Bias */ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_WRITE, gm_st_gain[i]); /* Gain */ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CTRL_WRITE, gm_st_ctrl[i]); /* Control */ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, WRITE_GM_SUB_TABLE | START_GM_SUB_TABLE_CLOCK); /* Write Words */ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ } ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, START_GM_SUB_TABLE_CLOCK); /* Clear Write */ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, 0); /* Stop Clock */ return 0; } int ad9361_set_tx_atten(struct ad9361_rf_phy *phy, u32 atten_mdb, bool tx1, bool tx2, bool immed) { u8 buf[2]; int ret = 0; dev_dbg(&phy->spi->dev, "%s : attenuation %u mdB tx1=%d tx2=%d", __func__, atten_mdb, tx1, tx2); if (atten_mdb > MAX_TX_ATTENUATION_DB) /* 89.75 dB */ return -EINVAL; atten_mdb /= 250; /* Scale to 0.25dB / LSB */ buf[0] = atten_mdb >> 8; buf[1] = atten_mdb & 0xFF; ad9361_spi_writef(phy->spi, REG_TX2_DIG_ATTEN, IMMEDIATELY_UPDATE_TPC_ATTEN, 0); if (tx1) ret = ad9361_spi_writem(phy->spi, REG_TX1_ATTEN_1, buf, 2); if (tx2) ret = ad9361_spi_writem(phy->spi, REG_TX2_ATTEN_1, buf, 2); if (immed) ad9361_spi_writef(phy->spi, REG_TX2_DIG_ATTEN, IMMEDIATELY_UPDATE_TPC_ATTEN, 1); return ret; } EXPORT_SYMBOL(ad9361_set_tx_atten); int ad9361_get_tx_atten(struct ad9361_rf_phy *phy, u32 tx_num) { u8 buf[2]; int ret = 0; u32 code; ret = ad9361_spi_readm(phy->spi, (tx_num == 1) ? REG_TX1_ATTEN_1 : REG_TX2_ATTEN_1, buf, 2); if (ret < 0) return ret; code = (buf[0] << 8) | buf[1]; code *= 250; return code; } EXPORT_SYMBOL(ad9361_get_tx_atten); int ad9361_tx_mute(struct ad9361_rf_phy *phy, u32 state) { struct ad9361_rf_phy_state *st = phy->state; int ret; if (state) { st->tx1_atten_cached = ad9361_get_tx_atten(phy, 1); st->tx2_atten_cached = ad9361_get_tx_atten(phy, 2); return ad9361_set_tx_atten(phy, 89750, true, true, true); } else { if (st->tx1_atten_cached == st->tx2_atten_cached) return ad9361_set_tx_atten(phy, st->tx1_atten_cached, true, true, true); ret = ad9361_set_tx_atten(phy, st->tx1_atten_cached, true, false, true); ret |= ad9361_set_tx_atten(phy, st->tx2_atten_cached, false, true, true); return ret; } } EXPORT_SYMBOL(ad9361_tx_mute); static int ad9361_trx_ext_lo_control(struct ad9361_rf_phy *phy, bool tx, bool enable) { struct ad9361_rf_phy_state *st = phy->state; u32 val = enable ? ~0 : 0; int ret; /* REVIST: * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict */ bool mcs_rf_enable = ad9361_spi_readf(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, MCS_RF_ENABLE); dev_dbg(&phy->spi->dev, "%s : %s state %d", __func__, tx ? "TX" : "RX", enable); if (tx) { ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, POWER_DOWN_TX_SYNTH, mcs_rf_enable ? 0 : enable); ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, TX_SYNTH_READY_MASK, enable); ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS, TX_VCO_DIVIDER(~0), enable ? 7 : st->cached_tx_rfpll_div); if (enable) st->cached_synth_pd[0] |= TX_SYNTH_VCO_ALC_POWER_DOWN | TX_SYNTH_PTAT_POWER_DOWN | TX_SYNTH_VCO_POWER_DOWN; else st->cached_synth_pd[0] &= ~(TX_SYNTH_VCO_ALC_POWER_DOWN | TX_SYNTH_PTAT_POWER_DOWN | TX_SYNTH_VCO_POWER_DOWN); ret |= ad9361_spi_write(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, st->cached_synth_pd[0]); ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, TX_EXT_VCO_BUFFER_POWER_DOWN, !enable); ret |= ad9361_spi_write(phy->spi, REG_TX_LO_GEN_POWER_MODE, TX_LO_GEN_POWER_MODE(val)); } else { ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, POWER_DOWN_RX_SYNTH, mcs_rf_enable ? 0 : enable); ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, RX_SYNTH_READY_MASK, enable); ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS, RX_VCO_DIVIDER(~0), enable ? 7 : st->cached_rx_rfpll_div); if (enable) st->cached_synth_pd[1] |= RX_SYNTH_VCO_ALC_POWER_DOWN | RX_SYNTH_PTAT_POWER_DOWN | RX_SYNTH_VCO_POWER_DOWN; else st->cached_synth_pd[1] &= ~(TX_SYNTH_VCO_ALC_POWER_DOWN | RX_SYNTH_PTAT_POWER_DOWN | RX_SYNTH_VCO_POWER_DOWN); ret |= ad9361_spi_write(phy->spi, REG_RX_SYNTH_POWER_DOWN_OVERRIDE, st->cached_synth_pd[1]); ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, RX_EXT_VCO_BUFFER_POWER_DOWN, !enable); ret |= ad9361_spi_write(phy->spi, REG_RX_LO_GEN_POWER_MODE, RX_LO_GEN_POWER_MODE(val)); } return ret; } static int ad9361_synth_lo_powerdown(struct ad9361_rf_phy *phy, enum synth_pd_ctrl rx, enum synth_pd_ctrl tx) { struct ad9361_rf_phy_state *st = phy->state; dev_dbg(&phy->spi->dev, "%s : RX(%d) TX(%d)", __func__, rx, tx); switch (rx) { case LO_OFF: st->cached_synth_pd[1] |= RX_LO_POWER_DOWN; break; case LO_ON: st->cached_synth_pd[1] &= ~RX_LO_POWER_DOWN; break; case LO_DONTCARE: break; } switch (tx) { case LO_OFF: st->cached_synth_pd[0] |= TX_LO_POWER_DOWN; break; case LO_ON: st->cached_synth_pd[0] &= ~TX_LO_POWER_DOWN; break; case LO_DONTCARE: break; } return ad9361_spi_writem(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, st->cached_synth_pd, 2); } static u32 ad9361_rfvco_tableindex(unsigned long freq) { if (freq < 50000000UL) return LUT_FTDD_40; if (freq <= 70000000UL) return LUT_FTDD_60; return LUT_FTDD_80; } static int ad9361_rfpll_vco_init(struct ad9361_rf_phy *phy, bool tx, u64 vco_freq, unsigned long ref_clk) { struct ad9361_rf_phy_state *st = phy->state; struct spi_device *spi = phy->spi; const struct SynthLUT (*tab); int i = 0; u32 range, offs = 0; range = ad9361_rfvco_tableindex(ref_clk); dev_dbg(&phy->spi->dev, "%s : vco_freq %llu : ref_clk %lu : range %d", __func__, vco_freq, ref_clk, range); do_div(vco_freq, 1000000UL); /* vco_freq in MHz */ if ((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) && (st->current_tx_lo_freq != st->current_rx_lo_freq)) { tab = &SynthLUT_FDD[range][0]; if (tx) st->current_tx_use_tdd_table = false; else st->current_rx_use_tdd_table = false; } else { tab = &SynthLUT_TDD[range][0]; if (tx) st->current_tx_use_tdd_table = true; else st->current_rx_use_tdd_table = true; } if (tx) offs = REG_TX_VCO_OUTPUT - REG_RX_VCO_OUTPUT; while (i < SYNTH_LUT_SIZE && tab[i].VCO_MHz > vco_freq) i++; dev_dbg(&phy->spi->dev, "%s : freq %d MHz : index %d", __func__, tab[i].VCO_MHz, i); ad9361_spi_write(spi, REG_RX_VCO_OUTPUT + offs, VCO_OUTPUT_LEVEL(tab[i].VCO_Output_Level) | PORB_VCO_LOGIC); ad9361_spi_writef(spi, REG_RX_ALC_VARACTOR + offs, VCO_VARACTOR(~0), tab[i].VCO_Varactor); ad9361_spi_write(spi, REG_RX_VCO_BIAS_1 + offs, VCO_BIAS_REF(tab[i].VCO_Bias_Ref) | VCO_BIAS_TCF(tab[i].VCO_Bias_Tcf)); ad9361_spi_write(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, VCO_CAL_OFFSET(tab[i].VCO_Cal_Offset)); ad9361_spi_write(spi, REG_RX_VCO_VARACTOR_CTRL_1 + offs, VCO_VARACTOR_REFERENCE( tab[i].VCO_Varactor_Reference)); ad9361_spi_write(spi, REG_RX_VCO_CAL_REF + offs, VCO_CAL_REF_TCF(0)); ad9361_spi_write(spi, REG_RX_VCO_VARACTOR_CTRL_0 + offs, VCO_VARACTOR_OFFSET(0) | VCO_VARACTOR_REFERENCE_TCF(7)); ad9361_spi_writef(spi, REG_RX_CP_CURRENT + offs, CHARGE_PUMP_CURRENT(~0), tab[i].Charge_Pump_Current); ad9361_spi_write(spi, REG_RX_LOOP_FILTER_1 + offs, LOOP_FILTER_C2(tab[i].LF_C2) | LOOP_FILTER_C1(tab[i].LF_C1)); ad9361_spi_write(spi, REG_RX_LOOP_FILTER_2 + offs, LOOP_FILTER_R1(tab[i].LF_R1) | LOOP_FILTER_C3(tab[i].LF_C3)); ad9361_spi_write(spi, REG_RX_LOOP_FILTER_3 + offs, LOOP_FILTER_R3(tab[i].LF_R3)); return 0; } static int ad9361_get_split_table_gain(struct ad9361_rf_phy *phy, u32 idx_reg, struct rf_rx_gain *rx_gain) { struct spi_device *spi = phy->spi; u32 val, tbl_addr; int rc = 0; rx_gain->fgt_lmt_index = ad9361_spi_readf(spi, idx_reg, FULL_TABLE_GAIN_INDEX(~0)); tbl_addr = ad9361_spi_read(spi, REG_GAIN_TABLE_ADDRESS); ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, rx_gain->fgt_lmt_index); val = ad9361_spi_read(spi, REG_GAIN_TABLE_READ_DATA1); rx_gain->lna_index = TO_LNA_GAIN(val); rx_gain->mixer_index = TO_MIXER_GM_GAIN(val); rx_gain->tia_index = ad9361_spi_readf(spi, REG_GAIN_TABLE_READ_DATA2, TIA_GAIN); rx_gain->lmt_gain = lna_table[ad9361_gt(phy)][rx_gain->lna_index] + mixer_table[ad9361_gt(phy)][rx_gain->mixer_index] + tia_table[rx_gain->tia_index]; ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, tbl_addr); /* Read LPF Index */ rx_gain->lpf_gain = ad9361_spi_readf(spi, idx_reg + 1, LPF_GAIN_RX(~0)); /* Read Digital Gain */ rx_gain->digital_gain = ad9361_spi_readf(spi, idx_reg + 2, DIGITAL_GAIN_RX(~0)); rx_gain->gain_db = rx_gain->lmt_gain + rx_gain->lpf_gain + rx_gain->digital_gain; return rc; } static int ad9361_get_full_table_gain(struct ad9361_rf_phy *phy, u32 idx_reg, struct rf_rx_gain *rx_gain) { struct spi_device *spi = phy->spi; u32 val; rx_gain->fgt_lmt_index = val = ad9361_spi_readf(spi, idx_reg, FULL_TABLE_GAIN_INDEX(~0)); /* Read Digital Gain */ rx_gain->digital_gain = ad9361_spi_readf(spi, idx_reg + 2, DIGITAL_GAIN_RX(~0)); rx_gain->gain_db = phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[val]; return 0; } static int ad9361_get_rx_gain(struct ad9361_rf_phy *phy, u32 rx_id, struct rf_rx_gain *rx_gain) { struct device *dev = &phy->spi->dev; struct spi_device *spi = phy->spi; u32 val, idx_reg; u8 gain_ctl_shift, rx_enable_mask; u8 fast_atk_shift; int rc = 0; if (rx_id == 1) { gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; idx_reg = REG_GAIN_RX1; rx_enable_mask = RX_CHANNEL_ENABLE(RX_1); fast_atk_shift = RX1_FAST_ATK_SHIFT; } else if (rx_id == 2) { gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; idx_reg = REG_GAIN_RX2; rx_enable_mask = RX_CHANNEL_ENABLE(RX_2); fast_atk_shift = RX2_FAST_ATK_SHIFT; } else { dev_err(dev, "Unknown Rx path %d\n", rx_id); rc = -EINVAL; goto out; } val = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, rx_enable_mask); if (!val) { dev_dbg(dev, "Rx%d is not enabled\n", rx_gain->ant); rc = -EAGAIN; goto out; } val = ad9361_spi_read(spi, REG_AGC_CONFIG_1); val = (val >> gain_ctl_shift) & RX_GAIN_CTL_MASK; if (val == RX_GAIN_CTL_AGC_FAST_ATK) { /* In fast attack mode check whether Fast attack state machine * has locked gain, if not then we can not read gain. */ val = ad9361_spi_read(spi, REG_FAST_ATTACK_STATE); val = (val >> fast_atk_shift) & FAST_ATK_MASK; if (val != FAST_ATK_GAIN_LOCKED) { dev_warn(dev, "Failed to read gain, state m/c at %x\n", val); rc = -EAGAIN; goto out; } } if (phy->pdata->split_gt) rc = ad9361_get_split_table_gain(phy, idx_reg, rx_gain); else rc = ad9361_get_full_table_gain(phy, idx_reg, rx_gain); out: return rc; } EXPORT_SYMBOL(ad9361_get_rx_gain); static u8 ad9361_ensm_get_state(struct ad9361_rf_phy *phy) { return ad9361_spi_readf(phy->spi, REG_STATE, ENSM_STATE(~0)); } void ad9361_ensm_force_state(struct ad9361_rf_phy *phy, u8 ensm_state) { struct ad9361_rf_phy_state *st = phy->state; struct spi_device *spi = phy->spi; struct device *dev = &phy->spi->dev; u8 dev_ensm_state; int rc; u32 val; dev_ensm_state = ad9361_spi_readf(spi, REG_STATE, ENSM_STATE(~0)); st->prev_ensm_state = dev_ensm_state; if (dev_ensm_state == ensm_state) { dev_dbg(dev, "Nothing to do, device is already in %d state\n", ensm_state); goto out; } dev_dbg(dev, "Device is in %x state, forcing to %x\n", dev_ensm_state, ensm_state); val = ad9361_spi_read(spi, REG_ENSM_CONFIG_1); /* Enable control through SPI writes, and take out from * Alert */ if (val & ENABLE_ENSM_PIN_CTRL) { val &= ~ENABLE_ENSM_PIN_CTRL; st->ensm_pin_ctl_en = true; } else { st->ensm_pin_ctl_en = false; } if (dev_ensm_state) val &= ~(TO_ALERT); switch (ensm_state) { case ENSM_STATE_TX: case ENSM_STATE_FDD: val |= FORCE_TX_ON; break; case ENSM_STATE_RX: val |= FORCE_RX_ON; break; case ENSM_STATE_ALERT: val &= ~(FORCE_TX_ON | FORCE_RX_ON); val |= TO_ALERT | FORCE_ALERT_STATE; break; default: dev_err(dev, "No handling for forcing %d ensm state\n", ensm_state); goto out; } ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE); rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); if (rc) dev_err(dev, "Failed to restore state\n"); out: return; } EXPORT_SYMBOL(ad9361_ensm_force_state); void ad9361_ensm_restore_state(struct ad9361_rf_phy *phy, u8 ensm_state) { struct ad9361_rf_phy_state *st = phy->state; struct spi_device *spi = phy->spi; struct device *dev = &phy->spi->dev; int rc; u32 val; val = ad9361_spi_read(spi, REG_ENSM_CONFIG_1); /* We are restoring state only, so clear State bits first * which might have set while forcing a particular state */ val &= ~(FORCE_TX_ON | FORCE_RX_ON | FORCE_ALERT_STATE); val |= TO_ALERT; switch (ensm_state) { case ENSM_STATE_TX: case ENSM_STATE_FDD: val |= FORCE_TX_ON; break; case ENSM_STATE_RX: val |= FORCE_RX_ON; break; case ENSM_STATE_ALERT: val |= TO_ALERT; break; case ENSM_STATE_INVALID: dev_dbg(dev, "No need to restore, ENSM state wasn't saved\n"); return; default: dev_dbg(dev, "Could not restore to %d ENSM state\n", ensm_state); return; } ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE); rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); if (rc) { dev_err(dev, "Failed to write ENSM_CONFIG_1"); return; } if (st->ensm_pin_ctl_en) { val |= ENABLE_ENSM_PIN_CTRL; rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); if (rc) dev_err(dev, "Failed to write ENSM_CONFIG_1"); } } EXPORT_SYMBOL(ad9361_ensm_restore_state); void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy) { return ad9361_ensm_restore_state(phy, phy->state->prev_ensm_state); } EXPORT_SYMBOL(ad9361_ensm_restore_prev_state); static int find_table_index(struct ad9361_rf_phy *phy, int gain) { u32 i, nm1, n; for (i = 0; i < phy->gt_info[ad9361_gt(phy)].max_index; i++) { if (phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[i] >= gain) { nm1 = abs(phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[ (i > 0) ? i - 1 : i] - gain); n = abs(phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[i] - gain); if (nm1 < n) return (i > 0) ? i - 1 : i; else return i; } } return -EINVAL; } static int ad9361_load_gt(struct ad9361_rf_phy *phy, u64 freq, u32 dest) { struct ad9361_rf_phy_state *st = phy->state; struct spi_device *spi = phy->spi; u8 (*tab)[3]; u32 band, index_max, i, lna, lpf_tia_mask, set_gain; int ret, rx1_gain, rx2_gain; dev_dbg(&phy->spi->dev, "%s: frequency %llu", __func__, freq); band = ad9361_gt_tableindex(phy, freq); dev_dbg(&phy->spi->dev, "%s: frequency %llu (band %d)", __func__, freq, band); /* check if table is present */ if (st->current_table == band) return 0; tab = phy->gt_info[band].tab; index_max = phy->gt_info[band].max_index; ad9361_spi_writef(spi, REG_AGC_CONFIG_2, AGC_USE_FULL_GAIN_TABLE, !phy->pdata->split_gt); ad9361_spi_write(spi, REG_MAX_LMT_FULL_GAIN, index_max - 1); /* Max Full/LMT Gain Table Index */ set_gain = ad9361_spi_readf(spi, REG_RX1_MANUAL_LMT_FULL_GAIN, RX_FULL_TBL_IDX_MASK); if (st->current_table >= 0) { rx1_gain = phy->gt_info[st->current_table].abs_gain_tbl[set_gain]; } else { if (set_gain > (index_max - 1)) set_gain = index_max - 1; rx1_gain = phy->gt_info[band].abs_gain_tbl[set_gain]; } set_gain = ad9361_spi_readf(spi, REG_RX2_MANUAL_LMT_FULL_GAIN, RX_FULL_TBL_IDX_MASK); if (st->current_table >= 0) { rx2_gain = phy->gt_info[st->current_table].abs_gain_tbl[set_gain]; } else { if (set_gain > (index_max - 1)) set_gain = index_max - 1; rx2_gain = phy->gt_info[band].abs_gain_tbl[set_gain]; } lna = phy->pdata->elna_ctrl.elna_in_gaintable_all_index_en ? EXT_LNA_CTRL : 0; ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, START_GAIN_TABLE_CLOCK | RECEIVER_SELECT(dest)); /* Start Gain Table Clock */ /* TX QUAD Calibration */ if (phy->pdata->split_gt) lpf_tia_mask = 0x20; else lpf_tia_mask = 0x3F; st->tx_quad_lpf_tia_match = -EINVAL; for (i = 0; i < index_max; i++) { ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, i); /* Gain Table Index */ ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA1, tab[i][0] | lna); /* Ext LNA, Int LNA, & Mixer Gain Word */ ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA2, tab[i][1]); /* TIA & LPF Word */ ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA3, tab[i][2]); /* DC Cal bit & Dig Gain Word */ ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, START_GAIN_TABLE_CLOCK | WRITE_GAIN_TABLE | RECEIVER_SELECT(dest)); /* Gain Table Index */ ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay 3 ADCCLK/16 cycles */ ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay ~1u */ if ((tab[i][1] & lpf_tia_mask) == 0x20) st->tx_quad_lpf_tia_match = i; } ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, START_GAIN_TABLE_CLOCK | RECEIVER_SELECT(dest)); /* Clear Write Bit */ ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay ~1u */ ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay ~1u */ ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, 0); /* Stop Gain Table Clock */ st->current_table = band; ret = find_table_index(phy, rx1_gain); if (ret < 0) ret = phy->gt_info[band].max_index - 1; ad9361_spi_writef(spi, REG_RX1_MANUAL_LMT_FULL_GAIN, RX_FULL_TBL_IDX_MASK, ret); /* Rx1 Full/LMT Gain Index */ ret = find_table_index(phy, rx2_gain); if (ret < 0) ret = phy->gt_info[band].max_index - 1; ad9361_spi_write(spi, REG_RX2_MANUAL_LMT_FULL_GAIN, ret); /* Rx2 Full/LMT Gain Index */ return 0; } static int set_split_table_gain(struct ad9361_rf_phy *phy, u32 idx_reg, struct rf_rx_gain *rx_gain) { struct device *dev = &phy->spi->dev; struct spi_device *spi = phy->spi; int rc = 0; if ((rx_gain->fgt_lmt_index > MAX_LMT_INDEX) || (rx_gain->lpf_gain > MAX_LPF_GAIN) || (rx_gain->digital_gain > MAX_DIG_GAIN)) { dev_err(dev, "LMT_INDEX missing or greater than max value %d", MAX_LMT_INDEX); dev_err(dev, "LPF_GAIN missing or greater than max value %d", MAX_LPF_GAIN); dev_err(dev, "DIGITAL_GAIN cannot be more than %d", MAX_DIG_GAIN); rc = -EINVAL; goto out; } rc = find_table_index(phy, rx_gain->gain_db); if (rc < 0) { dev_err(dev, "Invalid gain %d, supported range [%d - %d]\n", rx_gain->gain_db, phy->gt_info[ad9361_gt(phy)]. abs_gain_tbl[0], phy->gt_info[ad9361_gt(phy)].abs_gain_tbl [phy->gt_info[ad9361_gt(phy)].max_index - 1]); goto out; } rx_gain->fgt_lmt_index = rc; ad9361_spi_writef(spi, idx_reg, RX_FULL_TBL_IDX_MASK, rx_gain->fgt_lmt_index); ad9361_spi_writef(spi, idx_reg + 1, RX_LPF_IDX_MASK, rx_gain->lpf_gain); if (phy->pdata->gain_ctrl.dig_gain_en) { ad9361_spi_writef(spi, idx_reg + 2, RX_DIGITAL_IDX_MASK, rx_gain->digital_gain); } else if (rx_gain->digital_gain > 0) { dev_err(dev, "Digital gain is disabled and cannot be set"); } out: return 0; } static int set_full_table_gain(struct ad9361_rf_phy *phy, u32 idx_reg, struct rf_rx_gain *rx_gain) { struct spi_device *spi = phy->spi; struct device *dev = &phy->spi->dev; int rc = 0; if (rx_gain->fgt_lmt_index != ~0 || rx_gain->lpf_gain != ~0 || rx_gain->digital_gain > 0) dev_dbg(dev, "Ignoring lmt/lpf/digital gains in Single Table mode"); rc = find_table_index(phy, rx_gain->gain_db); if (rc < 0) { dev_err(dev, "Invalid gain %d, supported range [%d - %d]\n", rx_gain->gain_db, phy->gt_info[ad9361_gt(phy)]. abs_gain_tbl[0], phy->gt_info[ad9361_gt(phy)].abs_gain_tbl [phy->gt_info[ad9361_gt(phy)].max_index - 1]); goto out; } rc = ad9361_spi_writef(spi, idx_reg, RX_FULL_TBL_IDX_MASK, rc); out: return rc; } static int ad9361_set_rx_gain(struct ad9361_rf_phy *phy, u32 rx_id, struct rf_rx_gain *rx_gain) { struct spi_device *spi = phy->spi; struct device *dev = &phy->spi->dev; u32 val, idx_reg; u8 gain_ctl_shift; int rc = 0; if (rx_id == 1) { gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; idx_reg = REG_RX1_MANUAL_LMT_FULL_GAIN; } else if (rx_id == 2) { gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; idx_reg = REG_RX2_MANUAL_LMT_FULL_GAIN; } else { dev_err(dev, "Unknown Rx path %d\n", rx_id); rc = -EINVAL; goto out; } val = ad9361_spi_read(spi, REG_AGC_CONFIG_1); val = (val >> gain_ctl_shift) & RX_GAIN_CTL_MASK; if (val != RX_GAIN_CTL_MGC) { dev_dbg(dev, "Rx gain can be set in MGC mode only\n"); rc = -EOPNOTSUPP; goto out; } if (phy->pdata->split_gt) rc = set_split_table_gain(phy, idx_reg, rx_gain); else rc = set_full_table_gain(phy, idx_reg, rx_gain); if (rc) { dev_err(dev, "Unable to write gain tbl idx reg: %d\n", idx_reg); goto out; } out: return rc; } EXPORT_SYMBOL(ad9361_set_rx_gain); static int ad9361_gc_update(struct ad9361_rf_phy *phy) { struct ad9361_rf_phy_state *st = phy->state; struct spi_device *spi = phy->spi; unsigned long clkrf; u32 reg, delay_lna, settling_delay, dec_pow_meas_dur; int ret; clkrf = clk_get_rate(phy->clks[CLKRF_CLK]); delay_lna = phy->pdata->elna_ctrl.settling_delay_ns; /* * AGC Attack Delay (us)=ceiling((((0.2+Delay_LNA)*ClkRF+14))/(2*ClkRF))+1 * ClkRF in MHz, delay in us */ reg = (200 + delay_lna) / 2 + (14000000UL / (clkrf / 500U)); reg = DIV_ROUND_UP(reg, 1000UL) + phy->pdata->gain_ctrl.agc_attack_delay_extra_margin_us; reg = clamp_t(u8, reg, 0U, 31U); ret = ad9361_spi_writef(spi, REG_AGC_ATTACK_DELAY, AGC_ATTACK_DELAY(~0), reg); /* * Peak Overload Wait Time (ClkRF cycles)=ceiling((0.1+Delay_LNA) *clkRF+1) */ reg = (delay_lna + 100UL) * (clkrf / 1000UL); reg = DIV_ROUND_UP(reg, 1000000UL) + 1; reg = clamp_t(u8, reg, 0U, 31U); ret |= ad9361_spi_writef(spi, REG_PEAK_WAIT_TIME, PEAK_OVERLOAD_WAIT_TIME(~0), reg); /* * Settling Delay in 0x111. Applies to all gain control modes: * 0x111[D4:D0]= ceiling(((0.2+Delay_LNA)*clkRF dodebug = false;+14)/2) */ reg = (delay_lna + 200UL) * (clkrf / 2000UL); reg = DIV_ROUND_UP(reg, 1000000UL) + 7; reg = settling_delay = clamp_t(u8, reg, 0U, 31U); ret |= ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, SETTLING_DELAY(~0), reg); /* * Gain Update Counter [15:0]= round((((time*ClkRF-0x111[D4:D0]*2)-2))/2) */ reg = phy->pdata->gain_ctrl.gain_update_interval_us * (clkrf / 1000UL) - settling_delay * 2000UL - 2000UL; reg = DIV_ROUND_CLOSEST(reg, 2000UL); reg = clamp_t(u32, reg, 0U, 131071UL); if (st->agc_mode[0] == RF_GAIN_FASTATTACK_AGC || st->agc_mode[1] == RF_GAIN_FASTATTACK_AGC) { dec_pow_meas_dur = phy->pdata->gain_ctrl.f_agc_dec_pow_measuremnt_duration; } else { u32 fir_div = DIV_ROUND_CLOSEST(clkrf, clk_get_rate(phy->clks[RX_SAMPL_CLK])); dec_pow_meas_dur = phy->pdata->gain_ctrl.dec_pow_measuremnt_duration; if (((reg * 2 / fir_div) / dec_pow_meas_dur) < 2) { dec_pow_meas_dur = reg / fir_div; } } /* Power Measurement Duration */ ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, DEC_POWER_MEASUREMENT_DURATION(~0), ilog2(dec_pow_meas_dur / 16)); ret |= ad9361_spi_writef(spi, REG_DIGITAL_SAT_COUNTER, DOUBLE_GAIN_COUNTER, reg > 65535); if (reg > 65535) reg /= 2; ret |= ad9361_spi_write(spi, REG_GAIN_UPDATE_COUNTER1, reg & 0xFF); ret |= ad9361_spi_write(spi, REG_GAIN_UPDATE_COUNTER2, reg >> 8); /* * Fast AGC State Wait Time - Energy Detect Count */ reg = DIV_ROUND_CLOSEST(phy->pdata->gain_ctrl.f_agc_state_wait_time_ns * (clkrf / 1000UL), 1000000UL); reg = clamp_t(u32, reg, 0U, 31U); ret |= ad9361_spi_writef(spi, REG_FAST_ENERGY_DETECT_COUNT, ENERGY_DETECT_COUNT(~0), reg); return ret; } static int ad9361_set_gain_ctrl_mode(struct ad9361_rf_phy *phy, struct rf_gain_ctrl *gain_ctrl) { struct spi_device *spi = phy->spi; struct device *dev = &phy->spi->dev; int rc = 0; u32 gain_ctl_shift, mode; u8 val; rc = ad9361_spi_readm(spi, REG_AGC_CONFIG_1, &val, 1); if (rc) { dev_err(dev, "Unable to read AGC config1 register: %x\n", REG_AGC_CONFIG_1); goto out; } switch (gain_ctrl->mode) { case RF_GAIN_MGC: mode = RX_GAIN_CTL_MGC; break; case RF_GAIN_FASTATTACK_AGC: mode = RX_GAIN_CTL_AGC_FAST_ATK; break; case RF_GAIN_SLOWATTACK_AGC: mode = RX_GAIN_CTL_AGC_SLOW_ATK; break; case RF_GAIN_HYBRID_AGC: mode = RX_GAIN_CTL_AGC_SLOW_ATK_HYBD; break; default: rc = -EINVAL; goto out; } if (gain_ctrl->ant == 1) { gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; } else if (gain_ctrl->ant == 2) { gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; } else { dev_err(dev, "Unknown Rx path %d\n", gain_ctrl->ant); rc = -EINVAL; goto out; } rc = ad9361_en_dis_rx(phy, gain_ctrl->ant, RX_DISABLE); if (rc) { dev_err(dev, "Unable to disable rx%d\n", gain_ctrl->ant); goto out; } val &= ~(RX_GAIN_CTL_MASK << gain_ctl_shift); val |= mode << gain_ctl_shift; if (mode == RX_GAIN_CTL_AGC_SLOW_ATK_HYBD) val |= SLOW_ATTACK_HYBRID_MODE; else val &= ~SLOW_ATTACK_HYBRID_MODE; rc = ad9361_spi_write(spi, REG_AGC_CONFIG_1, val); if (rc) { dev_err(dev, "Unable to write AGC config1 register: %x\n", REG_AGC_CONFIG_1); goto out; } ad9361_en_dis_rx(phy, gain_ctrl->ant, RX_ENABLE); rc = ad9361_gc_update(phy); out: return rc; } EXPORT_SYMBOL(ad9361_set_gain_ctrl_mode); int ad9361_read_rssi(struct ad9361_rf_phy *phy, struct rf_rssi *rssi) { struct spi_device *spi = phy->spi; u8 reg_val_buf[6]; int rc; rc = ad9361_spi_readm(spi, REG_PREAMBLE_LSB, reg_val_buf, ARRAY_SIZE(reg_val_buf)); if (rssi->ant == 1) { rssi->symbol = RSSI_RESOLUTION * ((reg_val_buf[5] << RSSI_LSB_SHIFT) + (reg_val_buf[1] & RSSI_LSB_MASK1)); rssi->preamble = RSSI_RESOLUTION * ((reg_val_buf[4] << RSSI_LSB_SHIFT) + (reg_val_buf[0] & RSSI_LSB_MASK1)); } else if (rssi->ant == 2) { rssi->symbol = RSSI_RESOLUTION * ((reg_val_buf[3] << RSSI_LSB_SHIFT) + ((reg_val_buf[1] & RSSI_LSB_MASK2) >> 1)); rssi->preamble = RSSI_RESOLUTION * ((reg_val_buf[2] << RSSI_LSB_SHIFT) + ((reg_val_buf[0] & RSSI_LSB_MASK2) >> 1)); } else rc = -EFAULT; rssi->multiplier = RSSI_MULTIPLIER; return rc; } EXPORT_SYMBOL(ad9361_read_rssi); static int ad9361_rx_adc_setup(struct ad9361_rf_phy *phy, unsigned long bbpll_freq, unsigned long adc_sampl_freq_Hz) { struct ad9361_rf_phy_state *st = phy->state; unsigned long scale_snr_1e3, maxsnr, sqrt_inv_rc_tconst_1e3, tmp_1e3, scaled_adc_clk_1e6, inv_scaled_adc_clk_1e3, sqrt_term_1e3, min_sqrt_term_1e3, bb_bw_Hz; u64 tmp, invrc_tconst_1e6; u8 data[40]; u32 i; int ret; /* Following registers are set implicitly by the RX BB analog filter calibration */ u8 c3_msb = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_MSB); u8 c3_lsb = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_LSB); u8 r2346 = ad9361_spi_read(phy->spi, REG_RX_BBF_R2346); /* * BBBW = (BBPLL / RxTuneDiv) * ln(2) / (1.4 * 2PI ) * We assume ad9361_rx_bb_analog_filter_calib() is always run prior */ tmp = bbpll_freq * 10000ULL; do_div(tmp, 126906UL * st->rxbbf_div); bb_bw_Hz = tmp; dev_dbg(&phy->spi->dev, "%s : BBBW %lu : ADCfreq %lu", __func__, bb_bw_Hz, adc_sampl_freq_Hz); dev_dbg(&phy->spi->dev, "c3_msb 0x%X : c3_lsb 0x%X : r2346 0x%X : ", c3_msb, c3_lsb, r2346); bb_bw_Hz = clamp(bb_bw_Hz, 200000UL, 28000000UL); if (adc_sampl_freq_Hz < 80000000) scale_snr_1e3 = 1000; else scale_snr_1e3 = 1585; /* pow(10, scale_snr_dB/10); */ if (bb_bw_Hz >= 18000000) { invrc_tconst_1e6 = (160975ULL * r2346 * (160 * c3_msb + 10 * c3_lsb + 140) * (bb_bw_Hz) * (1000 + (10 * (bb_bw_Hz - 18000000) / 1000000))); do_div(invrc_tconst_1e6, 1000UL); } else { invrc_tconst_1e6 = (160975ULL * r2346 * (160 * c3_msb + 10 * c3_lsb + 140) * (bb_bw_Hz)); } do_div(invrc_tconst_1e6, 1000000000UL); if (invrc_tconst_1e6 > 0xFFFFFFFF) dev_err(&phy->spi->dev, "invrc_tconst_1e6 > ULONG_MAX"); sqrt_inv_rc_tconst_1e3 = int_sqrt((u32)invrc_tconst_1e6); maxsnr = 640/160; scaled_adc_clk_1e6 = DIV_ROUND_CLOSEST(adc_sampl_freq_Hz, 640); inv_scaled_adc_clk_1e3 = DIV_ROUND_CLOSEST(640000000, DIV_ROUND_CLOSEST(adc_sampl_freq_Hz, 1000)); tmp_1e3 = DIV_ROUND_CLOSEST(980000 + 20 * max_t(u32, 1000U, DIV_ROUND_CLOSEST(inv_scaled_adc_clk_1e3, maxsnr)), 1000); sqrt_term_1e3 = int_sqrt(scaled_adc_clk_1e6); min_sqrt_term_1e3 = min_t(u32, 1000U, int_sqrt(maxsnr * scaled_adc_clk_1e6)); dev_dbg(&phy->spi->dev, "invrc_tconst_1e6 %llu, sqrt_inv_rc_tconst_1e3 %lu\n", invrc_tconst_1e6, sqrt_inv_rc_tconst_1e3); dev_dbg(&phy->spi->dev, "scaled_adc_clk_1e6 %lu, inv_scaled_adc_clk_1e3 %lu\n", scaled_adc_clk_1e6, inv_scaled_adc_clk_1e3); dev_dbg(&phy->spi->dev, "tmp_1e3 %lu, sqrt_term_1e3 %lu, min_sqrt_term_1e3 %lu\n", tmp_1e3, sqrt_term_1e3, min_sqrt_term_1e3); data[0] = 0; data[1] = 0; data[2] = 0; data[3] = 0x24; data[4] = 0x24; data[5] = 0; data[6] = 0; tmp = -50000000 + 8ULL * scale_snr_1e3 * sqrt_inv_rc_tconst_1e3 * min_sqrt_term_1e3; do_div(tmp, 100000000UL); data[7] = min_t(u64, 124U, tmp); tmp = (invrc_tconst_1e6 >> 1) + 20 * inv_scaled_adc_clk_1e3 * data[7] / 80 * 1000ULL; do_div(tmp, invrc_tconst_1e6); data[8] = min_t(u64, 255U, tmp); tmp = (-500000 + 77ULL * sqrt_inv_rc_tconst_1e3 * min_sqrt_term_1e3); do_div(tmp, 1000000UL); data[10] = min_t(u64, 127U, tmp); data[9] = min_t(u32, 127U, ((800 * data[10]) / 1000)); tmp = ((invrc_tconst_1e6 >> 1) + (20 * inv_scaled_adc_clk_1e3 * data[10] * 1000ULL)); do_div(tmp, invrc_tconst_1e6 * 77); data[11] = min_t(u64, 255U, tmp); data[12] = min_t(u32, 127U, (-500000 + 80 * sqrt_inv_rc_tconst_1e3 * min_sqrt_term_1e3) / 1000000UL); tmp = -3*(long)(invrc_tconst_1e6 >> 1) + inv_scaled_adc_clk_1e3 * data[12] * (1000ULL * 20 / 80); do_div(tmp, invrc_tconst_1e6); data[13] = min_t(u64, 255, tmp); data[14] = 21 * (inv_scaled_adc_clk_1e3 / 10000); data[15] = min_t(u32, 127U, (500 + 1025 * data[7]) / 1000); data[16] = min_t(u32, 127U, (data[15] * tmp_1e3) / 1000); data[17] = data[15]; data[18] = min_t(u32, 127U, (500 + 975 * data[10]) / 1000); data[19] = min_t(u32, 127U, (data[18] * tmp_1e3) / 1000); data[20] = data[18]; data[21] = min_t(u32, 127U, (500 + 975 * data[12]) / 1000); data[22] = min_t(u32, 127, (data[21] * tmp_1e3) / 1000); data[23] = data[21]; data[24] = 0x2E; data[25] = (128 + min_t(u32, 63000U, DIV_ROUND_CLOSEST(63 * scaled_adc_clk_1e6, 1000)) / 1000); data[26] = min_t(u32, 63U,63 * scaled_adc_clk_1e6 / 1000000 * (920 + 80 * inv_scaled_adc_clk_1e3 / 1000) / 1000); data[27] = min_t(u32, 63,(32 * sqrt_term_1e3) / 1000); data[28] = data[25]; data[29] = data[26]; data[30] = data[27]; data[31] = data[25]; data[32] = data[26]; data[33] = min_t(u32, 63U, 63 * sqrt_term_1e3 / 1000); data[34] = min_t(u32, 127U, 64 * sqrt_term_1e3 / 1000); data[35] = 0x40; data[36] = 0x40; data[37] = 0x2C; data[38] = 0x00; data[39] = 0x00; for (i = 0; i < 40; i++) { ret = ad9361_spi_write(phy->spi, 0x200 + i, data[i]); if (ret < 0) return ret; } return 0; } static int ad9361_rx_tia_calib(struct ad9361_rf_phy *phy, unsigned long bb_bw_Hz) { unsigned long Cbbf, R2346; u64 CTIA_fF; /* Following registers are set implicitly by the RX BB analog filter calibration */ u8 reg1EB = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_MSB); u8 reg1EC = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_LSB); u8 reg1E6 = ad9361_spi_read(phy->spi, REG_RX_BBF_R2346); u8 reg1DB, reg1DF, reg1DD, reg1DC, reg1DE, temp; dev_dbg(&phy->spi->dev, "%s : bb_bw_Hz %lu", __func__, bb_bw_Hz); bb_bw_Hz = clamp(bb_bw_Hz, 200000UL, 20000000UL); Cbbf = (reg1EB * 160) + (reg1EC * 10) + 140; /* fF */ R2346 = 18300 * RX_BBF_R2346(reg1E6); CTIA_fF = Cbbf * R2346 * 560ULL; do_div(CTIA_fF, 3500000UL); if (bb_bw_Hz <= 3000000UL) reg1DB = 0xE0; else if (bb_bw_Hz <= 10000000UL) reg1DB = 0x60; else reg1DB = 0x20; if (CTIA_fF > 2920ULL) { reg1DC = 0x40; reg1DE = 0x40; temp = min(127U, DIV_ROUND_CLOSEST((u32)CTIA_fF - 400, 320U)); reg1DD = temp; reg1DF = temp; } else { temp = DIV_ROUND_CLOSEST((u32)CTIA_fF - 400, 40U) + 0x40; reg1DC = temp; reg1DE = temp; reg1DD = 0; reg1DF = 0; } ad9361_spi_write(phy->spi, REG_RX_TIA_CONFIG, reg1DB); ad9361_spi_write(phy->spi, REG_TIA1_C_LSB, reg1DC); ad9361_spi_write(phy->spi, REG_TIA1_C_MSB, reg1DD); ad9361_spi_write(phy->spi, REG_TIA2_C_LSB, reg1DE); ad9361_spi_write(phy->spi, REG_TIA2_C_MSB, reg1DF); return 0; } /* BASEBAND RX ANALOG FILTER CALIBRATION */ static int ad9361_rx_bb_analog_filter_calib(struct ad9361_rf_phy *phy, unsigned long rx_bb_bw, unsigned long bbpll_freq) { struct ad9361_rf_phy_state *st = phy->state; unsigned long target; u8 tmp; int ret; dev_dbg(&phy->spi->dev, "%s : rx_bb_bw %lu bbpll_freq %lu", __func__, rx_bb_bw, bbpll_freq); rx_bb_bw = clamp(rx_bb_bw, 200000UL, 28000000UL); /* 1.4 * BBBW * 2PI / ln(2) */ target = 126906UL * (rx_bb_bw / 10000UL); st->rxbbf_div = min_t(unsigned long, 511UL, DIV_ROUND_UP(bbpll_freq, target)); /* Set RX baseband filter divide value */ ad9361_spi_write(phy->spi, REG_RX_BBF_TUNE_DIVIDE, st->rxbbf_div); ad9361_spi_writef(phy->spi, REG_RX_BBF_TUNE_CONFIG, BIT(0), st->rxbbf_div >> 8); /* Write the BBBW into registers 0x1FB and 0x1FC */ ad9361_spi_write(phy->spi, REG_RX_BBBW_MHZ, rx_bb_bw / 1000000UL); tmp = DIV_ROUND_CLOSEST((rx_bb_bw % 1000000UL) * 128, 1000000UL); ad9361_spi_write(phy->spi, REG_RX_BBBW_KHZ, min_t(u8, 127, tmp)); ad9361_spi_write(phy->spi, REG_RX_MIX_LO_CM, RX_MIX_LO_CM(0x3F)); /* Set Rx Mix LO CM */ ad9361_spi_write(phy->spi, REG_RX_MIX_GM_CONFIG, RX_MIX_GM_PLOAD(3)); /* Set GM common mode */ /* Enable the RX BBF tune circuit by writing 0x1E2=0x02 and 0x1E3=0x02 */ ad9361_spi_write(phy->spi, REG_RX1_TUNE_CTRL, RX1_TUNE_RESAMPLE); ad9361_spi_write(phy->spi, REG_RX2_TUNE_CTRL, RX2_TUNE_RESAMPLE); /* Start the RX Baseband Filter calibration in register 0x016[7] */ /* Calibration is complete when register 0x016[7] self clears */ ret = ad9361_run_calibration(phy, RX_BB_TUNE_CAL); /* Disable the RX baseband filter tune circuit, write 0x1E2=3, 0x1E3=3 */ ad9361_spi_write(phy->spi, REG_RX1_TUNE_CTRL, RX1_TUNE_RESAMPLE | RX1_PD_TUNE); ad9361_spi_write(phy->spi, REG_RX2_TUNE_CTRL, RX2_TUNE_RESAMPLE | RX2_PD_TUNE); return ret; } /* BASEBAND TX ANALOG FILTER CALIBRATION */ static int ad9361_tx_bb_analog_filter_calib(struct ad9361_rf_phy *phy, unsigned long tx_bb_bw, unsigned long bbpll_freq) { unsigned long target, txbbf_div; int ret; dev_dbg(&phy->spi->dev, "%s : tx_bb_bw %lu bbpll_freq %lu", __func__, tx_bb_bw, bbpll_freq); tx_bb_bw = clamp(tx_bb_bw, 625000UL, 20000000UL); /* 1.6 * BBBW * 2PI / ln(2) */ target = 145036 * (tx_bb_bw / 10000UL); txbbf_div = min_t(unsigned long, 511UL, DIV_ROUND_UP(bbpll_freq, target)); /* Set TX baseband filter divide value */ ad9361_spi_write(phy->spi, REG_TX_BBF_TUNE_DIVIDER, txbbf_div); ad9361_spi_writef(phy->spi, REG_TX_BBF_TUNE_MODE, TX_BBF_TUNE_DIVIDER, txbbf_div >> 8); /* Enable the TX baseband filter tune circuit by setting 0x0CA=0x22. */ ad9361_spi_write(phy->spi, REG_TX_TUNE_CTRL, TUNER_RESAMPLE | TUNE_CTRL(1)); /* Start the TX Baseband Filter calibration in register 0x016[6] */ /* Calibration is complete when register 0x016[] self clears */ ret = ad9361_run_calibration(phy, TX_BB_TUNE_CAL); /* Disable the TX baseband filter tune circuit by writing 0x0CA=0x26. */ ad9361_spi_write(phy->spi, REG_TX_TUNE_CTRL, TUNER_RESAMPLE | TUNE_CTRL(1) | PD_TUNE); return ret; } /* BASEBAND TX SECONDARY FILTER */ static int ad9361_tx_bb_second_filter_calib(struct ad9361_rf_phy *phy, unsigned long tx_bb_bw) { u64 cap; unsigned long corner, res, div; u32 reg_conf, reg_res; int ret, i; dev_dbg(&phy->spi->dev, "%s : tx_bb_bw %lu", __func__, tx_bb_bw); tx_bb_bw = clamp(tx_bb_bw, 530000UL, 20000000UL); /* BBBW * 5PI */ corner = 15708 * (tx_bb_bw / 10000UL); for (i = 0, res = 1; i < 4; i++) { div = corner * res; cap = (500000000ULL) + (div >> 1); do_div(cap, div); cap -= 12ULL; if (cap < 64ULL) break; res <<= 1; } if (cap > 63ULL) cap = 63ULL; if(tx_bb_bw <= 4500000UL ) reg_conf = 0x59; else if (tx_bb_bw <= 12000000UL) reg_conf = 0x56; else reg_conf = 0x57; switch (res) { case 1: reg_res = 0x0C; break; case 2: reg_res = 0x04; break; case 4: reg_res = 0x03; break; case 8: reg_res = 0x01; break; default: reg_res = 0x01; } ret = ad9361_spi_write(phy->spi, REG_CONFIG0, reg_conf); ret |= ad9361_spi_write(phy->spi, REG_RESISTOR, reg_res); ret |= ad9361_spi_write(phy->spi, REG_CAPACITOR, (u8)cap); return ret; } /* RF SYNTHESIZER CHARGE PUMP CALIBRATION */ static int ad9361_txrx_synth_cp_calib(struct ad9361_rf_phy *phy, unsigned long ref_clk_hz, bool tx) { u32 offs = tx ? 0x40 : 0; u32 vco_cal_cnt; dev_dbg(&phy->spi->dev, "%s : ref_clk_hz %lu : is_tx %d", __func__, ref_clk_hz, tx); /* REVIST:*/ ad9361_spi_write(phy->spi, REG_RX_CP_LEVEL_DETECT + offs, 0x17); ad9361_spi_write(phy->spi, REG_RX_DSM_SETUP_1 + offs, 0x0); ad9361_spi_write(phy->spi, REG_RX_LO_GEN_POWER_MODE + offs, 0x00); ad9361_spi_write(phy->spi, REG_RX_VCO_LDO + offs, 0x0B); ad9361_spi_write(phy->spi, REG_RX_VCO_PD_OVERRIDES + offs, 0x02); ad9361_spi_write(phy->spi, REG_RX_CP_CURRENT + offs, 0x80); ad9361_spi_write(phy->spi, REG_RX_CP_CONFIG + offs, CP_OFFSET_OFF); /* see Table 70 Example Calibration Times for RF VCO Cal */ if (phy->pdata->fdd) { vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(3) | FB_CLOCK_ADV(2); } else { if (ref_clk_hz > 40000000UL) vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(1) | FB_CLOCK_ADV(2); else vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(0) | FB_CLOCK_ADV(2); } ad9361_spi_write(phy->spi, REG_RX_VCO_CAL + offs, vco_cal_cnt); /* Enable FDD mode during calibrations */ if (!phy->pdata->fdd) { ad9361_spi_writef(phy->spi, REG_PARALLEL_PORT_CONF_3, HALF_DUPLEX_MODE, 0); } ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, DUAL_SYNTH_MODE); ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_1, FORCE_ALERT_STATE | TO_ALERT); ad9361_spi_write(phy->spi, REG_ENSM_MODE, FDD_MODE); ad9361_spi_write(phy->spi, REG_RX_CP_CONFIG + offs, CP_OFFSET_OFF | CP_CAL_ENABLE); return ad9361_check_cal_done(phy, REG_RX_CAL_STATUS + offs, CP_CAL_VALID, 1); } /* BASEBAND DC OFFSET CALIBRATION */ static int ad9361_bb_dc_offset_calib(struct ad9361_rf_phy *phy) { dev_dbg(&phy->spi->dev, "%s", __func__); ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_COUNT, 0x3F); ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_SHIFT, BB_DC_M_SHIFT(0xF)); ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_ATTEN, BB_DC_OFFSET_ATTEN(1)); return ad9361_run_calibration(phy, BBDC_CAL); } /* RF DC OFFSET CALIBRATION */ static int ad9361_rf_dc_offset_calib(struct ad9361_rf_phy *phy, u64 rx_freq) { struct spi_device *spi = phy->spi; dev_dbg(&phy->spi->dev, "%s : rx_freq %llu", __func__, rx_freq); ad9361_spi_write(spi, REG_WAIT_COUNT, 0x20); if(rx_freq <= 4000000000ULL) { ad9361_spi_write(spi, REG_RF_DC_OFFSET_COUNT, phy->pdata->rf_dc_offset_count_low); ad9361_spi_write(spi, REG_RF_DC_OFFSET_CONFIG_1, RF_DC_CALIBRATION_COUNT(4) | DAC_FS(2)); ad9361_spi_write(spi, REG_RF_DC_OFFSET_ATTEN, RF_DC_OFFSET_ATTEN( phy->pdata->dc_offset_attenuation_low)); } else { ad9361_spi_write(spi, REG_RF_DC_OFFSET_COUNT, phy->pdata->rf_dc_offset_count_high); ad9361_spi_write(spi, REG_RF_DC_OFFSET_CONFIG_1, RF_DC_CALIBRATION_COUNT(4) | DAC_FS(3)); ad9361_spi_write(spi, REG_RF_DC_OFFSET_ATTEN, RF_DC_OFFSET_ATTEN( phy->pdata->dc_offset_attenuation_high)); } ad9361_spi_write(spi, REG_DC_OFFSET_CONFIG2, USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL | DC_OFFSET_UPDATE(3)); if (phy->pdata->rx1rx2_phase_inversion_en || (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2)) { ad9361_spi_write(spi, REG_INVERT_BITS, INVERT_RX1_RF_DC_CGOUT_WORD); } else { ad9361_spi_write(spi, REG_INVERT_BITS, INVERT_RX1_RF_DC_CGOUT_WORD | INVERT_RX2_RF_DC_CGOUT_WORD); } return ad9361_run_calibration(phy, RFDC_CAL); } static int __ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy, u32 rf_rx_bw, u32 rf_tx_bw) { u32 real_rx_bandwidth = rf_rx_bw / 2; u32 real_tx_bandwidth = rf_tx_bw / 2; unsigned long bbpll_freq; int ret; dev_dbg(&phy->spi->dev, "%s: %d %d", __func__, rf_rx_bw, rf_tx_bw); bbpll_freq = clk_get_rate(phy->clks[BBPLL_CLK]); ret = ad9361_rx_bb_analog_filter_calib(phy, real_rx_bandwidth, bbpll_freq); if (ret < 0) return ret; ret = ad9361_tx_bb_analog_filter_calib(phy, real_tx_bandwidth, bbpll_freq); if (ret < 0) return ret; ret = ad9361_rx_tia_calib(phy, real_rx_bandwidth); if (ret < 0) return ret; ret = ad9361_tx_bb_second_filter_calib(phy, real_tx_bandwidth); if (ret < 0) return ret; ret = ad9361_rx_adc_setup(phy, bbpll_freq, clk_get_rate(phy->clks[ADC_CLK])); if (ret < 0) return ret; return 0; } /* TX QUADRATURE CALIBRATION */ static int __ad9361_tx_quad_calib(struct ad9361_rf_phy *phy, u32 phase, u32 rxnco_word, u32 decim, u8 *res) { int ret; ad9361_spi_write(phy->spi, REG_QUAD_CAL_NCO_FREQ_PHASE_OFFSET, RX_NCO_FREQ(rxnco_word) | RX_NCO_PHASE_OFFSET(phase)); ad9361_spi_write(phy->spi, REG_QUAD_CAL_CTRL, SETTLE_MAIN_ENABLE | DC_OFFSET_ENABLE | QUAD_CAL_SOFT_RESET | GAIN_ENABLE | PHASE_ENABLE | M_DECIM(decim)); ad9361_spi_write(phy->spi, REG_QUAD_CAL_CTRL, SETTLE_MAIN_ENABLE | DC_OFFSET_ENABLE | GAIN_ENABLE | PHASE_ENABLE | M_DECIM(decim)); ret = ad9361_run_calibration(phy, TX_QUAD_CAL); if (ret < 0) return ret; if (res) *res = ad9361_spi_read(phy->spi, (phy->pdata->rx1tx1_mode_use_tx_num == 2) ? REG_QUAD_CAL_STATUS_TX2 : REG_QUAD_CAL_STATUS_TX1) & (TX1_LO_CONV | TX1_SSB_CONV); return 0; } static int ad9361_tx_quad_phase_search(struct ad9361_rf_phy *phy, u32 rxnco_word, u8 decim) { struct ad9361_rf_phy_state *st = phy->state; int i, ret; u8 field[64], val; u32 start; dev_dbg(&phy->spi->dev, "%s", __func__); for (i = 0; i < (ARRAY_SIZE(field) / 2); i++) { ret = __ad9361_tx_quad_calib(phy, i, rxnco_word, decim, &val); if (ret < 0) return ret; /* Handle 360/0 wrap around */ field[i] = field[i + 32] = !((val & TX1_LO_CONV) && (val & TX1_SSB_CONV)); } ret = ad9361_find_opt(field, ARRAY_SIZE(field), &start); st->last_tx_quad_cal_phase = (start + ret / 2) & 0x1F; #ifdef _DEBUG for (i = 0; i < 64; i++) { pr_err("%c", (field[i] ? '#' : 'o')); } pr_err(" RX_NCO_PHASE_OFFSET(%d, 0x%X)\n", st->last_tx_quad_cal_phase, st->last_tx_quad_cal_phase); #endif ret = __ad9361_tx_quad_calib(phy, st->last_tx_quad_cal_phase, rxnco_word, decim, NULL); if (ret < 0) return ret; return 0; } static int ad9361_tx_quad_calib(struct ad9361_rf_phy *phy, unsigned long bw_rx, unsigned long bw_tx, int rx_phase) { struct ad9361_rf_phy_state *st = phy->state; struct device *dev = &phy->spi->dev; struct spi_device *spi = phy->spi; unsigned long clktf, clkrf; int txnco_word, rxnco_word, txnco_freq, ret; u8 __rx_phase = 0, reg_inv_bits, val, decim; bool phase_inversion_en; if (st->cached_synth_pd[0] & TX_LO_POWER_DOWN) { if (phy->pdata->lo_powerdown_managed_en) { ad9361_spi_writef(spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, TX_LO_POWER_DOWN, 0); } else { dev_err(dev, "%s : Tx QUAD Cal abort due to TX LO in powerdown\n", __func__); return -EFAULT; } } /* * Find NCO frequency that matches this equation: * BW / 4 = Rx NCO freq = Tx NCO freq: * Rx NCO = ClkRF * (rxNCO <1:0> + 1) / 32 * Tx NCO = ClkTF * (txNCO <1:0> + 1) / 32 */ clkrf = clk_get_rate(phy->clks[CLKRF_CLK]); clktf = clk_get_rate(phy->clks[CLKTF_CLK]); dev_dbg(&phy->spi->dev, "%s : bw_tx %lu clkrf %lu clktf %lu", __func__, bw_tx, clkrf, clktf); txnco_word = DIV_ROUND_CLOSEST(bw_tx * 8, clktf) - 1; txnco_word = clamp_t(int, txnco_word, 0, 3); rxnco_word = txnco_word; dev_dbg(dev, "Tx NCO frequency: %lu (BW/4: %lu) txnco_word %d\n", clktf * (txnco_word + 1) / 32, bw_tx / 4, txnco_word); if (clktf <= 4000000UL) decim = 2; else decim = 3; if (clkrf == (2 * clktf)) { __rx_phase = 0x0E; switch (txnco_word) { case 0: txnco_word++; break; case 1: rxnco_word--; break; case 2: rxnco_word-=2; txnco_word--; break; case 3: rxnco_word-=2; /* REVISIT */ __rx_phase = 0x08; break; } } else if (clkrf == clktf) { switch (txnco_word) { case 0: case 3: __rx_phase = 0x15; break; case 2: __rx_phase = 0x1F; break; case 1: if (ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, 0x3F) == 0x22) __rx_phase = 0x15; /* REVISIT */ else __rx_phase = 0x1A; break; } } else dev_err(dev, "Unhandled case in %s line %d clkrf %lu clktf %lu\n", __func__, __LINE__, clkrf, clktf); if (rx_phase >= 0) __rx_phase = rx_phase; txnco_freq = clktf * (txnco_word + 1) / 32; if (txnco_freq > (bw_rx / 4) || txnco_freq > (bw_tx / 4)) { /* Make sure the BW during calibration is wide enough */ ret = __ad9361_update_rf_bandwidth(phy, txnco_freq * 8, txnco_freq * 8); if (ret < 0) goto out_restore; } phase_inversion_en = phy->pdata->rx1rx2_phase_inversion_en || (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2); if (phase_inversion_en) { ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 0); reg_inv_bits = ad9361_spi_read(spi, REG_INVERT_BITS); ad9361_spi_write(spi, REG_INVERT_BITS, INVERT_RX1_RF_DC_CGOUT_WORD | INVERT_RX2_RF_DC_CGOUT_WORD); } ad9361_spi_writef(spi, REG_KEXP_2, TX_NCO_FREQ(~0), txnco_word); ad9361_spi_write(spi, REG_QUAD_CAL_COUNT, 0xFF); ad9361_spi_write(spi, REG_KEXP_1, KEXP_TX(1) | KEXP_TX_COMP(3) | KEXP_DC_I(3) | KEXP_DC_Q(3)); ad9361_spi_write(spi, REG_MAG_FTEST_THRESH, 0x01); ad9361_spi_write(spi, REG_MAG_FTEST_THRESH_2, 0x01); if (st->tx_quad_lpf_tia_match < 0) /* set in ad9361_load_gt() */ dev_err(dev, "failed to find suitable LPF TIA value in gain table\n"); else ad9361_spi_write(spi, REG_TX_QUAD_FULL_LMT_GAIN, st->tx_quad_lpf_tia_match); ad9361_spi_write(spi, REG_QUAD_SETTLE_COUNT, 0xF0); ad9361_spi_write(spi, REG_TX_QUAD_LPF_GAIN, 0x00); if (rx_phase != -2) { ret = __ad9361_tx_quad_calib(phy, __rx_phase, rxnco_word, decim, &val); dev_dbg(dev, "LO leakage: %d Quadrature Calibration: %d : rx_phase %d\n", !!(val & TX1_LO_CONV), !!(val & TX1_SSB_CONV), __rx_phase); /* Calibration failed -> try last phase offset */ if (val != (TX1_LO_CONV | TX1_SSB_CONV)) { if (st->last_tx_quad_cal_phase < 31) ret = __ad9361_tx_quad_calib(phy, st->last_tx_quad_cal_phase, rxnco_word, decim, &val); } else { st->last_tx_quad_cal_phase = __rx_phase; } } else { /* force phase search */ val = 0; } /* Calibration failed -> loop through all 32 phase offsets */ if (val != (TX1_LO_CONV | TX1_SSB_CONV)) ret = ad9361_tx_quad_phase_search(phy, rxnco_word, decim); if (phase_inversion_en) { ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 1); ad9361_spi_write(spi, REG_INVERT_BITS, reg_inv_bits); } if (txnco_freq > (bw_rx / 4) || txnco_freq > (bw_tx / 4)) { __ad9361_update_rf_bandwidth(phy, st->current_rx_bw_Hz, st->current_tx_bw_Hz); } out_restore: /* Restore synthesizer powerdown configuration */ if (phy->pdata->lo_powerdown_managed_en && (st->cached_synth_pd[0] & TX_LO_POWER_DOWN)) ad9361_synth_lo_powerdown(phy, LO_DONTCARE, LO_DONTCARE); return ret; } static int ad9361_tracking_control(struct ad9361_rf_phy *phy, bool bbdc_track, bool rfdc_track, bool rxquad_track) { struct spi_device *spi = phy->spi; u32 qtrack = 0; dev_dbg(&spi->dev, "%s : bbdc_track=%d, rfdc_track=%d, rxquad_track=%d", __func__, bbdc_track, rfdc_track, rxquad_track); ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_2, CALIBRATION_CONFIG2_DFLT | K_EXP_PHASE(0x15)); ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_3, PREVENT_POS_LOOP_GAIN | K_EXP_AMPLITUDE(0x15)); ad9361_spi_write(spi, REG_DC_OFFSET_CONFIG2, USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL | DC_OFFSET_UPDATE(phy->pdata->dc_offset_update_events) | (bbdc_track ? ENABLE_BB_DC_OFFSET_TRACKING : 0) | (rfdc_track ? ENABLE_RF_OFFSET_TRACKING : 0)); ad9361_spi_writef(spi, REG_RX_QUAD_GAIN2, CORRECTION_WORD_DECIMATION_M(~0), phy->pdata->qec_tracking_slow_mode_en ? 4 : 0); if (rxquad_track) { if (phy->pdata->rx2tx2) qtrack = ENABLE_TRACKING_MODE_CH1 | ENABLE_TRACKING_MODE_CH2; else qtrack = (phy->pdata->rx1tx1_mode_use_rx_num == 1) ? ENABLE_TRACKING_MODE_CH1 : ENABLE_TRACKING_MODE_CH2; } ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_1, ENABLE_PHASE_CORR | ENABLE_GAIN_CORR | FREE_RUN_MODE | ENABLE_CORR_WORD_DECIMATION | qtrack); return 0; } static int ad9361_trx_vco_cal_control(struct ad9361_rf_phy *phy, bool tx, bool enable) { dev_dbg(&phy->spi->dev, "%s : state %d", __func__, enable); return ad9361_spi_writef(phy->spi, tx ? REG_TX_PFD_CONFIG : REG_RX_PFD_CONFIG, BYPASS_LD_SYNTH, !enable); } /* REFERENCE CLOCK DELAY UNIT COUNTER REGISTER */ static int ad9361_set_ref_clk_cycles(struct ad9361_rf_phy *phy, unsigned long ref_clk_hz) { dev_dbg(&phy->spi->dev, "%s : ref_clk_hz %lu", __func__, ref_clk_hz); return ad9361_spi_write(phy->spi, REG_REFERENCE_CLOCK_CYCLES, REFERENCE_CLOCK_CYCLES_PER_US((ref_clk_hz / 1000000UL) - 1)); } static int ad9361_set_dcxo_tune(struct ad9361_rf_phy *phy, u32 coarse, u32 fine) { dev_dbg(&phy->spi->dev, "%s : coarse %u fine %u", __func__, coarse, fine); if (phy->pdata->use_extclk) return -ENODEV; ad9361_spi_write(phy->spi, REG_DCXO_COARSE_TUNE, DCXO_TUNE_COARSE(coarse)); ad9361_spi_write(phy->spi, REG_DCXO_FINE_TUNE_LOW, DCXO_TUNE_FINE_LOW(fine)); return ad9361_spi_write(phy->spi, REG_DCXO_FINE_TUNE_HIGH, DCXO_TUNE_FINE_HIGH(fine)); } static int ad9361_txmon_setup(struct ad9361_rf_phy *phy, struct tx_monitor_control *ctrl) { struct spi_device *spi = phy->spi; dev_dbg(&phy->spi->dev, "%s", __func__); ad9361_spi_write(spi, REG_TPM_MODE_ENABLE, (ctrl->one_shot_mode_en ? ONE_SHOT_MODE : 0) | TX_MON_DURATION(ilog2(ctrl->tx_mon_duration / 16))); ad9361_spi_write(spi, REG_TX_MON_DELAY, ctrl->tx_mon_delay & 0xFF); ad9361_spi_writef(spi, REG_TX_LEVEL_THRESH, TX_MON_DELAY_COUNTER(~0), ctrl->tx_mon_delay >> 8); ad9361_spi_write(spi, REG_TX_MON_1_CONFIG, TX_MON_1_LO_CM(ctrl->tx1_mon_lo_cm) | TX_MON_1_GAIN(ctrl->tx1_mon_front_end_gain)); ad9361_spi_write(spi, REG_TX_MON_2_CONFIG, TX_MON_2_LO_CM(ctrl->tx2_mon_lo_cm) | TX_MON_2_GAIN(ctrl->tx2_mon_front_end_gain)); ad9361_spi_write(spi, REG_TX_ATTEN_THRESH, ctrl->low_high_gain_threshold_mdB / 250); ad9361_spi_write(spi, REG_TX_MON_HIGH_GAIN, TX_MON_HIGH_GAIN(ctrl->high_gain_dB)); ad9361_spi_write(spi, REG_TX_MON_LOW_GAIN, (ctrl->tx_mon_track_en ? TX_MON_TRACK : 0) | TX_MON_LOW_GAIN(ctrl->low_gain_dB)); return 0; } static int ad9361_txmon_control(struct ad9361_rf_phy *phy, unsigned en_mask) { dev_dbg(&phy->spi->dev, "%s: mask 0x%X", __func__, en_mask); #if 0 if (!phy->pdata->fdd && en_mask) { ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_1, ENABLE_RX_DATA_PORT_FOR_CAL, 1); st->txmon_tdd_en = true; } else { ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_1, ENABLE_RX_DATA_PORT_FOR_CAL, 0); st->txmon_tdd_en = false; } #endif ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, TX_MONITOR_POWER_DOWN(~0), ~en_mask); ad9361_spi_writef(phy->spi, REG_TPM_MODE_ENABLE, TX1_MON_ENABLE, !!(en_mask & TX_1)); return ad9361_spi_writef(phy->spi, REG_TPM_MODE_ENABLE, TX2_MON_ENABLE, !!(en_mask & TX_2)); } /* val * 0 (RX1A_N & RX1A_P) and (RX2A_N & RX2A_P) enabled; balanced * 1 (RX1B_N & RX1B_P) and (RX2B_N & RX2B_P) enabled; balanced * 2 (RX1C_N & RX1C_P) and (RX2C_N & RX2C_P) enabled; balanced * * 3 RX1A_N and RX2A_N enabled; unbalanced * 4 RX1A_P and RX2A_P enabled; unbalanced * 5 RX1B_N and RX2B_N enabled; unbalanced * 6 RX1B_P and RX2B_P enabled; unbalanced * 7 RX1C_N and RX2C_N enabled; unbalanced * 8 RX1C_P and RX2C_P enabled; unbalanced * 9 TX_MON1 * 10 TX_MON2 * 11 TX_MON1 & TX_MON2 */ static int ad9361_rf_port_setup(struct ad9361_rf_phy *phy, bool is_out, u32 rx_inputs, u32 txb) { u32 val; if (rx_inputs > 11) return -EINVAL; if (!is_out) { if (rx_inputs > 8) return ad9361_txmon_control(phy, rx_inputs & (TX_1 | TX_2)); else ad9361_txmon_control(phy, 0); } if (rx_inputs < 3) val = 3 << (rx_inputs * 2); else val = 1 << (rx_inputs - 3); if (txb) val |= TX_OUTPUT; /* Select TX1B, TX2B */ dev_dbg(&phy->spi->dev, "%s : INPUT_SELECT 0x%X", __func__, val); return ad9361_spi_write(phy->spi, REG_INPUT_SELECT, val); } int ad9361_set_rx_port(struct ad9361_rf_phy *phy, enum rx_port_sel sel) { struct ad9361_rf_phy_state *st; if (!phy) return -EINVAL; switch (sel) { case RX_A_BALANCED: /* FALLTHROUGH */ case RX_B_BALANCED: /* FALLTHROUGH */ case RX_C_BALANCED: /* FALLTHROUGH */ case RX_A_N: /* FALLTHROUGH */ case RX_A_P: /* FALLTHROUGH */ case RX_B_N: /* FALLTHROUGH */ case RX_B_P: /* FALLTHROUGH */ case RX_C_N: /* FALLTHROUGH */ case RX_C_P: /* FALLTHROUGH */ case TX_MON1: /* FALLTHROUGH */ case TX_MON2: /* FALLTHROUGH */ case TX_MON1_2: st = phy->state; if (st->rf_rx_input_sel == sel) return 0; st->rf_rx_input_sel = sel; return ad9361_rf_port_setup(phy, false, sel, st->rf_tx_output_sel); default: return -EINVAL; } } EXPORT_SYMBOL(ad9361_set_rx_port); int ad9361_set_tx_port(struct ad9361_rf_phy *phy, enum tx_port_sel sel) { struct ad9361_rf_phy_state *st; if (!phy) return -EINVAL; switch (sel) { case (TX_A): /* FALLTHROUGH */ case (TX_B): st = phy->state; if (st->rf_tx_output_sel == sel) return 0; st->rf_tx_output_sel = sel; return ad9361_rf_port_setup(phy, true, st->rf_rx_input_sel, sel); default: return -EINVAL; } } EXPORT_SYMBOL(ad9361_set_tx_port); /* * Setup the Parallel Port (Digital Data Interface) */ static int ad9361_pp_port_setup(struct ad9361_rf_phy *phy, bool restore_c3) { struct spi_device *spi = phy->spi; struct ad9361_phy_platform_data *pd = phy->pdata; dev_dbg(&phy->spi->dev, "%s", __func__); if (restore_c3) { return ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_3, pd->port_ctrl.pp_conf[2]); } /* Sanity check */ if (pd->port_ctrl.pp_conf[2] & LVDS_MODE) pd->port_ctrl.pp_conf[2] &= ~(HALF_DUPLEX_MODE | SINGLE_DATA_RATE | SINGLE_PORT_MODE); if (pd->port_ctrl.pp_conf[2] & FULL_PORT) pd->port_ctrl.pp_conf[2] &= ~(HALF_DUPLEX_MODE | SINGLE_PORT_MODE); ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_1, pd->port_ctrl.pp_conf[0]); ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_2, pd->port_ctrl.pp_conf[1]); ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_3, pd->port_ctrl.pp_conf[2]); ad9361_write_clock_data_delays(phy); ad9361_spi_write(spi, REG_LVDS_BIAS_CTRL, pd->port_ctrl.lvds_bias_ctrl); // ad9361_spi_write(spi, REG_DIGITAL_IO_CTRL, pd->port_ctrl.digital_io_ctrl); ad9361_spi_write(spi, REG_LVDS_INVERT_CTRL1, pd->port_ctrl.lvds_invert[0]); ad9361_spi_write(spi, REG_LVDS_INVERT_CTRL2, pd->port_ctrl.lvds_invert[1]); if (pd->rx1rx2_phase_inversion_en || (pd->port_ctrl.pp_conf[1] & INVERT_RX2)) { ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 1); ad9361_spi_writef(spi, REG_INVERT_BITS, INVERT_RX2_RF_DC_CGOUT_WORD, 0); } return 0; } static int ad9361_gc_setup(struct ad9361_rf_phy *phy, struct gain_control *ctrl) { struct ad9361_rf_phy_state *st = phy->state; struct spi_device *spi = phy->spi; u32 reg, tmp1, tmp2; dev_dbg(&phy->spi->dev, "%s", __func__); reg = DEC_PWR_FOR_GAIN_LOCK_EXIT | DEC_PWR_FOR_LOCK_LEVEL | DEC_PWR_FOR_LOW_PWR; if (ctrl->rx1_mode == RF_GAIN_HYBRID_AGC || ctrl->rx2_mode == RF_GAIN_HYBRID_AGC) reg |= SLOW_ATTACK_HYBRID_MODE; reg |= RX1_GAIN_CTRL_SETUP(ctrl->rx1_mode) | RX2_GAIN_CTRL_SETUP(ctrl->rx2_mode); st->agc_mode[0] = ctrl->rx1_mode; st->agc_mode[1] = ctrl->rx2_mode; ad9361_spi_write(spi, REG_AGC_CONFIG_1, reg); // Gain Control Mode Select /* AGC_USE_FULL_GAIN_TABLE handled in ad9361_load_gt() */ ad9361_spi_writef(spi, REG_AGC_CONFIG_2, MAN_GAIN_CTRL_RX1, ctrl->mgc_rx1_ctrl_inp_en); ad9361_spi_writef(spi, REG_AGC_CONFIG_2, MAN_GAIN_CTRL_RX2, ctrl->mgc_rx2_ctrl_inp_en); ad9361_spi_writef(spi, REG_AGC_CONFIG_2, DIG_GAIN_EN, ctrl->dig_gain_en); ctrl->adc_ovr_sample_size = clamp_t(u8, ctrl->adc_ovr_sample_size, 1U, 8U); reg = ADC_OVERRANGE_SAMPLE_SIZE(ctrl->adc_ovr_sample_size - 1); if (phy->pdata->split_gt && (ctrl->mgc_rx1_ctrl_inp_en || ctrl->mgc_rx2_ctrl_inp_en)) { switch (ctrl->mgc_split_table_ctrl_inp_gain_mode) { case 1: reg &= ~INCDEC_LMT_GAIN; break; case 2: reg |= INCDEC_LMT_GAIN; break; default: case 0: reg |= USE_AGC_FOR_LMTLPF_GAIN; break; } } ctrl->mgc_inc_gain_step = clamp_t(u8, ctrl->mgc_inc_gain_step, 1U, 8U); reg |= MANUAL_INCR_STEP_SIZE(ctrl->mgc_inc_gain_step - 1); ad9361_spi_write(spi, REG_AGC_CONFIG_3, reg); // Incr Step Size, ADC Overrange Size ctrl->mgc_dec_gain_step = clamp_t(u8, ctrl->mgc_dec_gain_step, 1U, 8U); reg = MANUAL_CTRL_IN_DECR_GAIN_STP_SIZE(ctrl->mgc_dec_gain_step - 1); ad9361_spi_write(spi, REG_PEAK_WAIT_TIME, reg); // Decr Step Size, Peak Overload Time if (ctrl->dig_gain_en) ad9361_spi_write(spi, REG_DIGITAL_GAIN, MAXIMUM_DIGITAL_GAIN(ctrl->max_dig_gain) | DIG_GAIN_STP_SIZE(ctrl->dig_gain_step_size)); if (ctrl->adc_large_overload_thresh >= ctrl->adc_small_overload_thresh) { ad9361_spi_write(spi, REG_ADC_SMALL_OVERLOAD_THRESH, ctrl->adc_small_overload_thresh); // ADC Small Overload Threshold ad9361_spi_write(spi, REG_ADC_LARGE_OVERLOAD_THRESH, ctrl->adc_large_overload_thresh); // ADC Large Overload Threshold } else { ad9361_spi_write(spi, REG_ADC_SMALL_OVERLOAD_THRESH, ctrl->adc_large_overload_thresh); // ADC Small Overload Threshold ad9361_spi_write(spi, REG_ADC_LARGE_OVERLOAD_THRESH, ctrl->adc_small_overload_thresh); // ADC Large Overload Threshold } reg = (ctrl->lmt_overload_high_thresh / 16) - 1; reg = clamp(reg, 0U, 63U); ad9361_spi_write(spi, REG_LARGE_LMT_OVERLOAD_THRESH, reg); reg = (ctrl->lmt_overload_low_thresh / 16) - 1; reg = clamp(reg, 0U, 63U); ad9361_spi_writef(spi, REG_SMALL_LMT_OVERLOAD_THRESH, SMALL_LMT_OVERLOAD_THRESH(~0), reg); if (phy->pdata->split_gt) { /* REVIST */ ad9361_spi_write(spi, REG_RX1_MANUAL_LPF_GAIN, 0x58); // Rx1 LPF Gain Index ad9361_spi_write(spi, REG_RX2_MANUAL_LPF_GAIN, 0x18); // Rx2 LPF Gain Index ad9361_spi_write(spi, REG_FAST_INITIAL_LMT_GAIN_LIMIT, 0x27); // Initial LMT Gain Limit } ad9361_spi_write(spi, REG_RX1_MANUAL_DIGITALFORCED_GAIN, 0x00); // Rx1 Digital Gain Index ad9361_spi_write(spi, REG_RX2_MANUAL_DIGITALFORCED_GAIN, 0x00); // Rx2 Digital Gain Index reg = clamp_t(u8, ctrl->low_power_thresh, 0U, 64U) * 2; ad9361_spi_write(spi, REG_FAST_LOW_POWER_THRESH, reg); // Low Power Threshold ad9361_spi_write(spi, REG_TX_SYMBOL_ATTEN_CONFIG, 0x00); // Tx Symbol Gain Control ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, USE_HB1_OUT_FOR_DEC_PWR_MEAS, !ctrl->use_rx_fir_out_for_dec_pwr_meas); // USE HB1 or FIR output for power measurements ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, ENABLE_DEC_PWR_MEAS, 1); // Power Measurement Duration if (ctrl->rx1_mode == RF_GAIN_FASTATTACK_AGC || ctrl->rx2_mode == RF_GAIN_FASTATTACK_AGC) reg = ilog2(ctrl->f_agc_dec_pow_measuremnt_duration / 16); else reg = ilog2(ctrl->dec_pow_measuremnt_duration / 16); ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, DEC_POWER_MEASUREMENT_DURATION(~0), reg); // Power Measurement Duration /* AGC */ tmp1 = reg = clamp_t(u8, ctrl->agc_inner_thresh_high, 0U, 127U); ad9361_spi_writef(spi, REG_AGC_LOCK_LEVEL, AGC_LOCK_LEVEL_FAST_AGC_INNER_HIGH_THRESH_SLOW(~0), reg); tmp2 = reg = clamp_t(u8, ctrl->agc_inner_thresh_low, 0U, 127U); reg |= (ctrl->adc_lmt_small_overload_prevent_gain_inc ? PREVENT_GAIN_INC : 0); ad9361_spi_write(spi, REG_AGC_INNER_LOW_THRESH, reg); reg = AGC_OUTER_HIGH_THRESH(tmp1 - ctrl->agc_outer_thresh_high) | AGC_OUTER_LOW_THRESH(ctrl->agc_outer_thresh_low - tmp2); ad9361_spi_write(spi, REG_OUTER_POWER_THRESHS, reg); reg = AGC_OUTER_HIGH_THRESH_EXED_STP_SIZE(ctrl->agc_outer_thresh_high_dec_steps) | AGC_OUTER_LOW_THRESH_EXED_STP_SIZE(ctrl->agc_outer_thresh_low_inc_steps); ad9361_spi_write(spi, REG_GAIN_STP_2, reg); reg = ((ctrl->immed_gain_change_if_large_adc_overload) ? IMMED_GAIN_CHANGE_IF_LG_ADC_OVERLOAD : 0) | ((ctrl->immed_gain_change_if_large_lmt_overload) ? IMMED_GAIN_CHANGE_IF_LG_LMT_OVERLOAD : 0) | AGC_INNER_HIGH_THRESH_EXED_STP_SIZE(ctrl->agc_inner_thresh_high_dec_steps) | AGC_INNER_LOW_THRESH_EXED_STP_SIZE(ctrl->agc_inner_thresh_low_inc_steps); ad9361_spi_write(spi, REG_GAIN_STP1, reg); reg = LARGE_ADC_OVERLOAD_EXED_COUNTER(ctrl->adc_large_overload_exceed_counter) | SMALL_ADC_OVERLOAD_EXED_COUNTER(ctrl->adc_small_overload_exceed_counter); ad9361_spi_write(spi, REG_ADC_OVERLOAD_COUNTERS, reg); reg = DECREMENT_STP_SIZE_FOR_SMALL_LPF_GAIN_CHANGE(ctrl->f_agc_large_overload_inc_steps) | LARGE_LPF_GAIN_STEP(ctrl->adc_large_overload_inc_steps); ad9361_spi_write(spi, REG_GAIN_STP_CONFIG_2, reg); reg = LARGE_LMT_OVERLOAD_EXED_COUNTER(ctrl->lmt_overload_large_exceed_counter) | SMALL_LMT_OVERLOAD_EXED_COUNTER(ctrl->lmt_overload_small_exceed_counter); ad9361_spi_write(spi, REG_LMT_OVERLOAD_COUNTERS, reg); ad9361_spi_writef(spi, REG_GAIN_STP_CONFIG1, DEC_STP_SIZE_FOR_LARGE_LMT_OVERLOAD(~0), ctrl->lmt_overload_large_inc_steps); reg = DIG_SATURATION_EXED_COUNTER(ctrl->dig_saturation_exceed_counter) | (ctrl->sync_for_gain_counter_en ? ENABLE_SYNC_FOR_GAIN_COUNTER : 0); ad9361_spi_write(spi, REG_DIGITAL_SAT_COUNTER, reg); /* * Fast AGC */ /* Fast AGC - Low Power */ ad9361_spi_writef(spi, REG_FAST_CONFIG_1, ENABLE_INCR_GAIN, ctrl->f_agc_allow_agc_gain_increase); ad9361_spi_write(spi, REG_FAST_INCREMENT_TIME, ctrl->f_agc_lp_thresh_increment_time); reg = ctrl->f_agc_lp_thresh_increment_steps - 1; reg = clamp_t(u32, reg, 0U, 7U); ad9361_spi_writef(spi, REG_FAST_ENERGY_DETECT_COUNT, INCREMENT_GAIN_STP_LPFLMT(~0), reg); /* Fast AGC - Lock Level */ /* Dual use see also agc_inner_thresh_high */ ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, ENABLE_LMT_GAIN_INC_FOR_LOCK_LEVEL, ctrl->f_agc_lock_level_lmt_gain_increase_en); reg = ctrl->f_agc_lock_level_gain_increase_upper_limit; reg = clamp_t(u32, reg, 0U, 63U); ad9361_spi_writef(spi, REG_FAST_AGCLL_UPPER_LIMIT, AGCLL_MAX_INCREASE(~0), reg); /* Fast AGC - Peak Detectors and Final Settling */ reg = ctrl->f_agc_lpf_final_settling_steps; reg = clamp_t(u32, reg, 0U, 3U); ad9361_spi_writef(spi, REG_FAST_ENERGY_LOST_THRESH, POST_LOCK_LEVEL_STP_SIZE_FOR_LPF_TABLE_FULL_TABLE(~0), reg); reg = ctrl->f_agc_lmt_final_settling_steps; reg = clamp_t(u32, reg, 0U, 3U); ad9361_spi_writef(spi, REG_FAST_STRONGER_SIGNAL_THRESH, POST_LOCK_LEVEL_STP_FOR_LMT_TABLE(~0), reg); reg = ctrl->f_agc_final_overrange_count; reg = clamp_t(u32, reg, 0U, 7U); ad9361_spi_writef(spi, REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN, FINAL_OVER_RANGE_COUNT(~0), reg); /* Fast AGC - Final Power Test */ ad9361_spi_writef(spi, REG_FAST_CONFIG_1, ENABLE_GAIN_INC_AFTER_GAIN_LOCK, ctrl->f_agc_gain_increase_after_gain_lock_en); /* Fast AGC - Unlocking the Gain */ /* 0 = MAX Gain, 1 = Optimized Gain, 2 = Set Gain */ reg = ctrl->f_agc_gain_index_type_after_exit_rx_mode; ad9361_spi_writef(spi, REG_FAST_CONFIG_1, GOTO_SET_GAIN_IF_EXIT_RX_STATE, reg == SET_GAIN); ad9361_spi_writef(spi, REG_FAST_CONFIG_1, GOTO_OPTIMIZED_GAIN_IF_EXIT_RX_STATE, reg == OPTIMIZED_GAIN); ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, USE_LAST_LOCK_LEVEL_FOR_SET_GAIN, ctrl->f_agc_use_last_lock_level_for_set_gain_en); reg = ctrl->f_agc_optimized_gain_offset; reg = clamp_t(u32, reg, 0U, 15U); ad9361_spi_writef(spi, REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN, OPTIMIZE_GAIN_OFFSET(~0), reg); tmp1 = !ctrl->f_agc_rst_gla_stronger_sig_thresh_exceeded_en || !ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en || !ctrl->f_agc_rst_gla_large_adc_overload_en || !ctrl->f_agc_rst_gla_large_lmt_overload_en || ctrl->f_agc_rst_gla_en_agc_pulled_high_en; ad9361_spi_writef(spi, REG_AGC_CONFIG_2, AGC_GAIN_UNLOCK_CTRL, tmp1); reg = !ctrl->f_agc_rst_gla_stronger_sig_thresh_exceeded_en; ad9361_spi_writef(spi, REG_FAST_STRONG_SIGNAL_FREEZE, DONT_UNLOCK_GAIN_IF_STRONGER_SIGNAL, reg); reg = ctrl->f_agc_rst_gla_stronger_sig_thresh_above_ll; reg = clamp_t(u32, reg, 0U, 63U); ad9361_spi_writef(spi, REG_FAST_STRONGER_SIGNAL_THRESH, STRONGER_SIGNAL_THRESH(~0), reg); reg = ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_below_ll; reg = clamp_t(u32, reg, 0U, 63U); ad9361_spi_writef(spi, REG_FAST_ENERGY_LOST_THRESH, ENERGY_LOST_THRESH(~0), reg); reg = ctrl->f_agc_rst_gla_engergy_lost_goto_optim_gain_en; ad9361_spi_writef(spi, REG_FAST_CONFIG_1, GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, reg); reg = !ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en; ad9361_spi_writef(spi, REG_FAST_CONFIG_1, DONT_UNLOCK_GAIN_IF_ENERGY_LOST, reg); reg = ctrl->f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt; reg = clamp_t(u32, reg, 0U, 63U); ad9361_spi_writef(spi, REG_FAST_GAIN_LOCK_EXIT_COUNT, GAIN_LOCK_EXIT_COUNT(~0), reg); reg = !ctrl->f_agc_rst_gla_large_adc_overload_en || !ctrl->f_agc_rst_gla_large_lmt_overload_en; ad9361_spi_writef(spi, REG_FAST_CONFIG_1, DONT_UNLOCK_GAIN_IF_LG_ADC_OR_LMT_OVRG, reg); reg = !ctrl->f_agc_rst_gla_large_adc_overload_en; ad9361_spi_writef(spi, REG_FAST_LOW_POWER_THRESH, DONT_UNLOCK_GAIN_IF_ADC_OVRG, reg); /* 0 = Max Gain, 1 = Set Gain, 2 = Optimized Gain, 3 = No Gain Change */ if (ctrl->f_agc_rst_gla_en_agc_pulled_high_en) { switch (ctrl->f_agc_rst_gla_if_en_agc_pulled_high_mode) { case MAX_GAIN: ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 1); ad9361_spi_writef(spi, REG_FAST_CONFIG_1, GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); ad9361_spi_writef(spi, REG_FAST_CONFIG_1, GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, 0); break; case SET_GAIN: ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0); ad9361_spi_writef(spi, REG_FAST_CONFIG_1, GOTO_SET_GAIN_IF_EN_AGC_HIGH, 1); break; case OPTIMIZED_GAIN: ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 1); ad9361_spi_writef(spi, REG_FAST_CONFIG_1, GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); ad9361_spi_writef(spi, REG_FAST_CONFIG_1, GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, 1); break; case NO_GAIN_CHANGE: ad9361_spi_writef(spi, REG_FAST_CONFIG_1, GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0); break; } } else { ad9361_spi_writef(spi, REG_FAST_CONFIG_1, GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0); } reg = ilog2(ctrl->f_agc_power_measurement_duration_in_state5 / 16); reg = clamp_t(u32, reg, 0U, 15U); ad9361_spi_writef(spi, REG_RX1_MANUAL_LPF_GAIN, POWER_MEAS_IN_STATE_5(~0), reg); ad9361_spi_writef(spi, REG_RX1_MANUAL_LMT_FULL_GAIN, POWER_MEAS_IN_STATE_5_MSB, reg >> 3); return ad9361_gc_update(phy); } static int ad9361_auxdac_set(struct ad9361_rf_phy *phy, unsigned dac, unsigned val_mV) { struct ad9361_rf_phy_state *st = phy->state; struct spi_device *spi = phy->spi; u32 val, tmp; dev_dbg(&phy->spi->dev, "%s DAC%d = %d mV", __func__, dac, val_mV); /* Disable DAC if val == 0, Ignored in ENSM Auto Mode */ ad9361_spi_writef(spi, REG_AUXDAC_ENABLE_CTRL, AUXDAC_MANUAL_BAR(dac), val_mV ? 0 : 1); if (val_mV < 306) val_mV = 306; if (val_mV < 1888) { val = ((val_mV - 306) * 1000) / 1469; /* Vref = 1V, Step = 2 */ tmp = AUXDAC_1_VREF(0); } else { val = ((val_mV - 1761) * 1000) / 1512; /* Vref = 2.5V, Step = 2 */ tmp = AUXDAC_1_VREF(3); } val = clamp_t(u32, val, 0, 1023); switch (dac) { case 1: ad9361_spi_write(spi, REG_AUXDAC_1_WORD, val >> 2); ad9361_spi_write(spi, REG_AUXDAC_1_CONFIG, AUXDAC_1_WORD_LSB(val) | tmp); st->auxdac1_value = val_mV; break; case 2: ad9361_spi_write(spi, REG_AUXDAC_2_WORD, val >> 2); ad9361_spi_write(spi, REG_AUXDAC_2_CONFIG, AUXDAC_2_WORD_LSB(val) | tmp); st->auxdac2_value = val_mV; break; default: return -EINVAL; } return 0; } static int ad9361_auxdac_get(struct ad9361_rf_phy *phy, unsigned dac) { struct ad9361_rf_phy_state *st = phy->state; switch (dac) { case 1: return st->auxdac1_value; case 2: return st->auxdac2_value; default: return -EINVAL; } return 0; } //************************************************************ // Setup AuxDAC //************************************************************ static int ad9361_auxdac_setup(struct ad9361_rf_phy *phy, struct auxdac_control *ctrl) { struct spi_device *spi = phy->spi; u8 tmp; dev_dbg(&phy->spi->dev, "%s", __func__); ad9361_auxdac_set(phy, 1, ctrl->dac1_default_value); ad9361_auxdac_set(phy, 2, ctrl->dac2_default_value); tmp = ~(AUXDAC_AUTO_TX_BAR(ctrl->dac2_in_tx_en << 1 | ctrl->dac1_in_tx_en) | AUXDAC_AUTO_RX_BAR(ctrl->dac2_in_rx_en << 1 | ctrl->dac1_in_rx_en) | AUXDAC_INIT_BAR(ctrl->dac2_in_alert_en << 1 | ctrl->dac1_in_alert_en)); ad9361_spi_writef(spi, REG_AUXDAC_ENABLE_CTRL, AUXDAC_AUTO_TX_BAR(~0) | AUXDAC_AUTO_RX_BAR(~0) | AUXDAC_INIT_BAR(~0), tmp); /* Auto Control */ ad9361_spi_writef(spi, REG_EXTERNAL_LNA_CTRL, AUXDAC_MANUAL_SELECT, ctrl->auxdac_manual_mode_en); ad9361_spi_write(spi, REG_AUXDAC1_RX_DELAY, ctrl->dac1_rx_delay_us); ad9361_spi_write(spi, REG_AUXDAC1_TX_DELAY, ctrl->dac1_tx_delay_us); ad9361_spi_write(spi, REG_AUXDAC2_RX_DELAY, ctrl->dac2_rx_delay_us); ad9361_spi_write(spi, REG_AUXDAC2_TX_DELAY, ctrl->dac2_tx_delay_us); return 0; } //************************************************************ // Setup AuxADC //************************************************************ static int ad9361_auxadc_setup(struct ad9361_rf_phy *phy, struct auxadc_control *ctrl, unsigned long bbpll_freq) { struct spi_device *spi = phy->spi; u32 val; dev_dbg(&phy->spi->dev, "%s", __func__); val = DIV_ROUND_CLOSEST(ctrl->temp_time_inteval_ms * (bbpll_freq / 1000UL), (1 << 29)); ad9361_spi_write(spi, REG_TEMP_OFFSET, ctrl->offset); ad9361_spi_write(spi, REG_START_TEMP_READING, 0x00); ad9361_spi_write(spi, REG_TEMP_SENSE2, MEASUREMENT_TIME_INTERVAL(val) | (ctrl->periodic_temp_measuremnt ? TEMP_SENSE_PERIODIC_ENABLE : 0)); ad9361_spi_write(spi, REG_TEMP_SENSOR_CONFIG, TEMP_SENSOR_DECIMATION( ilog2(ctrl->temp_sensor_decimation) - 8)); ad9361_spi_write(spi, REG_AUXADC_CLOCK_DIVIDER, bbpll_freq / ctrl->auxadc_clock_rate); ad9361_spi_write(spi, REG_AUXADC_CONFIG, AUX_ADC_DECIMATION( ilog2(ctrl->auxadc_decimation) - 8)); return 0; } static int ad9361_get_temp(struct ad9361_rf_phy *phy) { s32 val; ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 1); val = (s8) ad9361_spi_read(phy->spi, REG_TEMPERATURE); ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 0); return DIV_ROUND_CLOSEST(val * 1000000, 1140); } static int ad9361_get_auxadc(struct ad9361_rf_phy *phy) { u32 val; u8 buf[2]; ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 1); val = ad9361_spi_readm(phy->spi, REG_AUXADC_LSB, buf, 2); ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 0); return (buf[1] << 4) | AUXADC_WORD_LSB(buf[0]); } //************************************************************ // Setup Control Outs //************************************************************ int ad9361_ctrl_outs_setup(struct ad9361_rf_phy *phy, struct ctrl_outs_control *ctrl) { struct spi_device *spi = phy->spi; dev_dbg(&phy->spi->dev, "%s", __func__); ad9361_spi_write(spi, REG_CTRL_OUTPUT_POINTER, ctrl->index); // Ctrl Out index return ad9361_spi_write(spi, REG_CTRL_OUTPUT_ENABLE, ctrl->en_mask); // Ctrl Out [7:0] output enable } EXPORT_SYMBOL(ad9361_ctrl_outs_setup); //************************************************************ // Setup GPO //************************************************************ static int ad9361_gpo_setup(struct ad9361_rf_phy *phy, struct gpo_control *ctrl) { struct spi_device *spi = phy->spi; dev_dbg(&phy->spi->dev, "%s", __func__); ad9361_spi_write(spi, REG_AUTO_GPO, GPO_ENABLE_AUTO_RX(ctrl->gpo0_slave_rx_en | (ctrl->gpo1_slave_rx_en << 1) | (ctrl->gpo2_slave_rx_en << 2) | (ctrl->gpo3_slave_rx_en << 3)) | GPO_ENABLE_AUTO_TX(ctrl->gpo0_slave_tx_en | (ctrl->gpo1_slave_tx_en << 1) | (ctrl->gpo2_slave_tx_en << 2) | (ctrl->gpo3_slave_tx_en << 3))); ad9361_spi_write(spi, REG_GPO_FORCE_AND_INIT, GPO_MANUAL_CTRL(ctrl->gpo_manual_mode_enable_mask) | GPO_INIT_STATE(ctrl->gpo0_inactive_state_high_en | (ctrl->gpo1_inactive_state_high_en << 1) | (ctrl->gpo2_inactive_state_high_en << 2) | (ctrl->gpo3_inactive_state_high_en << 3))); ad9361_spi_write(spi, REG_GPO0_RX_DELAY, ctrl->gpo0_rx_delay_us); ad9361_spi_write(spi, REG_GPO0_TX_DELAY, ctrl->gpo0_tx_delay_us); ad9361_spi_write(spi, REG_GPO1_RX_DELAY, ctrl->gpo1_rx_delay_us); ad9361_spi_write(spi, REG_GPO1_TX_DELAY, ctrl->gpo1_tx_delay_us); ad9361_spi_write(spi, REG_GPO2_RX_DELAY, ctrl->gpo2_rx_delay_us); ad9361_spi_write(spi, REG_GPO2_TX_DELAY, ctrl->gpo2_tx_delay_us); ad9361_spi_write(spi, REG_GPO3_RX_DELAY, ctrl->gpo3_rx_delay_us); ad9361_spi_write(spi, REG_GPO3_TX_DELAY, ctrl->gpo3_tx_delay_us); /* * GPO manual mode conflicts with automatic ENSM slave and eLNA mode */ ad9361_spi_writef(phy->spi, REG_EXTERNAL_LNA_CTRL, GPO_MANUAL_SELECT, ctrl->gpo_manual_mode_en); return 0; } int ad9361_rssi_setup(struct ad9361_rf_phy *phy, struct rssi_control *ctrl, bool is_update) { struct spi_device *spi = phy->spi; u32 total_weight, weight[4], total_dur = 0, temp; u8 dur_buf[4] = {0}; int val, ret, i, j = 0; u32 rssi_delay; u32 rssi_wait; s32 rssi_duration; unsigned long rate; dev_dbg(&phy->spi->dev, "%s", __func__); if (ctrl->rssi_unit_is_rx_samples) { if (is_update) return 0; /* no update required */ rssi_delay = ctrl->rssi_delay; rssi_wait = ctrl->rssi_wait; rssi_duration = ctrl->rssi_duration; } else { /* update sample based on RX rate */ rate = DIV_ROUND_CLOSEST( clk_get_rate(phy->clks[RX_SAMPL_CLK]), 1000); /* units are in us */ rssi_delay = DIV_ROUND_CLOSEST(ctrl->rssi_delay * rate, 1000); rssi_wait = DIV_ROUND_CLOSEST(ctrl->rssi_wait * rate, 1000); rssi_duration = DIV_ROUND_CLOSEST( ctrl->rssi_duration * rate, 1000); } if (ctrl->restart_mode == EN_AGC_PIN_IS_PULLED_HIGH) rssi_delay = 0; rssi_delay = clamp(rssi_delay / 8, 0U, 255U); rssi_wait = clamp(rssi_wait / 4, 0U, 255U); do { for (i = 14; rssi_duration > 0 && i >= 0 ; i--) { val = 1 << i; if (rssi_duration >= val) { dur_buf[j++] = i; total_dur += val; rssi_duration -= val; break; } } } while (j < 4 && rssi_duration > 0); for (i = 0, total_weight = 0; i < 4; i++) { if (i < j) total_weight += weight[i] = DIV_ROUND_CLOSEST(RSSI_MAX_WEIGHT * (1 << dur_buf[i]), total_dur); else total_weight += weight[i] = 0; } /* total of all weights must be 0xFF */ val = total_weight - 0xFF; weight[j - 1] -= val; ad9361_spi_write(spi, REG_MEASURE_DURATION_01, (dur_buf[1] << 4) | dur_buf[0]); // RSSI Measurement Duration 0, 1 ad9361_spi_write(spi, REG_MEASURE_DURATION_23, (dur_buf[3] << 4) | dur_buf[2]); // RSSI Measurement Duration 2, 3 ad9361_spi_write(spi, REG_RSSI_WEIGHT_0, weight[0]); // RSSI Weighted Multiplier 0 ad9361_spi_write(spi, REG_RSSI_WEIGHT_1, weight[1]); // RSSI Weighted Multiplier 1 ad9361_spi_write(spi, REG_RSSI_WEIGHT_2, weight[2]); // RSSI Weighted Multiplier 2 ad9361_spi_write(spi, REG_RSSI_WEIGHT_3, weight[3]); // RSSI Weighted Multiplier 3 ad9361_spi_write(spi, REG_RSSI_DELAY, rssi_delay); // RSSI Delay ad9361_spi_write(spi, REG_RSSI_WAIT_TIME, rssi_wait); // RSSI Wait temp = RSSI_MODE_SELECT(ctrl->restart_mode); if (ctrl->restart_mode == SPI_WRITE_TO_REGISTER) temp |= START_RSSI_MEAS; if (rssi_duration == 0 && j == 1) /* Power of two */ temp |= DEFAULT_RSSI_MEAS_MODE; ret = ad9361_spi_write(spi, REG_RSSI_CONFIG, temp); // RSSI Mode Select if (ret < 0) dev_err(&phy->spi->dev, "Unable to write rssi config\n"); return 0; } EXPORT_SYMBOL(ad9361_rssi_setup); static int ad9361_bb_clk_change_handler(struct ad9361_rf_phy *phy) { int ret; ret = ad9361_gc_update(phy); ret |= ad9361_rssi_setup(phy, &phy->pdata->rssi_ctrl, true); ret |= ad9361_auxadc_setup(phy, &phy->pdata->auxadc_ctrl, clk_get_rate(phy->clks[BBPLL_CLK])); return ret; } static int ad9361_ensm_set_state(struct ad9361_rf_phy *phy, u8 ensm_state, bool pinctrl) { struct ad9361_rf_phy_state *st = phy->state; struct spi_device *spi = phy->spi; struct device *dev = &phy->spi->dev; int rc = 0; u32 val; dev_dbg(dev, "Device is in %x state, moving to %x\n", st->curr_ensm_state, ensm_state); if (st->curr_ensm_state == ENSM_STATE_SLEEP) { ad9361_spi_write(spi, REG_CLOCK_ENABLE, DIGITAL_POWER_UP | CLOCK_ENABLE_DFLT | BBPLL_ENABLE | (phy->pdata->use_extclk ? XO_BYPASS : 0)); /* Enable Clocks */ udelay(20); ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE); ad9361_trx_vco_cal_control(phy, false, true); /* Enable VCO Cal */ ad9361_trx_vco_cal_control(phy, true, true); } val = (phy->pdata->ensm_pin_pulse_mode ? 0 : LEVEL_MODE) | (pinctrl ? ENABLE_ENSM_PIN_CTRL : 0) | (st->txmon_tdd_en ? ENABLE_RX_DATA_PORT_FOR_CAL : 0) | TO_ALERT; switch (ensm_state) { case ENSM_STATE_TX: val |= FORCE_TX_ON; if (phy->pdata->fdd) rc = -EINVAL; else if (st->curr_ensm_state != ENSM_STATE_ALERT) rc = -EINVAL; break; case ENSM_STATE_RX: val |= FORCE_RX_ON; if (phy->pdata->fdd) rc = -EINVAL; else if (st->curr_ensm_state != ENSM_STATE_ALERT) rc = -EINVAL; break; case ENSM_STATE_FDD: val |= FORCE_TX_ON; if (!phy->pdata->fdd) rc = -EINVAL; break; case ENSM_STATE_ALERT: val &= ~(FORCE_TX_ON | FORCE_RX_ON); val |= TO_ALERT | FORCE_ALERT_STATE; break; case ENSM_STATE_SLEEP_WAIT: break; case ENSM_STATE_SLEEP: ad9361_trx_vco_cal_control(phy, false, false); /* Disable VCO Cal */ ad9361_trx_vco_cal_control(phy, true, false); ad9361_spi_write(spi, REG_ENSM_CONFIG_1, 0); /* Clear To Alert */ ad9361_spi_write(spi, REG_ENSM_CONFIG_1, phy->pdata->fdd ? FORCE_TX_ON : FORCE_RX_ON); /* Delay Flush Time 384 ADC clock cycles */ udelay(384000000UL / clk_get_rate(phy->clks[ADC_CLK])); ad9361_spi_write(spi, REG_ENSM_CONFIG_1, 0); /* Move to Wait*/ udelay(1); /* Wait for ENSM settle */ ad9361_spi_write(spi, REG_CLOCK_ENABLE, (phy->pdata->use_extclk ? XO_BYPASS : 0)); /* Turn off all clocks */ st->curr_ensm_state = ensm_state; return 0; default: dev_err(dev, "No handling for forcing %d ensm state\n", ensm_state); goto out; } if (rc) { if ((st->curr_ensm_state != ENSM_STATE_ALERT) && (val & (FORCE_RX_ON | FORCE_TX_ON))) { u32 val2 = val; val2 &= ~(FORCE_TX_ON | FORCE_RX_ON); val2 |= TO_ALERT | FORCE_ALERT_STATE; ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val2); ad9361_check_cal_done(phy, REG_STATE, ENSM_STATE(~0), ENSM_STATE_ALERT); } else { dev_err(dev, "Invalid ENSM state transition in %s mode\n", phy->pdata->fdd ? "FDD" : "TDD"); goto out; } } if (!phy->pdata->fdd && !pinctrl && !phy->pdata->tdd_use_dual_synth && (ensm_state == ENSM_STATE_TX || ensm_state == ENSM_STATE_RX)) { u32 reg, check; if (ensm_state == ENSM_STATE_TX) { reg = REG_TX_CP_OVERRANGE_VCO_LOCK; check = !(st->cached_synth_pd[0] & TX_SYNTH_VCO_POWER_DOWN); } else { reg = REG_RX_CP_OVERRANGE_VCO_LOCK; check = !(st->cached_synth_pd[1] & RX_SYNTH_VCO_POWER_DOWN); } ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, TXNRX_SPI_CTRL, ensm_state == ENSM_STATE_TX); if (check) ad9361_check_cal_done(phy, reg, VCO_LOCK, 1); } rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); if (rc) dev_err(dev, "Failed to restore state\n"); if ((val & FORCE_RX_ON) && (st->agc_mode[0] == RF_GAIN_MGC || st->agc_mode[1] == RF_GAIN_MGC)) { u32 tmp = ad9361_spi_read(spi, REG_SMALL_LMT_OVERLOAD_THRESH); ad9361_spi_write(spi, REG_SMALL_LMT_OVERLOAD_THRESH, (tmp & SMALL_LMT_OVERLOAD_THRESH(~0)) | (st->agc_mode[0] == RF_GAIN_MGC ? FORCE_PD_RESET_RX1 : 0) | (st->agc_mode[1] == RF_GAIN_MGC ? FORCE_PD_RESET_RX2 : 0)); ad9361_spi_write(spi, REG_SMALL_LMT_OVERLOAD_THRESH, tmp & SMALL_LMT_OVERLOAD_THRESH(~0)); } st->curr_ensm_state = ensm_state; out: return rc; } static int ad9361_validate_trx_clock_chain(struct ad9361_rf_phy *phy, unsigned long *rx_path_clks, unsigned long *tx_path_clks) { static const unsigned long max_rx_rates[] = {MAX_BBPLL_FREQ, MAX_ADC_CLK, MAX_RX_HB3, MAX_RX_HB2, MAX_RX_HB1, MAX_BASEBAND_RATE}; static const unsigned long max_tx_rates[] = {MAX_BBPLL_FREQ, MAX_DAC_CLK, MAX_TX_HB3, MAX_TX_HB2, MAX_TX_HB1, MAX_BASEBAND_RATE}; int i, data_clk; data_clk = (phy->pdata->rx2tx2 ? 4 : 2) / ((phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) ? 1 : 2) * rx_path_clks[RX_SAMPL_FREQ]; /* CMOS Mode */ if (!(phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) && (data_clk > MAX_BASEBAND_RATE)) { dev_err(&phy->spi->dev, "%s: Failed CMOS MODE DATA_CLK > 61.44MSPS", __func__); return -EINVAL; } /* Validate MAX PLL, ADC, DAC and HB filter rates */ for (i = 0; i < ARRAY_SIZE(max_rx_rates); i++) { if (rx_path_clks[i] > max_rx_rates[i]) { dev_err(&phy->spi->dev, "%s: Failed RX max rate check (%lu > %lu)", __func__, rx_path_clks[i], max_rx_rates[i]); return -EINVAL; } if (tx_path_clks[i] > max_tx_rates[i]) { dev_err(&phy->spi->dev, "%s: Failed TX max rate check (%lu > %lu)", __func__, tx_path_clks[i], max_tx_rates[i]); return -EINVAL; } } /* Validate that DATA_CLK exist within the clock chain */ for (i = 1; i <= 3; i++) { if (abs(rx_path_clks[ADC_FREQ] / i - data_clk) < 4) return 0; } for (i = 1; i <= 4; i++) { if (abs((rx_path_clks[R2_FREQ] >> i) - data_clk) < 4) return 0; } dev_err(&phy->spi->dev, "%s: Failed - at least one of the clock rates" " must be equal to the DATA_CLK (lvds) rate", __func__); return -EINVAL; } int ad9361_clk_set_rate(struct clk *clk, unsigned long rate) { clk_set_rate(clk, rate); return 0; } EXPORT_SYMBOL(ad9361_clk_set_rate); static int ad9361_set_trx_clock_chain(struct ad9361_rf_phy *phy, unsigned long *rx_path_clks, unsigned long *tx_path_clks) { struct device *dev = &phy->spi->dev; struct ad9361_rf_phy_state *st = phy->state; int ret, i, j, n; dev_dbg(&phy->spi->dev, "%s", __func__); if (!rx_path_clks || !tx_path_clks) return -EINVAL; dev_dbg(&phy->spi->dev, "%s: %lu %lu %lu %lu %lu %lu", __func__, rx_path_clks[BBPLL_FREQ], rx_path_clks[ADC_FREQ], rx_path_clks[R2_FREQ], rx_path_clks[R1_FREQ], rx_path_clks[CLKRF_FREQ], rx_path_clks[RX_SAMPL_FREQ]); dev_dbg(&phy->spi->dev, "%s: %lu %lu %lu %lu %lu %lu", __func__, tx_path_clks[BBPLL_FREQ], tx_path_clks[ADC_FREQ], tx_path_clks[R2_FREQ], tx_path_clks[R1_FREQ], tx_path_clks[CLKRF_FREQ], tx_path_clks[RX_SAMPL_FREQ]); ret = ad9361_validate_trx_clock_chain(phy, rx_path_clks, tx_path_clks); if (ret < 0) return ret; ret = clk_set_rate(phy->clks[BBPLL_CLK], rx_path_clks[BBPLL_FREQ]); if (ret < 0) return ret; st->current_rx_path_clks[BBPLL_FREQ] = rx_path_clks[BBPLL_FREQ]; for (i = ADC_CLK, j = DAC_CLK, n = ADC_FREQ; i <= RX_SAMPL_CLK; i++, j++, n++) { ret = clk_set_rate(phy->clks[i], rx_path_clks[n]); if (ret < 0) { dev_err(dev, "Failed to set BB ref clock rate (%d)\n", ret); return ret; } st->current_rx_path_clks[n] = rx_path_clks[n]; ret = clk_set_rate(phy->clks[j], tx_path_clks[n]); if (ret < 0) { dev_err(dev, "Failed to set BB ref clock rate (%d)\n", ret); return ret; } st->current_tx_path_clks[n] = tx_path_clks[n]; } /* * Workaround for clock framework since clocks don't change we * manually need to enable the filter */ if (st->rx_fir_dec == 1 || st->bypass_rx_fir) { ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0), !st->bypass_rx_fir); } if (st->tx_fir_int == 1 || st->bypass_tx_fir) { ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0), !st->bypass_tx_fir); } /* The FIR filter once enabled causes the interface timing to change. * It's typically not a problem if the timing margin is big enough. * However at 61.44 MSPS it causes problems on some systems. * So we always run the digital tune in case the filter is enabled. * If it is disabled we restore the values from the initial calibration. */ if (!phy->pdata->dig_interface_tune_fir_disable && !(st->bypass_tx_fir && st->bypass_rx_fir)) ret = ad9361_dig_tune(phy, 0, SKIP_STORE_RESULT); return ad9361_bb_clk_change_handler(phy); } int ad9361_set_trx_clock_chain_default(struct ad9361_rf_phy *phy) { return ad9361_set_trx_clock_chain(phy, phy->pdata->rx_path_clks, phy->pdata->tx_path_clks); } EXPORT_SYMBOL(ad9361_set_trx_clock_chain_default); bool ad9361_uses_rx2tx2(struct ad9361_rf_phy *phy) { return phy && phy->pdata && phy->pdata->rx2tx2; } EXPORT_SYMBOL(ad9361_uses_rx2tx2); int ad9361_get_dig_tune_data(struct ad9361_rf_phy *phy, struct ad9361_dig_tune_data *data) { struct ad9361_rf_phy_state *st; if (!phy || !data) return -EINVAL; st = phy->state; data->ensm_state = ad9361_ensm_get_state(phy); data->bist_loopback_mode = st->bist_loopback_mode; data->skip_mode = phy->pdata->dig_interface_tune_skipmode; data->bist_config = st->bist_config; return 0; } EXPORT_SYMBOL(ad9361_get_dig_tune_data); bool ad9361_uses_lvds_mode(struct ad9361_rf_phy *phy) { return (phy && phy->pdata && !!(phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE)); } EXPORT_SYMBOL(ad9361_uses_lvds_mode); int ad9361_write_clock_data_delays(struct ad9361_rf_phy *phy) { if (!phy || !phy->pdata) return -EINVAL; ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY, phy->pdata->port_ctrl.rx_clk_data_delay); ad9361_spi_write(phy->spi, REG_TX_CLOCK_DATA_DELAY, phy->pdata->port_ctrl.tx_clk_data_delay); return 0; } EXPORT_SYMBOL(ad9361_write_clock_data_delays); int ad9361_read_clock_data_delays(struct ad9361_rf_phy *phy) { if (!phy || !phy->pdata) return -EINVAL; phy->pdata->port_ctrl.rx_clk_data_delay = ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY); phy->pdata->port_ctrl.tx_clk_data_delay = ad9361_spi_read(phy->spi, REG_TX_CLOCK_DATA_DELAY); return 0; } EXPORT_SYMBOL(ad9361_read_clock_data_delays); static int ad9361_get_trx_clock_chain(struct ad9361_rf_phy *phy, unsigned long *rx_path_clks, unsigned long *tx_path_clks) { int i, j, n; unsigned long bbpll_freq; if (!rx_path_clks && !tx_path_clks) return -EINVAL; bbpll_freq = clk_get_rate(phy->clks[BBPLL_CLK]); if (rx_path_clks) rx_path_clks[BBPLL_FREQ] = bbpll_freq; if (tx_path_clks) tx_path_clks[BBPLL_FREQ] = bbpll_freq; for (i = ADC_CLK, j = DAC_CLK, n = ADC_FREQ; i <= RX_SAMPL_CLK; i++, j++, n++) { if (rx_path_clks) rx_path_clks[n] = clk_get_rate(phy->clks[i]); if (tx_path_clks) tx_path_clks[n] = clk_get_rate(phy->clks[j]); } return 0; } static int ad9361_calculate_rf_clock_chain(struct ad9361_rf_phy *phy, unsigned long tx_sample_rate, u32 rate_gov, unsigned long *rx_path_clks, unsigned long *tx_path_clks) { struct ad9361_rf_phy_state *st = phy->state; unsigned long clktf, clkrf, adc_rate = 0, dac_rate = 0; u64 bbpll_rate; int i, index_rx = -1, index_tx = -1, tmp; u32 div, tx_intdec, rx_intdec, recursion = 1; const char clk_dividers[][4] = { {12,3,2,2}, {8,2,2,2}, {6,3,1,2}, {4,2,2,1}, {3,3,1,1}, {2,2,1,1}, {1,1,1,1}, }; if (st->bypass_rx_fir) rx_intdec = 1; else rx_intdec = st->rx_fir_dec; if (st->bypass_tx_fir) tx_intdec = 1; else tx_intdec = st->tx_fir_int; if ((rate_gov == 1) && ((rx_intdec * tx_sample_rate * 8) < MIN_ADC_CLK)) { recursion = 0; rate_gov = 0; } dev_dbg(&phy->spi->dev, "%s: requested rate %lu TXFIR int %d RXFIR dec %d mode %s", __func__, tx_sample_rate, tx_intdec, rx_intdec, rate_gov ? "Nominal" : "Highest OSR"); if (tx_sample_rate > MAX_BASEBAND_RATE) return -EINVAL; clktf = tx_sample_rate * tx_intdec; clkrf = tx_sample_rate * rx_intdec * (st->rx_eq_2tx ? 2 : 1); for (i = rate_gov; i < 7; i++) { adc_rate = clkrf * clk_dividers[i][0]; dac_rate = clktf * clk_dividers[i][0]; if ((adc_rate <= MAX_ADC_CLK) && (adc_rate >= MIN_ADC_CLK)) { if (dac_rate > adc_rate) tmp = (dac_rate / adc_rate) * -1; else tmp = adc_rate / dac_rate; if (adc_rate <= MAX_DAC_CLK) { index_rx = i; index_tx = i - ((tmp == 1) ? 0 : tmp); dac_rate = adc_rate; /* ADC_CLK */ break; } else { dac_rate = adc_rate / 2; /* ADC_CLK/2 */ index_rx = i; if (i == 4 && tmp >= 0) index_tx = 7; /* STOP: 3/2 != 1 */ else index_tx = i + ((i == 5 && tmp >= 0) ? 1 : 2) - ((tmp == 1) ? 0 : tmp); break; } } } if ((index_tx < 0 || index_tx > 6 || index_rx < 0 || index_rx > 6) && rate_gov < 7 && recursion) { return ad9361_calculate_rf_clock_chain(phy, tx_sample_rate, ++rate_gov, rx_path_clks, tx_path_clks); } else if ((index_tx < 0 || index_tx > 6 || index_rx < 0 || index_rx > 6)) { dev_err(&phy->spi->dev, "%s: Failed to find suitable dividers: %s", __func__, (adc_rate < MIN_ADC_CLK) ? "ADC clock below limit" : "BBPLL rate above limit"); return -EINVAL; } /* Calculate target BBPLL rate */ div = MAX_BBPLL_DIV; do { bbpll_rate = (u64)adc_rate * div; div >>= 1; } while ((bbpll_rate > MAX_BBPLL_FREQ) && (div >= MIN_BBPLL_DIV)); rx_path_clks[BBPLL_FREQ] = bbpll_rate; rx_path_clks[ADC_FREQ] = adc_rate; rx_path_clks[R2_FREQ] = rx_path_clks[ADC_FREQ] / clk_dividers[index_rx][1]; rx_path_clks[R1_FREQ] = rx_path_clks[R2_FREQ] / clk_dividers[index_rx][2]; rx_path_clks[CLKRF_FREQ] = rx_path_clks[R1_FREQ] / clk_dividers[index_rx][3]; rx_path_clks[RX_SAMPL_FREQ] = rx_path_clks[CLKRF_FREQ] / rx_intdec; tx_path_clks[BBPLL_FREQ] = bbpll_rate; tx_path_clks[DAC_FREQ] = dac_rate; tx_path_clks[T2_FREQ] = tx_path_clks[DAC_FREQ] / clk_dividers[index_tx][1]; tx_path_clks[T1_FREQ] =tx_path_clks[T2_FREQ] / clk_dividers[index_tx][2]; tx_path_clks[CLKTF_FREQ] = tx_path_clks[T1_FREQ] / clk_dividers[index_tx][3]; tx_path_clks[TX_SAMPL_FREQ] = tx_path_clks[CLKTF_FREQ] / tx_intdec; return 0; } int ad9361_set_trx_clock_chain_freq(struct ad9361_rf_phy *phy, unsigned long freq) { struct ad9361_rf_phy_state *st = phy->state; unsigned long rx[6], tx[6]; int ret; ret = ad9361_calculate_rf_clock_chain(phy, freq, st->rate_governor, rx, tx); if (ret < 0) return ret; return ad9361_set_trx_clock_chain(phy, rx, tx); } EXPORT_SYMBOL(ad9361_set_trx_clock_chain_freq); static int ad9361_set_ensm_mode(struct ad9361_rf_phy *phy, bool fdd, bool pinctrl) { struct ad9361_phy_platform_data *pd = phy->pdata; int ret; u32 val = 0; ad9361_spi_write(phy->spi, REG_ENSM_MODE, fdd ? FDD_MODE : 0); val = ad9361_spi_read(phy->spi, REG_ENSM_CONFIG_2); val &= POWER_DOWN_RX_SYNTH | POWER_DOWN_TX_SYNTH | RX_SYNTH_READY_MASK | TX_SYNTH_READY_MASK; if (fdd) ret = ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, val | DUAL_SYNTH_MODE | (pd->fdd_independent_mode ? FDD_EXTERNAL_CTRL_ENABLE : 0)); else ret = ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, val | (pd->tdd_use_dual_synth ? DUAL_SYNTH_MODE : 0) | (pd->tdd_use_dual_synth ? 0 : (pinctrl ? SYNTH_ENABLE_PIN_CTRL_MODE : 0))); return ret; } int ad9361_ensm_mode_disable_pinctrl(struct ad9361_rf_phy *phy) { if (!phy->pdata->fdd) return ad9361_set_ensm_mode(phy, true, false); return 0; } EXPORT_SYMBOL(ad9361_ensm_mode_disable_pinctrl); int ad9361_ensm_mode_restore_pinctrl(struct ad9361_rf_phy *phy) { if (!phy->pdata->fdd) return ad9361_set_ensm_mode(phy, phy->pdata->fdd, phy->pdata->ensm_pin_ctrl); return 0; } EXPORT_SYMBOL(ad9361_ensm_mode_restore_pinctrl); /* Fast Lock */ static int ad9361_fastlock_readval(struct spi_device *spi, bool tx, u32 profile, u32 word) { u32 offs = 0; if (tx) offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_ADDR + offs, RX_FAST_LOCK_PROFILE_ADDR(profile) | RX_FAST_LOCK_PROFILE_WORD(word)); return ad9361_spi_read(spi, REG_RX_FAST_LOCK_PROGRAM_READ + offs); } static int ad9361_fastlock_writeval(struct spi_device *spi, bool tx, u32 profile, u32 word, u8 val, bool last) { u32 offs = 0; int ret; if (tx) offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; ret = ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_ADDR + offs, RX_FAST_LOCK_PROFILE_ADDR(profile) | RX_FAST_LOCK_PROFILE_WORD(word)); ret |= ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_DATA + offs, val); ret |= ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, RX_FAST_LOCK_PROGRAM_WRITE | RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE); if (last) /* Stop Clocks */ ret |= ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, 0); return ret; } static int ad9361_fastlock_load(struct ad9361_rf_phy *phy, bool tx, u32 profile, u8 *values) { struct ad9361_rf_phy_state *st = phy->state; u32 offs = 0; int i, ret = 0; u8 buf[4]; dev_dbg(&phy->spi->dev, "%s: %s Profile %d:", __func__, tx ? "TX" : "RX", profile); if (tx) offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; buf[0] = values[0]; buf[1] = RX_FAST_LOCK_PROFILE_ADDR(profile) | RX_FAST_LOCK_PROFILE_WORD(0); ad9361_spi_writem(phy->spi, REG_RX_FAST_LOCK_PROGRAM_DATA + offs, buf, 2); for (i = 1; i < RX_FAST_LOCK_CONFIG_WORD_NUM; i++) { buf[0] = RX_FAST_LOCK_PROGRAM_WRITE | RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE; buf[1] = 0; buf[2] = values[i]; buf[3] = RX_FAST_LOCK_PROFILE_ADDR(profile) | RX_FAST_LOCK_PROFILE_WORD(i); ad9361_spi_writem(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, buf, 4); } ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, RX_FAST_LOCK_PROGRAM_WRITE | RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE); ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, 0); st->fastlock.entry[tx][profile].flags = FASTLOOK_INIT; st->fastlock.entry[tx][profile].alc_orig = values[15]; st->fastlock.entry[tx][profile].alc_written = values[15]; return ret; } static int ad9361_fastlock_store(struct ad9361_rf_phy *phy, bool tx, u32 profile) { struct spi_device *spi = phy->spi; u8 val[16]; u32 offs = 0, x, y; dev_dbg(&phy->spi->dev, "%s: %s Profile %d:", __func__, tx ? "TX" : "RX", profile); if (tx) offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; val[0] = ad9361_spi_read(spi, REG_RX_INTEGER_BYTE_0 + offs); val[1] = ad9361_spi_read(spi, REG_RX_INTEGER_BYTE_1 + offs); val[2] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_0 + offs); val[3] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_1 + offs); val[4] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_2 + offs); x = ad9361_spi_readf(spi, REG_RX_VCO_BIAS_1 + offs, VCO_BIAS_REF(~0)); y = ad9361_spi_readf(spi, REG_RX_ALC_VARACTOR + offs, VCO_VARACTOR(~0)); val[5] = (x << 4) | y; x = ad9361_spi_readf(spi, REG_RX_VCO_BIAS_1 + offs, VCO_BIAS_TCF(~0)); y = ad9361_spi_readf(spi, REG_RX_CP_CURRENT + offs, CHARGE_PUMP_CURRENT(~0)); /* Wide BW option: N = 1 * Set init and steady state values to the same - let user space handle it */ val[6] = (x << 3) | y; val[7] = y; x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_3 + offs, LOOP_FILTER_R3(~0)); val[8] = (x << 4) | x; x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_2 + offs, LOOP_FILTER_C3(~0)); val[9] = (x << 4) | x; x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_1 + offs, LOOP_FILTER_C1(~0)); y = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_1 + offs, LOOP_FILTER_C2(~0)); val[10] = (x << 4) | y; x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_2 + offs, LOOP_FILTER_R1(~0)); val[11] = (x << 4) | x; x = ad9361_spi_readf(spi, REG_RX_VCO_VARACTOR_CTRL_0 + offs, VCO_VARACTOR_REFERENCE_TCF(~0)); y = ad9361_spi_readf(spi, REG_RFPLL_DIVIDERS, tx ? TX_VCO_DIVIDER(~0) : RX_VCO_DIVIDER(~0)); val[12] = (x << 4) | y; x = ad9361_spi_readf(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, VCO_CAL_OFFSET(~0)); y = ad9361_spi_readf(spi, REG_RX_VCO_VARACTOR_CTRL_1 + offs, VCO_VARACTOR_REFERENCE(~0)); val[13] = (x << 4) | y; val[14] = ad9361_spi_read(spi, REG_RX_FORCE_VCO_TUNE_0 + offs); x = ad9361_spi_readf(spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_WORD(~0)); y = ad9361_spi_readf(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE); val[15] = (x << 1) | y; return ad9361_fastlock_load(phy, tx, profile, val); } static int ad9361_fastlock_prepare(struct ad9361_rf_phy *phy, bool tx, u32 profile, bool prepare) { struct ad9361_rf_phy_state *st = phy->state; u32 offs, ready_mask; bool is_prepared; dev_dbg(&phy->spi->dev, "%s: %s Profile %d: %s", __func__, tx ? "TX" : "RX", profile, prepare ? "Prepare" : "Un-Prepare"); if (tx) { offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; ready_mask = TX_SYNTH_READY_MASK; } else { offs = 0; ready_mask = RX_SYNTH_READY_MASK; } is_prepared = !!st->fastlock.current_profile[tx]; if (prepare && !is_prepared) { ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP_INIT_DELAY + offs, (tx ? phy->pdata->tx_fastlock_delay_ns : phy->pdata->rx_fastlock_delay_ns) / 250); ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs, RX_FAST_LOCK_PROFILE(profile) | RX_FAST_LOCK_MODE_ENABLE); ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, 0); ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, ready_mask, 1); ad9361_trx_vco_cal_control(phy, tx, false); } else if (!prepare && is_prepared) { ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs, 0); /* Workaround: Exiting Fastlock Mode */ ad9361_spi_writef(phy->spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_ENABLE, 1); ad9361_spi_writef(phy->spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE, 1); ad9361_spi_writef(phy->spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_ENABLE, 0); ad9361_spi_writef(phy->spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE, 0); ad9361_trx_vco_cal_control(phy, tx, true); ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, ready_mask, 0); st->fastlock.current_profile[tx] = 0; } return 0; } static int ad9361_fastlock_recall(struct ad9361_rf_phy *phy, bool tx, u32 profile) { struct ad9361_rf_phy_state *st = phy->state; u32 offs = 0; u8 curr, new, orig, current_profile; dev_dbg(&phy->spi->dev, "%s: %s Profile %d:", __func__, tx ? "TX" : "RX", profile); if (tx) offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; if (st->fastlock.entry[tx][profile].flags != FASTLOOK_INIT) return -EINVAL; /* Workaround: Lock problem with same ALC word */ current_profile = st->fastlock.current_profile[tx]; new = st->fastlock.entry[tx][profile].alc_written; if (current_profile == 0) curr = ad9361_spi_readf(phy->spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_WORD(~0)) << 1; else curr = st->fastlock.entry[tx][current_profile - 1].alc_written; if ((curr >> 1) == (new >> 1)) { orig = st->fastlock.entry[tx][profile].alc_orig; if ((orig >> 1) == (new >> 1)) st->fastlock.entry[tx][profile].alc_written += 2; else st->fastlock.entry[tx][profile].alc_written = orig; ad9361_fastlock_writeval(phy->spi, tx, profile, 0xF, st->fastlock.entry[tx][profile].alc_written, true); } ad9361_fastlock_prepare(phy, tx, profile, true); st->fastlock.current_profile[tx] = profile + 1; return ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs, RX_FAST_LOCK_PROFILE(profile) | (phy->pdata->trx_fastlock_pinctrl_en[tx] ? RX_FAST_LOCK_PROFILE_PIN_SELECT : 0) | RX_FAST_LOCK_MODE_ENABLE); } static int ad9361_fastlock_save(struct ad9361_rf_phy *phy, bool tx, u32 profile, u8 *values) { int i; dev_dbg(&phy->spi->dev, "%s: %s Profile %d:", __func__, tx ? "TX" : "RX", profile); for (i = 0; i < RX_FAST_LOCK_CONFIG_WORD_NUM; i++) values[i] = ad9361_fastlock_readval(phy->spi, tx, profile, i); return 0; } static int ad9361_mcs(struct ad9361_rf_phy *phy, unsigned step) { unsigned mcs_mask = MCS_RF_ENABLE | MCS_BBPLL_ENABLE | MCS_DIGITAL_CLK_ENABLE | MCS_BB_ENABLE; dev_dbg(&phy->spi->dev, "%s: MCS step %d", __func__, step); switch(spi_get_device_id(phy->spi)->driver_data) { case ID_AD9363A: return -ENODEV; } switch (step) { case 1: /* REVIST: * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict */ ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, POWER_DOWN_TX_SYNTH | POWER_DOWN_RX_SYNTH, 0); ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, mcs_mask, MCS_BB_ENABLE | MCS_BBPLL_ENABLE | MCS_RF_ENABLE); ad9361_spi_writef(phy->spi, REG_CP_BLEED_CURRENT, MCS_REFCLK_SCALE_EN, 1); break; case 2: if (!phy->pdata->sync_gpio) break; /* * NOTE: This is not a regular GPIO - * HDL ensures Multi-chip Synchronization SYNC_IN Pulse Timing * relative to rising and falling edge of REF_CLK */ gpiod_set_value(phy->pdata->sync_gpio, 1); gpiod_set_value(phy->pdata->sync_gpio, 0); break; case 3: ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, mcs_mask, MCS_BB_ENABLE | MCS_DIGITAL_CLK_ENABLE | MCS_RF_ENABLE); break; case 4: if (!phy->pdata->sync_gpio) break; gpiod_set_value(phy->pdata->sync_gpio, 1); gpiod_set_value(phy->pdata->sync_gpio, 0); break; case 5: ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, mcs_mask, MCS_RF_ENABLE); break; } return 0; } static int ad9361_rssi_write_err_tbl(struct ad9361_rf_phy *phy) { u8 i; ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK); for(i = 0; i < 4; i++) { ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, phy->pdata->rssi_lna_err_tbl[i]); ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x3) | WRITE_LNA_ERROR_TABLE | START_CALIB_TABLE_CLOCK); } ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK); for(i = 0; i < 16; i++) { ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, phy->pdata->rssi_mixer_err_tbl[i]); ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x3) | WRITE_MIXER_ERROR_TABLE | START_CALIB_TABLE_CLOCK); } ad9361_spi_write(phy->spi, REG_CONFIG, 0x00); return 0; } static int ad9361_rssi_program_lna_gain(struct ad9361_rf_phy *phy) { u8 i; ad9361_spi_write(phy->spi, REG_LNA_GAIN, phy->pdata->rssi_gain_step_calib_reg_val[0]); /* Program the LNA gain step words into the internal table. */ ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK); for(i = 0; i < 4; i++) { ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, phy->pdata->rssi_gain_step_calib_reg_val[i+1]); ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x3) | WRITE_LNA_GAIN_DIFF | START_CALIB_TABLE_CLOCK); udelay(3); //Wait for data to fully write to internal table } return 0; } static int ad9361_rssi_gain_step_calib(struct ad9361_rf_phy *phy) { /* * Before running the function, provide a single tone within the channel * bandwidth and monitor the received data. Adjust the tone amplitude until * the received data is within a few dB of full scale but not overloading. */ u64 lo_freq_hz; u8 lo_index; u8 i; int ret; lo_freq_hz = ad9361_from_clk(clk_get_rate(phy->clks[RX_RFPLL])); if (lo_freq_hz < 1300000000ULL) lo_index = 0; else if (lo_freq_hz < 3300000000ULL) lo_index = 1; else if (lo_freq_hz < 4100000000ULL) lo_index = 2; else lo_index = 3; for(i = 0; i < 4; i++) phy->pdata->rssi_gain_step_calib_reg_val[i] = gain_step_calib_reg_val[lo_index][i]; /* Put the AD9361 into the Alert state. */ ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); /* Program the directly-addressable register values. */ ad9361_spi_write(phy->spi, REG_MAX_MIXER_CALIBRATION_GAIN_INDEX, MAX_MIXER_CALIBRATION_GAIN_INDEX(0x0F)); ad9361_spi_write(phy->spi, REG_MEASURE_DURATION, GAIN_CAL_MEAS_DURATION(0x0E)); ad9361_spi_write(phy->spi, REG_SETTLE_TIME, SETTLE_TIME(0x3F)); ad9361_spi_write(phy->spi, REG_RSSI_CONFIG, RSSI_MODE_SELECT(0x3) | DEFAULT_RSSI_MEAS_MODE); ad9361_spi_write(phy->spi, REG_MEASURE_DURATION_01, MEASUREMENT_DURATION_0(0x0E)); ad9361_rssi_program_lna_gain(phy); ad9361_spi_write(phy->spi, REG_CONFIG, START_CALIB_TABLE_CLOCK); ad9361_spi_write(phy->spi, REG_CONFIG, 0x00); /* Run and wait until the calibration completes. */ ret = ad9361_run_calibration(phy, RX_GAIN_STEP_CAL); /* Read the LNA and Mixer error terms into nonvolatile memory. */ ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x1) | READ_SELECT); for(i = 0; i < 4; i++) { ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); phy->pdata->rssi_lna_err_tbl[i] = ad9361_spi_read(phy->spi, REG_GAIN_ERROR_READ); } ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x1)); for(i = 0; i < 16; i++) { ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); phy->pdata->rssi_mixer_err_tbl[i] = ad9361_spi_read(phy->spi, REG_GAIN_ERROR_READ); } ad9361_spi_write(phy->spi, REG_CONFIG, 0x00); /* Programming gain step errors into the AD9361 in the field */ ad9361_rssi_write_err_tbl(phy); ad9361_spi_write(phy->spi, REG_SETTLE_TIME, ENABLE_DIG_GAIN_CORR | SETTLE_TIME(0x10)); ad9361_ensm_restore_prev_state(phy); return ret; } static void ad9361_init_state(struct ad9361_rf_phy *phy) { struct ad9361_rf_phy_state *st = phy->state; st->current_table = -1; st->bypass_tx_fir = true; st->bypass_rx_fir = true; st->rate_governor = 1; st->rfdc_track_en = true; st->bbdc_track_en = true; st->quad_track_en = true; } static void ad9361_clear_state(struct ad9361_rf_phy *phy) { struct ad9361_rf_phy_state *st = phy->state; memset(st, 0, sizeof(*st)); ad9361_init_state(phy); } static unsigned long ad9361_ref_div_sel(unsigned long refin_Hz, unsigned long max) { if (refin_Hz <= (max / 2)) return 2 * refin_Hz; else if (refin_Hz <= max) return refin_Hz; else if (refin_Hz <= (max * 2)) return refin_Hz / 2; else if (refin_Hz <= (max * 4)) return refin_Hz / 4; else return 0; } static int ad9361_setup(struct ad9361_rf_phy *phy) { struct ad9361_rf_phy_state *st = phy->state; unsigned long refin_Hz, ref_freq, bbpll_freq; struct device *dev = &phy->spi->dev; struct spi_device *spi = phy->spi; struct ad9361_phy_platform_data *pd = phy->pdata; u32 real_rx_bandwidth, real_tx_bandwidth; int ret; pd->rf_rx_bandwidth_Hz = ad9361_validate_rf_bw(phy, pd->rf_rx_bandwidth_Hz); pd->rf_tx_bandwidth_Hz = ad9361_validate_rf_bw(phy, pd->rf_tx_bandwidth_Hz); real_rx_bandwidth = pd->rf_rx_bandwidth_Hz / 2; real_tx_bandwidth = pd->rf_tx_bandwidth_Hz / 2; dev_dbg(dev, "%s", __func__); if (pd->fdd) { pd->tdd_skip_vco_cal = false; if (pd->ensm_pin_ctrl && pd->fdd_independent_mode) { dev_warn(dev, "%s: Either set ENSM PINCTRL or FDD Independent Mode", __func__); pd->ensm_pin_ctrl = false; } } ret = ad9361_auxdac_setup(phy, &pd->auxdac_ctrl); if (ret < 0) return ret; ret = ad9361_gpo_setup(phy, &pd->gpo_ctrl); if (ret < 0) return ret; if (pd->port_ctrl.pp_conf[2] & FDD_RX_RATE_2TX_RATE) st->rx_eq_2tx = true; ad9361_spi_write(spi, REG_CTRL, CTRL_ENABLE); ad9361_spi_write(spi, REG_BANDGAP_CONFIG0, MASTER_BIAS_TRIM(0x0E)); /* Enable Master Bias */ ad9361_spi_write(spi, REG_BANDGAP_CONFIG1, BANDGAP_TEMP_TRIM(0x0E)); /* Set Bandgap Trim */ ad9361_set_dcxo_tune(phy, pd->dcxo_coarse, pd->dcxo_fine); refin_Hz = clk_get_rate(phy->clk_refin); ref_freq = ad9361_ref_div_sel(refin_Hz, MAX_BBPLL_FREF); if (!ref_freq) return -EINVAL; ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_1, RX_REF_RESET_BAR, 1); ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, TX_REF_RESET_BAR, 1); ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, TX_REF_DOUBLER_FB_DELAY(~0), 3); /* FB DELAY */ ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, RX_REF_DOUBLER_FB_DELAY(~0), 3); /* FB DELAY */ ad9361_spi_write(spi, REG_CLOCK_ENABLE, DIGITAL_POWER_UP | CLOCK_ENABLE_DFLT | BBPLL_ENABLE | (pd->use_extclk ? XO_BYPASS : 0)); /* Enable Clocks */ ret = clk_prepare_enable(phy->clk_refin); if (ret < 0) return ret; ret = clk_set_rate(phy->clks[BB_REFCLK], ref_freq); if (ret < 0) { dev_err(dev, "Failed to set BB ref clock rate (%d)\n", ret); return ret; } ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_2, 0x12); ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_3, 0x34); ret = ad9361_set_trx_clock_chain_default(phy); if (ret < 0) return ret; if (!pd->rx2tx2) { pd->rx1tx1_mode_use_tx_num = clamp_t(u32, pd->rx1tx1_mode_use_tx_num, TX_1, TX_2); pd->rx1tx1_mode_use_rx_num = clamp_t(u32, pd->rx1tx1_mode_use_rx_num, RX_1, RX_2); ad9361_en_dis_tx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_tx_num); ad9361_en_dis_rx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_rx_num); } else { ad9361_en_dis_tx(phy, TX_1 | TX_2, TX_1 | TX_2); ad9361_en_dis_rx(phy, RX_1 | RX_2, RX_1 | RX_2); } ret = ad9361_rf_port_setup(phy, true, st->rf_rx_input_sel, st->rf_tx_output_sel); if (ret < 0) return ret; ret = ad9361_pp_port_setup(phy, false); if (ret < 0) return ret; bbpll_freq = clk_get_rate(phy->clks[BBPLL_CLK]); ret = ad9361_auxadc_setup(phy, &pd->auxadc_ctrl, bbpll_freq); if (ret < 0) return ret; ret = ad9361_ctrl_outs_setup(phy, &pd->ctrl_outs_ctrl); if (ret < 0) return ret; ret = ad9361_set_ref_clk_cycles(phy, refin_Hz); if (ret < 0) return ret; ret = ad9361_setup_ext_lna(phy, &pd->elna_ctrl); if (ret < 0) return ret; /* * This allows forcing a lower F_REF window * (worse phase noise, better fractional spurs) */ pd->trx_synth_max_fref = clamp_t(u32, pd->trx_synth_max_fref, MIN_SYNTH_FREF, MAX_SYNTH_FREF); ref_freq = ad9361_ref_div_sel(refin_Hz, pd->trx_synth_max_fref); if (!ref_freq) return -EINVAL; clk_set_parent(phy->clks[TX_RFPLL], phy->clks[TX_RFPLL_INT]); clk_set_parent(phy->clks[RX_RFPLL], phy->clks[RX_RFPLL_INT]); ret = clk_set_rate(phy->clks[RX_REFCLK], ref_freq); if (ret < 0) { dev_err(dev, "Failed to set RX Synth ref clock rate (%d)\n", ret); return ret; } ret = clk_set_rate(phy->clks[TX_REFCLK], ref_freq); if (ret < 0) { dev_err(dev, "Failed to set TX Synth ref clock rate (%d)\n", ret); return ret; } ret = ad9361_txrx_synth_cp_calib(phy, ref_freq, false); /* RXCP */ if (ret < 0) return ret; ret = ad9361_txrx_synth_cp_calib(phy, ref_freq, true); /* TXCP */ if (ret < 0) return ret; ret = clk_set_rate(phy->clks[RX_RFPLL], ad9361_to_clk(pd->rx_synth_freq)); if (ret < 0) { dev_err(dev, "Failed to set RX Synth rate (%d)\n", ret); return ret; } ret = clk_prepare_enable(phy->clks[RX_RFPLL]); if (ret < 0) return ret; /* Skip quad cal here we do it later again */ st->last_tx_quad_cal_freq = pd->tx_synth_freq; ret = clk_set_rate(phy->clks[TX_RFPLL], ad9361_to_clk(pd->tx_synth_freq)); if (ret < 0) { dev_err(dev, "Failed to set TX Synth rate (%d)\n", ret); return ret; } ret = clk_prepare_enable(phy->clks[TX_RFPLL]); if (ret < 0) return ret; clk_set_parent(phy->clks[RX_RFPLL], pd->use_ext_rx_lo ? phy->clk_ext_lo_rx : phy->clks[RX_RFPLL_INT]); clk_set_parent(phy->clks[TX_RFPLL], pd->use_ext_tx_lo ? phy->clk_ext_lo_tx : phy->clks[TX_RFPLL_INT]); ret = ad9361_load_mixer_gm_subtable(phy); if (ret < 0) return ret; ret = ad9361_gc_setup(phy, &pd->gain_ctrl); if (ret < 0) return ret; ret = ad9361_rx_bb_analog_filter_calib(phy, real_rx_bandwidth, bbpll_freq); if (ret < 0) return ret; ret = ad9361_tx_bb_analog_filter_calib(phy, real_tx_bandwidth, bbpll_freq); if (ret < 0) return ret; ret = ad9361_rx_tia_calib(phy, real_rx_bandwidth); if (ret < 0) return ret; ret = ad9361_tx_bb_second_filter_calib(phy, real_tx_bandwidth); if (ret < 0) return ret; ret = ad9361_rx_adc_setup(phy, bbpll_freq, clk_get_rate(phy->clks[ADC_CLK])); if (ret < 0) return ret; ret = ad9361_bb_dc_offset_calib(phy); if (ret < 0) return ret; ret = ad9361_rf_dc_offset_calib(phy, ad9361_from_clk(clk_get_rate(phy->clks[RX_RFPLL]))); if (ret < 0) return ret; st->current_rx_bw_Hz = pd->rf_rx_bandwidth_Hz; st->current_tx_bw_Hz = pd->rf_tx_bandwidth_Hz; st->last_tx_quad_cal_phase = ~0; ret = ad9361_tx_quad_calib(phy, real_rx_bandwidth, real_tx_bandwidth, -1); if (ret < 0) return ret; ret = ad9361_tracking_control(phy, st->bbdc_track_en, st->rfdc_track_en, st->quad_track_en); if (ret < 0) return ret; ad9361_pp_port_setup(phy, true); ret = ad9361_set_ensm_mode(phy, pd->fdd, pd->ensm_pin_ctrl); if (ret < 0) return ret; ad9361_spi_writef(phy->spi, REG_TX_ATTEN_OFFSET, MASK_CLR_ATTEN_UPDATE, 0); ret = ad9361_set_tx_atten(phy, pd->tx_atten, pd->rx2tx2 ? true : pd->rx1tx1_mode_use_tx_num == 1, pd->rx2tx2 ? true : pd->rx1tx1_mode_use_tx_num == 2, true); if (ret < 0) return ret; if (!pd->rx2tx2) { ret = ad9361_set_tx_atten(phy, 89750, pd->rx1tx1_mode_use_tx_num == 2, pd->rx1tx1_mode_use_tx_num == 1, true); if (ret < 0) return ret; } ret = ad9361_rssi_setup(phy, &pd->rssi_ctrl, false); if (ret < 0) return ret; ret = ad9361_clkout_control(phy, pd->ad9361_clkout_mode); if (ret < 0) return ret; ret = ad9361_txmon_setup(phy, &pd->txmon_ctrl); if (ret < 0) return ret; st->curr_ensm_state = ad9361_spi_readf(spi, REG_STATE, ENSM_STATE(~0)); ad9361_ensm_set_state(phy, pd->fdd ? ENSM_STATE_FDD : ENSM_STATE_RX, pd->ensm_pin_ctrl); st->auto_cal_en = true; st->cal_threshold_freq = 100000000ULL; /* 100 MHz */ if (!pd->rssi_skip_calib) { ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); ad9361_rssi_program_lna_gain(phy); ad9361_rssi_write_err_tbl(phy); ad9361_spi_write(phy->spi, REG_SETTLE_TIME, ENABLE_DIG_GAIN_CORR | SETTLE_TIME(0x10)); ad9361_ensm_restore_prev_state(phy); } return 0; } static int ad9361_do_calib_run(struct ad9361_rf_phy *phy, u32 cal, int arg) { struct ad9361_rf_phy_state *st = phy->state; int ret; dev_dbg(&phy->spi->dev, "%s: CAL %u ARG %d", __func__, cal, arg); ret = ad9361_tracking_control(phy, false, false, false); if (ret < 0) return ret; ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); switch (cal) { case TX_QUAD_CAL: ret = ad9361_tx_quad_calib(phy, st->current_rx_bw_Hz / 2, st->current_tx_bw_Hz / 2, arg); break; case RFDC_CAL: ret = ad9361_rf_dc_offset_calib(phy, ad9361_from_clk(clk_get_rate(phy->clks[RX_RFPLL]))); break; default: ret = -EINVAL; break; } ret = ad9361_tracking_control(phy, st->bbdc_track_en, st->rfdc_track_en, st->quad_track_en); ad9361_ensm_restore_prev_state(phy); return ret; } int ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy, u32 rf_rx_bw, u32 rf_tx_bw) { struct ad9361_rf_phy_state *st = phy->state; int ret; ret = ad9361_tracking_control(phy, false, false, false); if (ret < 0) return ret; ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); ret = __ad9361_update_rf_bandwidth(phy, rf_rx_bw, rf_tx_bw); if (ret < 0) return ret; st->current_rx_bw_Hz = rf_rx_bw; st->current_tx_bw_Hz = rf_tx_bw; if (st->manual_tx_quad_cal_en == false) { ret = ad9361_tx_quad_calib(phy, rf_rx_bw / 2, rf_tx_bw / 2, -1); if (ret < 0) return ret; } ret = ad9361_tracking_control(phy, st->bbdc_track_en, st->rfdc_track_en, st->quad_track_en); if (ret < 0) return ret; ad9361_ensm_restore_prev_state(phy); return 0; } EXPORT_SYMBOL(ad9361_update_rf_bandwidth); static int ad9361_verify_fir_filter_coef(struct ad9361_rf_phy *phy, enum fir_dest dest, u32 ntaps, short *coef) { struct spi_device *spi = phy->spi; u32 val, offs = 0, gain = 0, conf, sel, cnt; int ret = 0; #ifndef DEBUG return 0; #endif dev_dbg(&phy->spi->dev, "%s: TAPS %d, dest %d", __func__, ntaps, dest); if (dest & FIR_IS_RX) { gain = ad9361_spi_read(spi, REG_RX_FILTER_GAIN); offs = REG_RX_FILTER_COEF_ADDR - REG_TX_FILTER_COEF_ADDR; ad9361_spi_write(spi, REG_RX_FILTER_GAIN, 0); } conf = ad9361_spi_read(spi, REG_TX_FILTER_CONF + offs); if ((dest & 3) == 3) { sel = 1; cnt = 2; } else { sel = (dest & 3); cnt = 1; } for (; cnt > 0; cnt--, sel++) { ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, FIR_NUM_TAPS(ntaps / 16 - 1) | FIR_SELECT(sel) | FIR_START_CLK); for (val = 0; val < ntaps; val++) { short tmp; ad9361_spi_write(spi, REG_TX_FILTER_COEF_ADDR + offs, val); tmp = (ad9361_spi_read(spi, REG_TX_FILTER_COEF_READ_DATA_1 + offs) & 0xFF) | (ad9361_spi_read(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs) << 8); if (tmp != coef[val]) { dev_err(&phy->spi->dev,"%s%d read verify failed TAP%d %d =! %d \n", (dest & FIR_IS_RX) ? "RX" : "TX", sel, val, tmp, coef[val]); ret = -EIO; } } } if (dest & FIR_IS_RX) { ad9361_spi_write(spi, REG_RX_FILTER_GAIN, gain); } ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, conf); return ret; } static int ad9361_load_fir_filter_coef(struct ad9361_rf_phy *phy, enum fir_dest dest, int gain_dB, u32 ntaps, short *coef) { struct ad9361_rf_phy_state *st = phy->state; struct spi_device *spi = phy->spi; u32 val, offs = 0, fir_conf = 0, fir_enable = 0; int ret; dev_dbg(&phy->spi->dev, "%s: TAPS %d, gain %d, dest %d", __func__, ntaps, gain_dB, dest); if (coef == NULL || !ntaps || ntaps > 128 || ntaps % 16) { dev_err(&phy->spi->dev, "%s: Invalid parameters: TAPS %d, gain %d, dest 0x%X", __func__, ntaps, gain_dB, dest); return -EINVAL; } ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); if (dest & FIR_IS_RX) { val = 3 - (gain_dB + 12) / 6; ad9361_spi_write(spi, REG_RX_FILTER_GAIN, val & 0x3); offs = REG_RX_FILTER_COEF_ADDR - REG_TX_FILTER_COEF_ADDR; st->rx_fir_ntaps = ntaps; fir_enable = ad9361_spi_readf(phy->spi, REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0)); ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0), (st->rx_fir_dec == 4) ? 3 : st->rx_fir_dec); } else { if (gain_dB == -6) fir_conf = TX_FIR_GAIN_6DB; st->tx_fir_ntaps = ntaps; fir_enable = ad9361_spi_readf(phy->spi, REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0)); ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0), (st->tx_fir_int == 4) ? 3 : st->tx_fir_int); } val = ntaps / 16 - 1; fir_conf |= FIR_NUM_TAPS(val) | FIR_SELECT(dest) | FIR_START_CLK; ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf); for (val = 0; val < ntaps; val++) { ad9361_spi_write(spi, REG_TX_FILTER_COEF_ADDR + offs, val); ad9361_spi_write(spi, REG_TX_FILTER_COEF_WRITE_DATA_1 + offs, coef[val] & 0xFF); ad9361_spi_write(spi, REG_TX_FILTER_COEF_WRITE_DATA_2 + offs, coef[val] >> 8); ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf | FIR_WRITE); ad9361_spi_write(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0); ad9361_spi_write(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0); } ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf); fir_conf &= ~FIR_START_CLK; ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf); ret = ad9361_verify_fir_filter_coef(phy, dest, ntaps, coef); if (dest & FIR_IS_RX) ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0), fir_enable); else ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0), fir_enable); ad9361_ensm_restore_prev_state(phy); return ret; } static int ad9361_parse_fir(struct ad9361_rf_phy *phy, char *data, u32 size) { struct ad9361_rf_phy_state *st = phy->state; char *line; int i = 0, ret, txc, rxc; int tx = -1, tx_gain, tx_int; int rx = -1, rx_gain, rx_dec; int rtx = -1, rrx = -1; short coef_tx[128]; short coef_rx[128]; char *ptr = data; st->filt_rx_bw_Hz = 0; st->filt_tx_bw_Hz = 0; st->filt_valid = false; while ((line = strsep(&ptr, "\n"))) { if (line >= data + size) { break; } if (line[0] == '#') continue; if (tx < 0) { ret = sscanf(line, "TX %d GAIN %d INT %d", &tx, &tx_gain, &tx_int); if (ret == 3) continue; else tx = -1; } if (rx < 0) { ret = sscanf(line, "RX %d GAIN %d DEC %d", &rx, &rx_gain, &rx_dec); if (ret == 3) continue; else rx = -1; } if (rtx < 0) { ret = sscanf(line, "RTX %lu %lu %lu %lu %lu %lu", &st->filt_tx_path_clks[0], &st->filt_tx_path_clks[1], &st->filt_tx_path_clks[2], &st->filt_tx_path_clks[3], &st->filt_tx_path_clks[4], &st->filt_tx_path_clks[5]); if (ret == 6) { rtx = 0; continue; } else { rtx = -1; } } if (rrx < 0) { ret = sscanf(line, "RRX %lu %lu %lu %lu %lu %lu", &st->filt_rx_path_clks[0], &st->filt_rx_path_clks[1], &st->filt_rx_path_clks[2], &st->filt_rx_path_clks[3], &st->filt_rx_path_clks[4], &st->filt_rx_path_clks[5]); if (ret == 6) { rrx = 0; continue; } else { rrx = -1; } } if (!st->filt_rx_bw_Hz) { ret = sscanf(line, "BWRX %d", &st->filt_rx_bw_Hz); if (ret == 1) continue; else st->filt_rx_bw_Hz = 0; } if (!st->filt_tx_bw_Hz) { ret = sscanf(line, "BWTX %d", &st->filt_tx_bw_Hz); if (ret == 1) continue; else st->filt_tx_bw_Hz = 0; } ret = sscanf(line, "%d,%d", &txc, &rxc); if (ret == 1) { if (i >= ARRAY_SIZE(coef_tx)) return -EINVAL; coef_tx[i] = coef_rx[i] = (short)txc; i++; continue; } else if (ret == 2) { if (i >= ARRAY_SIZE(coef_tx)) return -EINVAL; coef_tx[i] = (short)txc; coef_rx[i] = (short)rxc; i++; continue; } } if (tx != -1) { switch (tx) { case FIR_TX1: case FIR_TX2: case FIR_TX1_TX2: st->tx_fir_int = tx_int; ret = ad9361_load_fir_filter_coef(phy, tx, tx_gain, i, coef_tx); break; default: ret = -EINVAL; } } if (rx != -1) { switch (rx | FIR_IS_RX) { case FIR_RX1: case FIR_RX2: case FIR_RX1_RX2: st->rx_fir_dec = rx_dec; ret = ad9361_load_fir_filter_coef(phy, rx | FIR_IS_RX, rx_gain, i, coef_rx); break; default: ret = -EINVAL; } } if (tx == -1 && rx == -1) ret = -EINVAL; if (ret < 0) return ret; if (!(rrx | rtx)) st->filt_valid = true; return size; } static int ad9361_validate_enable_fir(struct ad9361_rf_phy *phy) { struct ad9361_rf_phy_state *st = phy->state; struct device *dev = &phy->spi->dev; int ret; unsigned long rx[6], tx[6]; u32 max, valid; dev_dbg(dev, "%s: TX FIR EN=%d/TAPS%d/INT%d, RX FIR EN=%d/TAPS%d/DEC%d", __func__, !st->bypass_tx_fir, st->tx_fir_ntaps, st->tx_fir_int, !st->bypass_rx_fir, st->rx_fir_ntaps, st->rx_fir_dec); if (!st->bypass_tx_fir) { if (!(st->tx_fir_int == 1 || st->tx_fir_int == 2 || st->tx_fir_int == 4)) { dev_err(dev, "%s: Invalid: Interpolation %d in filter config", __func__, st->tx_fir_int); return -EINVAL; } if (st->tx_fir_int == 1 && st->tx_fir_ntaps > 64) { dev_err(dev, "%s: Invalid: TAPS > 64 and Interpolation = 1", __func__); return -EINVAL; } } if (!st->bypass_rx_fir) { if (!(st->rx_fir_dec == 1 || st->rx_fir_dec == 2 || st->rx_fir_dec == 4)) { dev_err(dev, "%s: Invalid: Decimation %d in filter config", __func__, st->rx_fir_dec); return -EINVAL; } } if (!st->filt_valid || st->bypass_rx_fir || st->bypass_tx_fir) { ret = ad9361_calculate_rf_clock_chain(phy, clk_get_rate(phy->clks[TX_SAMPL_CLK]), st->rate_governor, rx, tx); if (ret < 0) { u32 min = st->rate_governor ? 1500000U : 1000000U; dev_err(dev, "%s: Calculating filter rates failed %d " "using min frequency",__func__, ret); ret = ad9361_calculate_rf_clock_chain(phy, min, st->rate_governor, rx, tx); if (ret < 0) { return ret; } } valid = false; } else { memcpy(rx, st->filt_rx_path_clks, sizeof(rx)); memcpy(tx, st->filt_tx_path_clks, sizeof(tx)); valid = true; } #ifdef _DEBUG dev_dbg(&phy->spi->dev, "%s:RX %lu %lu %lu %lu %lu %lu", __func__, rx[BBPLL_FREQ], rx[ADC_FREQ], rx[R2_FREQ], rx[R1_FREQ], rx[CLKRF_FREQ], rx[RX_SAMPL_FREQ]); dev_dbg(&phy->spi->dev, "%s:TX %lu %lu %lu %lu %lu %lu", __func__, tx[BBPLL_FREQ], tx[ADC_FREQ], tx[R2_FREQ], tx[R1_FREQ], tx[CLKRF_FREQ], tx[RX_SAMPL_FREQ]); #endif if (!st->bypass_tx_fir) { max = (tx[DAC_FREQ] / tx[TX_SAMPL_FREQ]) * 16; if (st->tx_fir_ntaps > max) { dev_err(dev, "%s: Invalid: ratio ADC/2 / TX_SAMPL * 16 > TAPS" "(max %d, adc %lu, tx %lu)", __func__, max, rx[ADC_FREQ], tx[TX_SAMPL_FREQ]); return -EINVAL; } } if (!st->bypass_rx_fir) { max = ((rx[ADC_FREQ] / ((rx[ADC_FREQ] == rx[R2_FREQ]) ? 1 : 2)) / rx[RX_SAMPL_FREQ]) * 16; if (st->rx_fir_ntaps > max) { dev_err(dev, "%s: Invalid: ratio ADC/2 / RX_SAMPL * 16 > TAPS (max %d)", __func__, max); return -EINVAL; } } ret = ad9361_set_trx_clock_chain(phy, rx, tx); if (ret < 0) return ret; /* See also: ad9361_set_trx_clock_chain() */ if (!phy->pdata->dig_interface_tune_fir_disable && st->bypass_tx_fir && st->bypass_rx_fir) ad9361_dig_tune(phy, 0, RESTORE_DEFAULT); return ad9361_update_rf_bandwidth(phy, (valid && st->filt_rx_bw_Hz) ? st->filt_rx_bw_Hz : st->current_rx_bw_Hz, (valid && st->filt_tx_bw_Hz) ? st->filt_tx_bw_Hz : st->current_tx_bw_Hz); } static void ad9361_work_func(struct work_struct *work) { struct ad9361_rf_phy *phy = container_of(work, struct ad9361_rf_phy, work); struct ad9361_rf_phy_state *st = phy->state; int ret; dev_dbg(&phy->spi->dev, "%s:", __func__); ret = ad9361_do_calib_run(phy, TX_QUAD_CAL, st->last_tx_quad_cal_phase); if (ret < 0) dev_err(&phy->spi->dev, "%s: TX QUAD cal failed", __func__); complete_all(&phy->complete); clear_bit(0, &st->flags); } /* * AD9361 Clocks */ #define to_clk_priv(_hw) container_of(_hw, struct refclk_scale, hw) static inline int ad9361_set_muldiv(struct refclk_scale *priv, u32 mul, u32 div) { priv->mult = mul; priv->div = div; return 0; } static int ad9361_get_clk_scaler(struct clk_hw *hw) { struct refclk_scale *clk_priv = to_clk_priv(hw); struct spi_device *spi = clk_priv->spi; u32 tmp, tmp1; switch (clk_priv->source) { case BB_REFCLK: tmp = ad9361_spi_read(spi, REG_CLOCK_CTRL); tmp &= 0x3; break; case RX_REFCLK: tmp = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_1, RX_REF_DIVIDER_MSB); tmp1 = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_2, RX_REF_DIVIDER_LSB); tmp = (tmp << 1) | tmp1; break; case TX_REFCLK: tmp = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_2, TX_REF_DIVIDER(~0)); break; case ADC_CLK: tmp = ad9361_spi_read(spi, REG_BBPLL); return ad9361_set_muldiv(clk_priv, 1, 1 << (tmp & 0x7)); case R2_CLK: tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, DEC3_ENABLE_DECIMATION(~0)); return ad9361_set_muldiv(clk_priv, 1, tmp + 1); case R1_CLK: tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, RHB2_EN); return ad9361_set_muldiv(clk_priv, 1, tmp + 1); case CLKRF_CLK: tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, RHB1_EN); return ad9361_set_muldiv(clk_priv, 1, tmp + 1); case RX_SAMPL_CLK: tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0)); if (!tmp) tmp = 1; /* bypass filter */ else tmp = (1 << (tmp - 1)); return ad9361_set_muldiv(clk_priv, 1, tmp); case DAC_CLK: tmp = ad9361_spi_readf(spi, REG_BBPLL, BIT(3)); return ad9361_set_muldiv(clk_priv, 1, tmp + 1); case T2_CLK: tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, THB3_ENABLE_INTERP(~0)); return ad9361_set_muldiv(clk_priv, 1, tmp + 1); case T1_CLK: tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, THB2_EN); return ad9361_set_muldiv(clk_priv, 1, tmp + 1); case CLKTF_CLK: tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, THB1_EN); return ad9361_set_muldiv(clk_priv, 1, tmp + 1); case TX_SAMPL_CLK: tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0)); if (!tmp) tmp = 1; /* bypass filter */ else tmp = (1 << (tmp - 1)); return ad9361_set_muldiv(clk_priv, 1, tmp); default: return -EINVAL; } /* REFCLK Scaler */ switch (tmp) { case 0: ad9361_set_muldiv(clk_priv, 1, 1); break; case 1: ad9361_set_muldiv(clk_priv, 1, 2); break; case 2: ad9361_set_muldiv(clk_priv, 1, 4); break; case 3: ad9361_set_muldiv(clk_priv, 2, 1); break; default: return -EINVAL; } return 0; } static int ad9361_to_refclk_scaler(struct refclk_scale *clk_priv) { /* REFCLK Scaler */ switch (((clk_priv->mult & 0xF) << 4) | (clk_priv->div & 0xF)) { case 0x11: return 0; case 0x12: return 1; case 0x14: return 2; case 0x21: return 3; default: return -EINVAL; } }; static int ad9361_set_clk_scaler(struct clk_hw *hw, bool set) { struct refclk_scale *clk_priv = to_clk_priv(hw); struct spi_device *spi = clk_priv->spi; u32 tmp; int ret; switch (clk_priv->source) { case BB_REFCLK: ret = ad9361_to_refclk_scaler(clk_priv); if (ret < 0) return ret; if (set) return ad9361_spi_writef(spi, REG_CLOCK_CTRL, REF_FREQ_SCALER(~0), ret); break; case RX_REFCLK: ret = ad9361_to_refclk_scaler(clk_priv); if (ret < 0) return ret; if (set) { tmp = ret; ret = ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_1, RX_REF_DIVIDER_MSB, tmp >> 1); ret |= ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, RX_REF_DIVIDER_LSB, tmp & 1); return ret; } break; case TX_REFCLK: ret = ad9361_to_refclk_scaler(clk_priv); if (ret < 0) return ret; if (set) return ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, TX_REF_DIVIDER(~0), ret); break; case ADC_CLK: tmp = ilog2((u8)clk_priv->div); if (clk_priv->mult != 1 || tmp > 6 || tmp < 1) return -EINVAL; if (set) return ad9361_spi_writef(spi, REG_BBPLL, 0x7, tmp); break; case R2_CLK: if (clk_priv->mult != 1 || clk_priv->div > 3 || clk_priv->div < 1) return -EINVAL; if (set) return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, DEC3_ENABLE_DECIMATION(~0), clk_priv->div - 1); break; case R1_CLK: if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) return -EINVAL; if (set) return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, RHB2_EN, clk_priv->div - 1); break; case CLKRF_CLK: if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) return -EINVAL; if (set) return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, RHB1_EN, clk_priv->div - 1); break; case RX_SAMPL_CLK: if (clk_priv->mult != 1 || clk_priv->div > 4 || clk_priv->div < 1 || clk_priv->div == 3) return -EINVAL; if (clk_priv->phy->state->bypass_rx_fir) tmp = 0; else tmp = ilog2(clk_priv->div) + 1; if (set) return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0), tmp); break; case DAC_CLK: if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) return -EINVAL; if (set) return ad9361_spi_writef(spi, REG_BBPLL, BIT(3), clk_priv->div - 1); break; case T2_CLK: if (clk_priv->mult != 1 || clk_priv->div > 3 || clk_priv->div < 1) return -EINVAL; if (set) return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, THB3_ENABLE_INTERP(~0), clk_priv->div - 1); break; case T1_CLK: if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) return -EINVAL; if (set) return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, THB2_EN, clk_priv->div - 1); break; case CLKTF_CLK: if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) return -EINVAL; if (set) return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, THB1_EN, clk_priv->div - 1); break; case TX_SAMPL_CLK: if (clk_priv->mult != 1 || clk_priv->div > 4 || clk_priv->div < 1 || clk_priv->div == 3) return -EINVAL; if (clk_priv->phy->state->bypass_tx_fir) tmp = 0; else tmp = ilog2(clk_priv->div) + 1; if (set) return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0), tmp); break; default: return -EINVAL; } return 0; } static unsigned long ad9361_clk_factor_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct refclk_scale *clk_priv = to_clk_priv(hw); u64 rate; ad9361_get_clk_scaler(hw); rate = (parent_rate * clk_priv->mult) / clk_priv->div; return (unsigned long)rate; } static long ad9361_clk_factor_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct refclk_scale *clk_priv = to_clk_priv(hw); int ret; if (rate >= *prate) { clk_priv->mult = DIV_ROUND_CLOSEST(rate, *prate); clk_priv->div = 1; } else { clk_priv->div = DIV_ROUND_CLOSEST(*prate, rate); clk_priv->mult = 1; if (!clk_priv->div) { dev_err(&clk_priv->spi->dev, "%s: divide by zero", __func__); clk_priv->div = 1; } } ret = ad9361_set_clk_scaler(hw, false); if (ret < 0) return ret; return (*prate / clk_priv->div) * clk_priv->mult; } static int ad9361_clk_factor_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct refclk_scale *clk_priv = to_clk_priv(hw); dev_dbg(&clk_priv->spi->dev, "%s: Rate %lu Hz Parent Rate %lu Hz", __func__, rate, parent_rate); if (rate >= parent_rate) { clk_priv->mult = DIV_ROUND_CLOSEST(rate, parent_rate); clk_priv->div = 1; } else { clk_priv->div = DIV_ROUND_CLOSEST(parent_rate, rate); clk_priv->mult = 1; if (!clk_priv->div) { dev_err(&clk_priv->spi->dev, "%s: divide by zero", __func__); clk_priv->div = 1; } } return ad9361_set_clk_scaler(hw, true); } static const struct clk_ops refclk_scale_ops = { .round_rate = ad9361_clk_factor_round_rate, .set_rate = ad9361_clk_factor_set_rate, .recalc_rate = ad9361_clk_factor_recalc_rate, }; /* * BBPLL */ static unsigned long ad9361_bbpll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct refclk_scale *clk_priv = to_clk_priv(hw); u64 rate; unsigned long fract, integer; u8 buf[4]; ad9361_spi_readm(clk_priv->spi, REG_INTEGER_BB_FREQ_WORD, &buf[0], REG_INTEGER_BB_FREQ_WORD - REG_FRACT_BB_FREQ_WORD_1 + 1); fract = (buf[3] << 16) | (buf[2] << 8) | buf[1]; integer = buf[0]; rate = ((u64)parent_rate * fract); do_div(rate, BBPLL_MODULUS); rate += (u64)parent_rate * integer; return (unsigned long)rate; } static long ad9361_bbpll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { u64 tmp, rate64 = rate; u32 fract, integer; if (rate > MAX_BBPLL_FREQ) return MAX_BBPLL_FREQ; if (rate < MIN_BBPLL_FREQ) return MIN_BBPLL_FREQ; tmp = do_div(rate64, *prate); tmp = tmp * BBPLL_MODULUS + (*prate >> 1); do_div(tmp, *prate); integer = rate64; fract = tmp; tmp = *prate * (u64)fract; do_div(tmp, BBPLL_MODULUS); tmp += *prate * integer; return tmp; } static int ad9361_bbpll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct refclk_scale *clk_priv = to_clk_priv(hw); struct spi_device *spi = clk_priv->spi; u64 tmp, rate64 = rate; u32 fract, integer; int icp_val; u8 lf_defaults[3] = {0x35, 0x5B, 0xE8}; dev_dbg(&spi->dev, "%s: Rate %lu Hz Parent Rate %lu Hz", __func__, rate, parent_rate); /* * Setup Loop Filter and CP Current * Scale is 150uA @ (1280MHz BBPLL, 40MHz REFCLK) */ tmp = (rate64 >> 7) * 150ULL; do_div(tmp, (parent_rate >> 7) * 32UL); /* 25uA/LSB, Offset 25uA */ icp_val = DIV_ROUND_CLOSEST((u32)tmp, 25U) - 1; icp_val = clamp(icp_val, 1, 64); ad9361_spi_write(spi, REG_CP_CURRENT, icp_val); ad9361_spi_writem(spi, REG_LOOP_FILTER_3, lf_defaults, ARRAY_SIZE(lf_defaults)); /* Allow calibration to occur and set cal count to 1024 for max accuracy */ ad9361_spi_write(spi, REG_VCO_CTRL, FREQ_CAL_ENABLE | FREQ_CAL_COUNT_LENGTH(3)); /* Set calibration clock to REFCLK/4 for more accuracy */ ad9361_spi_write(spi, REG_SDM_CTRL, 0x10); /* Calculate and set BBPLL frequency word */ tmp = do_div(rate64, parent_rate); tmp = tmp *(u64) BBPLL_MODULUS + (parent_rate >> 1); do_div(tmp, parent_rate); integer = rate64; fract = tmp; ad9361_spi_write(spi, REG_INTEGER_BB_FREQ_WORD, integer); ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_3, fract); ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_2, fract >> 8); ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_1, fract >> 16); ad9361_spi_write(spi, REG_SDM_CTRL_1, INIT_BB_FO_CAL | BBPLL_RESET_BAR); /* Start BBPLL Calibration */ ad9361_spi_write(spi, REG_SDM_CTRL_1, BBPLL_RESET_BAR); /* Clear BBPLL start calibration bit */ ad9361_spi_write(spi, REG_VCO_PROGRAM_1, 0x86); /* Increase BBPLL KV and phase margin */ ad9361_spi_write(spi, REG_VCO_PROGRAM_2, 0x01); /* Increase BBPLL KV and phase margin */ ad9361_spi_write(spi, REG_VCO_PROGRAM_2, 0x05); /* Increase BBPLL KV and phase margin */ return ad9361_check_cal_done(clk_priv->phy, REG_CH_1_OVERFLOW, BBPLL_LOCK, 1); } static const struct clk_ops bbpll_clk_ops = { .round_rate = ad9361_bbpll_round_rate, .set_rate = ad9361_bbpll_set_rate, .recalc_rate = ad9361_bbpll_recalc_rate, }; /* * RFPLL */ static u64 ad9361_calc_rfpll_freq(u64 parent_rate, u64 integer, u64 fract, u32 vco_div) { u64 rate; rate = parent_rate * fract; do_div(rate, RFPLL_MODULUS); rate += parent_rate * integer; return rate >> (vco_div + 1); } static int ad9361_calc_rfpll_divder(struct ad9361_rf_phy *phy, struct refclk_scale *clk_priv, u64 freq, u64 parent_rate, u32 *integer, u32 *fract, int *vco_div, u64 *vco_freq) { u64 tmp; int div, ret; ret = ad9361_validate_rfpll(phy, clk_priv->source == TX_RFPLL_INT, freq); if (ret) return ret; div = -1; while (freq <= MIN_VCO_FREQ_HZ) { freq <<= 1; div++; } *vco_div = div; *vco_freq = freq; tmp = do_div(freq, parent_rate); tmp = tmp * RFPLL_MODULUS + (parent_rate >> 1); do_div(tmp, parent_rate); *integer = freq; *fract = tmp; return 0; } static unsigned long ad9361_rfpll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct refclk_scale *clk_priv = to_clk_priv(hw); struct ad9361_rf_phy *phy = clk_priv->phy; struct ad9361_rf_phy_state *st = phy->state; unsigned long fract, integer; u8 buf[5]; u32 reg, div_mask, vco_div, profile; dev_dbg(&clk_priv->spi->dev, "%s: Parent Rate %lu Hz", __func__, parent_rate); switch (clk_priv->source) { case RX_RFPLL_INT: reg = REG_RX_FRACT_BYTE_2; div_mask = RX_VCO_DIVIDER(~0); profile = st->fastlock.current_profile[0]; break; case TX_RFPLL_INT: reg = REG_TX_FRACT_BYTE_2; div_mask = TX_VCO_DIVIDER(~0); profile = st->fastlock.current_profile[1]; break; default: return -EINVAL; } if (profile) { bool tx = clk_priv->source == TX_RFPLL_INT; profile = profile - 1; buf[0] = ad9361_fastlock_readval(phy->spi, tx, profile, 4); buf[1] = ad9361_fastlock_readval(phy->spi, tx, profile, 3); buf[2] = ad9361_fastlock_readval(phy->spi, tx, profile, 2); buf[3] = ad9361_fastlock_readval(phy->spi, tx, profile, 1); buf[4] = ad9361_fastlock_readval(phy->spi, tx, profile, 0); vco_div = ad9361_fastlock_readval(phy->spi, tx, profile, 12) & 0xF; } else { ad9361_spi_readm(clk_priv->spi, reg, &buf[0], ARRAY_SIZE(buf)); vco_div = ad9361_spi_readf(clk_priv->spi, REG_RFPLL_DIVIDERS, div_mask); } fract = (SYNTH_FRACT_WORD(buf[0]) << 16) | (buf[1] << 8) | buf[2]; integer = (SYNTH_INTEGER_WORD(buf[3]) << 8) | buf[4]; return ad9361_to_clk(ad9361_calc_rfpll_freq(parent_rate, integer, fract, vco_div)); } static int ad9361_rfpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct refclk_scale *clk_priv = to_clk_priv(hw); struct ad9361_rf_phy *phy = clk_priv->phy; int ret; dev_dbg(&clk_priv->spi->dev, "%s: Rate %lu Hz", __func__, req->rate); ret = ad9361_validate_rfpll(phy, clk_priv->source == TX_RFPLL_INT, ad9361_from_clk(req->rate)); if (ret) return ret; return 0; } static int ad9361_rfpll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct refclk_scale *clk_priv = to_clk_priv(hw); struct ad9361_rf_phy *phy = clk_priv->phy; struct ad9361_rf_phy_state *st = phy->state; u64 vco; u8 buf[5]; u32 reg, div_mask, lock_reg, fract, integer; int vco_div, ret, fixup_other; dev_dbg(&clk_priv->spi->dev, "%s: %s Rate %lu Hz Parent Rate %lu Hz", __func__, clk_priv->source == TX_RFPLL_INT ? "TX" : "RX", rate, parent_rate); ad9361_fastlock_prepare(phy, clk_priv->source == TX_RFPLL_INT, 0, false); ret = ad9361_calc_rfpll_divder(phy, clk_priv, ad9361_from_clk(rate), parent_rate, &integer, &fract, &vco_div, &vco); if (ret) return ret; switch (clk_priv->source) { case RX_RFPLL_INT: reg = REG_RX_FRACT_BYTE_2; lock_reg = REG_RX_CP_OVERRANGE_VCO_LOCK; div_mask = RX_VCO_DIVIDER(~0); st->cached_rx_rfpll_div = vco_div; st->current_rx_lo_freq = rate; break; case TX_RFPLL_INT: reg = REG_TX_FRACT_BYTE_2; lock_reg = REG_TX_CP_OVERRANGE_VCO_LOCK; div_mask = TX_VCO_DIVIDER(~0); st->cached_tx_rfpll_div = vco_div; st->current_tx_lo_freq = rate; break; default: return -EINVAL; } /* Option to skip VCO cal in TDD mode when moving from TX/RX to Alert */ if (phy->pdata->tdd_skip_vco_cal) ad9361_trx_vco_cal_control(phy, clk_priv->source == TX_RFPLL_INT, true); do { fixup_other = 0; ad9361_rfpll_vco_init(phy, div_mask == TX_VCO_DIVIDER(~0), vco, parent_rate); buf[0] = SYNTH_FRACT_WORD(fract >> 16); buf[1] = fract >> 8; buf[2] = fract & 0xFF; buf[3] = SYNTH_INTEGER_WORD(integer >> 8) | (~SYNTH_INTEGER_WORD(~0) & ad9361_spi_read(clk_priv->spi, reg - 3)); buf[4] = integer & 0xFF; ad9361_spi_writem(clk_priv->spi, reg, buf, 5); ad9361_spi_writef(clk_priv->spi, REG_RFPLL_DIVIDERS, div_mask, vco_div); ret = ad9361_check_cal_done(phy, lock_reg, VCO_LOCK, 1); /* In FDD mode with RX LO == TX LO frequency we use TDD tables to * reduce VCO pulling */ if (((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) && (st->current_tx_lo_freq == st->current_rx_lo_freq) && (st->current_tx_use_tdd_table != st->current_rx_use_tdd_table)) || ((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) && (st->current_tx_lo_freq != st->current_rx_lo_freq) && (st->current_tx_use_tdd_table || st->current_rx_use_tdd_table))) { unsigned long _rate; switch (clk_priv->source) { case RX_RFPLL_INT: reg = REG_TX_FRACT_BYTE_2; lock_reg = REG_TX_CP_OVERRANGE_VCO_LOCK; div_mask = TX_VCO_DIVIDER(~0); _rate = st->current_tx_lo_freq; break; case TX_RFPLL_INT: reg = REG_RX_FRACT_BYTE_2; lock_reg = REG_RX_CP_OVERRANGE_VCO_LOCK; div_mask = RX_VCO_DIVIDER(~0); _rate = st->current_rx_lo_freq; break; default: return -EINVAL; } if (st->current_tx_lo_freq != st->current_rx_lo_freq) { ad9361_calc_rfpll_divder(phy, clk_priv, ad9361_from_clk(_rate), parent_rate, &integer, &fract, &vco_div, &vco); ad9361_fastlock_prepare(phy, clk_priv->source == RX_RFPLL_INT, 0, false); } fixup_other = 1; } } while (fixup_other); if (phy->pdata->tdd_skip_vco_cal) ad9361_trx_vco_cal_control(phy, clk_priv->source == TX_RFPLL_INT, false); return ret; } static const struct clk_ops rfpll_clk_ops_int = { .determine_rate = ad9361_rfpll_determine_rate, .set_rate = ad9361_rfpll_set_rate, .recalc_rate = ad9361_rfpll_recalc_rate, }; static unsigned long ad9361_rfpll_dummy_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct refclk_scale *clk_priv = to_clk_priv(hw); return clk_priv->rate; } static int ad9361_rfpll_dummy_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct refclk_scale *clk_priv = to_clk_priv(hw); clk_priv->rate = rate; return 0; } static const struct clk_ops rfpll_dummy_clk_ops_int = { .determine_rate = ad9361_rfpll_determine_rate, .set_rate = ad9361_rfpll_dummy_set_rate, .recalc_rate = ad9361_rfpll_dummy_recalc_rate, }; static u8 ad9361_clk_mux_get_parent(struct clk_hw *hw) { struct refclk_scale *clk_priv = to_clk_priv(hw); dev_dbg(&clk_priv->spi->dev, "%s: index %d", __func__, clk_priv->mult); return clk_priv->mult; } static int ad9361_clk_mux_set_parent(struct clk_hw *hw, u8 index) { struct refclk_scale *clk_priv = to_clk_priv(hw); struct ad9361_rf_phy *phy = clk_priv->phy; int ret; dev_dbg(&clk_priv->spi->dev, "%s: index %d", __func__, index); ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); ret = ad9361_trx_ext_lo_control(phy, clk_priv->source == TX_RFPLL, index == 1); if (ret >= 0) clk_priv->mult = index; ad9361_ensm_restore_prev_state(phy); return ret; } static const struct clk_ops rfpll_clk_ops = { .get_parent = ad9361_clk_mux_get_parent, .set_parent = ad9361_clk_mux_set_parent, .determine_rate = __clk_mux_determine_rate, }; static int ad9361_rx_rfpll_rate_change(struct notifier_block *nb, unsigned long flags, void *data) { struct clk_notifier_data *cnd = data; struct ad9361_rf_phy *phy = container_of(nb, struct ad9361_rf_phy, clk_nb_rx); u64 new_rate; if (flags == POST_RATE_CHANGE) { new_rate = ad9361_from_clk(cnd->new_rate); dev_dbg(&phy->spi->dev, "%s: rate %llu Hz", __func__, new_rate); if (cnd->new_rate) ad9361_load_gt(phy, new_rate, GT_RX1 + GT_RX2); ad9361_adjust_rx_ext_band_settings(phy, new_rate); } return NOTIFY_OK; } static int ad9361_tx_rfpll_rate_change(struct notifier_block *nb, unsigned long flags, void *data) { struct clk_notifier_data *cnd = data; struct ad9361_rf_phy *phy = container_of(nb, struct ad9361_rf_phy, clk_nb_tx); struct ad9361_rf_phy_state *st = phy->state; u64 new_rate; if (flags == POST_RATE_CHANGE) { new_rate = ad9361_from_clk(cnd->new_rate); dev_dbg(&phy->spi->dev, "%s: rate %llu Hz", __func__, new_rate); /* For RX LO we typically have the tracking option enabled * so for now do nothing here. */ if (st->auto_cal_en) if (abs(st->last_tx_quad_cal_freq - new_rate) > st->cal_threshold_freq) { set_bit(0, &st->flags); reinit_completion(&phy->complete); schedule_work(&phy->work); st->last_tx_quad_cal_freq = new_rate; } ad9361_adjust_tx_ext_band_settings(phy, new_rate); } return NOTIFY_OK; } #define AD9361_MAX_CLK_NAME 79 static char *ad9361_clk_set_dev_name(struct ad9361_rf_phy *phy, char *dest, const char *name) { size_t len = 0; if (name == NULL) return NULL; if (*name == '-') len = strlcpy(dest, dev_name(&phy->spi->dev), AD9361_MAX_CLK_NAME); else *dest = '\0'; return strncat(dest, name, AD9361_MAX_CLK_NAME - len); } static int ad9361_clk_register(struct ad9361_rf_phy *phy, const char *name, const char *parent_name, const char *parent_name2, unsigned long flags, u32 source) { struct refclk_scale *clk_priv = &phy->clk_priv[source]; struct clk_init_data init; struct clk *clk; char c_name[AD9361_MAX_CLK_NAME + 1], p_name[2][AD9361_MAX_CLK_NAME + 1]; const char *_parent_name[2]; /* struct refclk_scale assignments */ clk_priv->source = source; clk_priv->hw.init = &init; clk_priv->spi = phy->spi; clk_priv->phy = phy; _parent_name[0] = ad9361_clk_set_dev_name(phy, p_name[0], parent_name); _parent_name[1] = ad9361_clk_set_dev_name(phy, p_name[1], parent_name2); init.name = ad9361_clk_set_dev_name(phy, c_name, name);; init.flags = flags; init.parent_names = &_parent_name[0]; init.num_parents = _parent_name[1] ? 2 : _parent_name[0] ? 1 : 0; switch (source) { case BBPLL_CLK: init.ops = &bbpll_clk_ops; break; case RX_RFPLL_INT: case TX_RFPLL_INT: init.ops = &rfpll_clk_ops_int; break; case RX_RFPLL_DUMMY: init.ops = &rfpll_dummy_clk_ops_int; clk_priv->rate = ad9361_to_clk(phy->pdata->rx_synth_freq); break; case TX_RFPLL_DUMMY: init.ops = &rfpll_dummy_clk_ops_int; clk_priv->rate = ad9361_to_clk(phy->pdata->tx_synth_freq); break; case RX_RFPLL: case TX_RFPLL: init.ops = &rfpll_clk_ops; break; default: init.ops = &refclk_scale_ops; } clk = devm_clk_register(&phy->spi->dev, &clk_priv->hw); phy->clks[source] = clk; return 0; } static int ad9361_clks_disable(struct ad9361_rf_phy *phy) { clk_disable_unprepare(phy->clks[TX_RFPLL]); clk_disable_unprepare(phy->clks[RX_RFPLL]); return 0; } static int ad9361_clks_resync(struct ad9361_rf_phy *phy) { int i; for (i = TX_RFPLL; i >= 0; i--) clk_get_rate(phy->clks[i]); return 0; } static int register_clocks(struct ad9361_rf_phy *phy) { const char *parent_name; const char *ext_tx_lo = NULL; const char *ext_rx_lo = NULL; u32 flags = CLK_GET_RATE_NOCACHE; int ret; parent_name = __clk_get_name(phy->clk_refin); phy->clk_data.clks = phy->clks; phy->clk_data.clk_num = NUM_AD9361_CLKS; /* Dummy Clock in case no external LO clock given */ phy->clk_ext_lo_rx = devm_clk_get(&phy->spi->dev, "ext_rx_lo"); phy->clk_ext_lo_tx = devm_clk_get(&phy->spi->dev, "ext_tx_lo"); if (PTR_ERR(phy->clk_ext_lo_rx) == -EPROBE_DEFER) return -EPROBE_DEFER; if (PTR_ERR(phy->clk_ext_lo_tx) == -EPROBE_DEFER) return -EPROBE_DEFER; if (IS_ERR_OR_NULL(phy->clk_ext_lo_rx)) { ad9361_clk_register(phy, "-rx_lo_dummy", NULL, NULL, CLK_IGNORE_UNUSED, RX_RFPLL_DUMMY); phy->clk_ext_lo_rx = phy->clks[RX_RFPLL_DUMMY]; ext_rx_lo = "-rx_lo_dummy"; } else { ext_rx_lo = __clk_get_name(phy->clk_ext_lo_rx); } if (IS_ERR_OR_NULL(phy->clk_ext_lo_tx)) { ad9361_clk_register(phy, "-tx_lo_dummy", NULL, NULL, CLK_IGNORE_UNUSED, TX_RFPLL_DUMMY); phy->clk_ext_lo_tx = phy->clks[TX_RFPLL_DUMMY]; ext_tx_lo = "-tx_lo_dummy"; } else { ext_tx_lo = __clk_get_name(phy->clk_ext_lo_tx); } /* Scaled Reference Clocks */ ad9361_clk_register(phy, "-tx_refclk", parent_name, NULL, flags | CLK_IGNORE_UNUSED, TX_REFCLK); ad9361_clk_register(phy, "-rx_refclk", parent_name, NULL, flags | CLK_IGNORE_UNUSED, RX_REFCLK); ad9361_clk_register(phy, "-bb_refclk", parent_name, NULL, flags | CLK_IGNORE_UNUSED, BB_REFCLK); /* Base Band PLL Clock */ ad9361_clk_register(phy, "-bbpll_clk", "-bb_refclk", NULL, flags | CLK_IGNORE_UNUSED, BBPLL_CLK); ad9361_clk_register(phy, "-adc_clk", "-bbpll_clk", NULL, flags | CLK_IGNORE_UNUSED, ADC_CLK); ad9361_clk_register(phy, "-r2_clk", "-adc_clk", NULL, flags | CLK_IGNORE_UNUSED, R2_CLK); ad9361_clk_register(phy, "-r1_clk", "-r2_clk", NULL, flags | CLK_IGNORE_UNUSED, R1_CLK); ad9361_clk_register(phy, "-clkrf_clk", "-r1_clk", NULL, flags | CLK_IGNORE_UNUSED, CLKRF_CLK); ad9361_clk_register(phy, "-rx_sampl_clk", "-clkrf_clk", NULL, flags | CLK_IGNORE_UNUSED, RX_SAMPL_CLK); ad9361_clk_register(phy, "-dac_clk", "-adc_clk", NULL, flags | CLK_IGNORE_UNUSED, DAC_CLK); ad9361_clk_register(phy, "-t2_clk", "-dac_clk", NULL, flags | CLK_IGNORE_UNUSED, T2_CLK); ad9361_clk_register(phy, "-t1_clk", "-t2_clk", NULL, flags | CLK_IGNORE_UNUSED, T1_CLK); ad9361_clk_register(phy, "-clktf_clk", "-t1_clk", NULL, flags | CLK_IGNORE_UNUSED, CLKTF_CLK); ad9361_clk_register(phy, "-tx_sampl_clk", "-clktf_clk", NULL, flags | CLK_IGNORE_UNUSED, TX_SAMPL_CLK); ad9361_clk_register(phy, "-rx_rfpll_int", "-rx_refclk", NULL, flags | CLK_IGNORE_UNUSED, RX_RFPLL_INT); ad9361_clk_register(phy, "-tx_rfpll_int", "-tx_refclk", NULL, flags | CLK_IGNORE_UNUSED, TX_RFPLL_INT); ad9361_clk_register(phy, "-rx_rfpll", "-rx_rfpll_int", ext_rx_lo, flags | CLK_IGNORE_UNUSED | CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, RX_RFPLL); ad9361_clk_register(phy, "-tx_rfpll", "-tx_rfpll_int", ext_tx_lo, flags | CLK_IGNORE_UNUSED | CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, TX_RFPLL); phy->clk_nb_rx.notifier_call = ad9361_rx_rfpll_rate_change; ret = clk_notifier_register(phy->clks[RX_RFPLL], &phy->clk_nb_rx); if (ret < 0) return ret; phy->clk_nb_tx.notifier_call = ad9361_tx_rfpll_rate_change; ret = clk_notifier_register(phy->clks[TX_RFPLL], &phy->clk_nb_tx); if (ret < 0) return ret; return 0; } enum ad9361_iio_dev_attr { AD9361_RF_RX_BANDWIDTH, AD9361_RF_TX_BANDWIDTH, AD9361_ENSM_MODE, AD9361_ENSM_MODE_AVAIL, AD9361_CALIB_MODE, AD9361_CALIB_MODE_AVAIL, AD9361_RSSI_GAIN_STEP_ERROR, AD9361_RX_PATH_FREQ, AD9361_TX_PATH_FREQ, AD9361_TRX_RATE_GOV, AD9361_TRX_RATE_GOV_AVAIL, AD9361_FIR_RX_ENABLE, AD9361_FIR_TX_ENABLE, AD9361_FIR_TRX_ENABLE, AD9361_BBDC_OFFS_ENABLE, AD9361_RFDC_OFFS_ENABLE, AD9361_QUAD_ENABLE, AD9361_DCXO_TUNE_COARSE, AD9361_DCXO_TUNE_COARSE_AVAILABLE, AD9361_DCXO_TUNE_FINE, AD9361_DCXO_TUNE_FINE_AVAILABLE, AD9361_XO_CORRECTION, AD9361_XO_CORRECTION_AVAILABLE, AD9361_MCS_SYNC, }; static ssize_t ad9361_phy_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; long readin; int ret = 0, arg = -1; u32 val; bool res; if (st->curr_ensm_state == ENSM_STATE_SLEEP && this_attr->address != AD9361_ENSM_MODE) return -EINVAL; mutex_lock(&indio_dev->mlock); switch ((u32)this_attr->address) { case AD9361_RF_RX_BANDWIDTH: ret = kstrtol(buf, 10, &readin); if (ret) break; readin = ad9361_validate_rf_bw(phy, readin); if (st->current_rx_bw_Hz != readin) ret = ad9361_update_rf_bandwidth(phy, readin, st->current_tx_bw_Hz); else ret = 0; break; case AD9361_RF_TX_BANDWIDTH: ret = kstrtol(buf, 10, &readin); if (ret) break; readin = ad9361_validate_rf_bw(phy, readin); if (st->current_tx_bw_Hz != readin) ret = ad9361_update_rf_bandwidth(phy, st->current_rx_bw_Hz, readin); else ret = 0; break; case AD9361_ENSM_MODE: res = false; phy->pdata->fdd_independent_mode = false; if (sysfs_streq(buf, "tx")) val = ENSM_STATE_TX; else if (sysfs_streq(buf, "rx")) val = ENSM_STATE_RX; else if (sysfs_streq(buf, "alert")) val = ENSM_STATE_ALERT; else if (sysfs_streq(buf, "fdd")) val = ENSM_STATE_FDD; else if (sysfs_streq(buf, "wait")) val = ENSM_STATE_SLEEP_WAIT; else if (sysfs_streq(buf, "sleep")) val = ENSM_STATE_SLEEP; else if (sysfs_streq(buf, "pinctrl")) { res = true; val = ENSM_STATE_SLEEP_WAIT; } else if (sysfs_streq(buf, "pinctrl_fdd_indep")) { val = ENSM_STATE_FDD; phy->pdata->fdd_independent_mode = true; } else break; ad9361_set_ensm_mode(phy, phy->pdata->fdd, res); ret = ad9361_ensm_set_state(phy, val, res); break; case AD9361_TRX_RATE_GOV: if (sysfs_streq(buf, "highest_osr")) st->rate_governor = 0; else if (sysfs_streq(buf, "nominal")) st->rate_governor = 1; else ret = -EINVAL; break; case AD9361_FIR_TRX_ENABLE: ret = strtobool(buf, &res); if (ret < 0) break; if ((st->bypass_rx_fir == st->bypass_tx_fir) && (st->bypass_rx_fir == !res)) break; st->bypass_rx_fir = st->bypass_tx_fir = !res; ret = ad9361_validate_enable_fir(phy); if (ret < 0) { st->bypass_rx_fir = true; st->bypass_tx_fir = true; } break; case AD9361_FIR_RX_ENABLE: ret = strtobool(buf, &res); if (ret < 0) break; if (st->bypass_rx_fir == !res) break; st->bypass_rx_fir = !res; ret = ad9361_validate_enable_fir(phy); if (ret < 0) { st->bypass_rx_fir = true; } break; case AD9361_FIR_TX_ENABLE: ret = strtobool(buf, &res); if (ret < 0) break; if (st->bypass_tx_fir == !res) break; st->bypass_tx_fir = !res; ret = ad9361_validate_enable_fir(phy); if (ret < 0) { st->bypass_tx_fir = true; } break; case AD9361_CALIB_MODE: val = 0; if (sysfs_streq(buf, "auto")) { st->auto_cal_en = true; st->manual_tx_quad_cal_en = false; } else if (sysfs_streq(buf, "manual")) { st->auto_cal_en = false; st->manual_tx_quad_cal_en = false; } else if (sysfs_streq(buf, "manual_tx_quad")) { st->auto_cal_en = false; st->manual_tx_quad_cal_en = true; } else if (!strncmp(buf, "tx_quad", 7)) { ret = sscanf(buf, "tx_quad %d", &arg); if (ret != 1) arg = -1; val = TX_QUAD_CAL; } else if (sysfs_streq(buf, "rf_dc_offs")) val = RFDC_CAL; else if (sysfs_streq(buf, "rssi_gain_step")) ret = ad9361_rssi_gain_step_calib(phy); else break; if (val) ret = ad9361_do_calib_run(phy, val, arg); break; case AD9361_RSSI_GAIN_STEP_ERROR: ret = sscanf(buf, "lna_error: %d %d %d %d " "mixer_error: %d %d %d %d %d %d %d %d " "%d %d %d %d %d %d %d %d " "gain_step_calib_reg_val: %d %d %d %d %d", &phy->pdata->rssi_lna_err_tbl[0], &phy->pdata->rssi_lna_err_tbl[1], &phy->pdata->rssi_lna_err_tbl[2], &phy->pdata->rssi_lna_err_tbl[3], &phy->pdata->rssi_mixer_err_tbl[0], &phy->pdata->rssi_mixer_err_tbl[1], &phy->pdata->rssi_mixer_err_tbl[2], &phy->pdata->rssi_mixer_err_tbl[3], &phy->pdata->rssi_mixer_err_tbl[4], &phy->pdata->rssi_mixer_err_tbl[5], &phy->pdata->rssi_mixer_err_tbl[6], &phy->pdata->rssi_mixer_err_tbl[7], &phy->pdata->rssi_mixer_err_tbl[8], &phy->pdata->rssi_mixer_err_tbl[9], &phy->pdata->rssi_mixer_err_tbl[10], &phy->pdata->rssi_mixer_err_tbl[11], &phy->pdata->rssi_mixer_err_tbl[12], &phy->pdata->rssi_mixer_err_tbl[13], &phy->pdata->rssi_mixer_err_tbl[14], &phy->pdata->rssi_mixer_err_tbl[15], &phy->pdata->rssi_gain_step_calib_reg_val[0], &phy->pdata->rssi_gain_step_calib_reg_val[1], &phy->pdata->rssi_gain_step_calib_reg_val[2], &phy->pdata->rssi_gain_step_calib_reg_val[3], &phy->pdata->rssi_gain_step_calib_reg_val[4]); if (ret == 25) { ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); ad9361_rssi_program_lna_gain(phy); ad9361_rssi_write_err_tbl(phy); ad9361_spi_write(phy->spi, REG_SETTLE_TIME, ENABLE_DIG_GAIN_CORR | SETTLE_TIME(0x10)); ad9361_ensm_restore_prev_state(phy); ret = 0; } else ret = -EINVAL; break; case AD9361_BBDC_OFFS_ENABLE: ret = strtobool(buf, &st->bbdc_track_en); if (ret < 0) break; ret = ad9361_tracking_control(phy, st->bbdc_track_en, st->rfdc_track_en, st->quad_track_en); break; case AD9361_RFDC_OFFS_ENABLE: ret = strtobool(buf, &st->rfdc_track_en); if (ret < 0) break; ret = ad9361_tracking_control(phy, st->bbdc_track_en, st->rfdc_track_en, st->quad_track_en); break; case AD9361_QUAD_ENABLE: ret = strtobool(buf, &st->quad_track_en); if (ret < 0) break; ret = ad9361_tracking_control(phy, st->bbdc_track_en, st->rfdc_track_en, st->quad_track_en); break; case AD9361_DCXO_TUNE_COARSE: ret = kstrtol(buf, 10, &readin); if (ret) break; val = clamp_t(u32, (u32)readin, 0 , 63U); if (val == phy->pdata->dcxo_coarse) break; phy->pdata->dcxo_coarse = val; ret = ad9361_set_dcxo_tune(phy, phy->pdata->dcxo_coarse, phy->pdata->dcxo_fine); break; case AD9361_DCXO_TUNE_FINE: ret = kstrtol(buf, 10, &readin); if (ret) break; val = clamp_t(u32, (u32)readin, 0 , 8191U); if (val == phy->pdata->dcxo_fine) break; phy->pdata->dcxo_fine = val; ret = ad9361_set_dcxo_tune(phy, phy->pdata->dcxo_coarse, phy->pdata->dcxo_fine); break; case AD9361_XO_CORRECTION: { unsigned long rx, tx; ret = kstrtol(buf, 10, &readin); if (ret) break; if (readin == clk_get_rate(phy->clk_refin)) break; rx = st->current_rx_lo_freq; tx = st->current_tx_lo_freq; ret = clk_set_rate(phy->clk_refin, (unsigned long) readin); if (ret < 0) break; ad9361_set_trx_clock_chain(phy, st->current_rx_path_clks, st->current_tx_path_clks); clk_set_rate(phy->clks[RX_RFPLL], rx); clk_set_rate(phy->clks[TX_RFPLL], tx); break; } case AD9361_MCS_SYNC: ret = kstrtol(buf, 10, &readin); if (ret) break; ret = ad9361_mcs(phy, readin); break; default: ret = -EINVAL; } mutex_unlock(&indio_dev->mlock); return ret ? ret : len; } static ssize_t ad9361_phy_show(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; int ret = 0; unsigned long clk[6]; u64 delta; mutex_lock(&indio_dev->mlock); switch ((u32)this_attr->address) { case AD9361_RF_RX_BANDWIDTH: ret = sprintf(buf, "%u\n", st->current_rx_bw_Hz); break; case AD9361_RF_TX_BANDWIDTH: ret = sprintf(buf, "%u\n", st->current_tx_bw_Hz); break; case AD9361_ENSM_MODE: ret = ad9361_spi_readf(phy->spi, REG_STATE, ENSM_STATE(~0)); if (ret < 0) break; if (ret >= ARRAY_SIZE(ad9361_ensm_states) || ad9361_ensm_states[ret] == NULL) { ret = -EIO; break; } ret = sprintf(buf, "%s\n", ad9361_ensm_states[ret]); break; case AD9361_ENSM_MODE_AVAIL: ret = sprintf(buf, "%s\n", phy->pdata->fdd ? "sleep wait alert fdd pinctrl pinctrl_fdd_indep" : "sleep wait alert rx tx pinctrl"); break; case AD9361_TX_PATH_FREQ: ad9361_get_trx_clock_chain(phy, NULL, clk); ret = sprintf(buf, "BBPLL:%lu DAC:%lu T2:%lu T1:%lu TF:%lu TXSAMP:%lu\n", clk[0], clk[1], clk[2], clk[3], clk[4], clk[5]); break; case AD9361_RX_PATH_FREQ: ad9361_get_trx_clock_chain(phy, clk, NULL); ret = sprintf(buf, "BBPLL:%lu ADC:%lu R2:%lu R1:%lu RF:%lu RXSAMP:%lu\n", clk[0], clk[1], clk[2], clk[3], clk[4], clk[5]); break; case AD9361_TRX_RATE_GOV: ret = sprintf(buf, "%s\n", st->rate_governor ? "nominal" : "highest_osr"); break; case AD9361_TRX_RATE_GOV_AVAIL: ret = sprintf(buf, "%s\n", "nominal highest_osr"); break; case AD9361_FIR_RX_ENABLE: ret = sprintf(buf, "%d\n", !st->bypass_rx_fir); break; case AD9361_FIR_TX_ENABLE: ret = sprintf(buf, "%d\n", !st->bypass_tx_fir); break; case AD9361_FIR_TRX_ENABLE: ret = sprintf(buf, "%d\n", !st->bypass_tx_fir && !st->bypass_rx_fir); break; case AD9361_CALIB_MODE_AVAIL: ret = sprintf(buf, "auto manual manual_tx_quad tx_quad rf_dc_offs rssi_gain_step\n"); break; case AD9361_CALIB_MODE: if (st->manual_tx_quad_cal_en) ret = sprintf(buf, "manual_tx_quad %d\n", st->last_tx_quad_cal_phase); else ret = sprintf(buf, "%s\n", st->auto_cal_en ? "auto" : "manual"); break; case AD9361_RSSI_GAIN_STEP_ERROR: ret = sprintf(buf, "lna_error: %d %d %d %d\n" "mixer_error: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n" "gain_step_calib_reg_val: %d %d %d %d %d\n", phy->pdata->rssi_lna_err_tbl[0], phy->pdata->rssi_lna_err_tbl[1], phy->pdata->rssi_lna_err_tbl[2], phy->pdata->rssi_lna_err_tbl[3], phy->pdata->rssi_mixer_err_tbl[0], phy->pdata->rssi_mixer_err_tbl[1], phy->pdata->rssi_mixer_err_tbl[2], phy->pdata->rssi_mixer_err_tbl[3], phy->pdata->rssi_mixer_err_tbl[4], phy->pdata->rssi_mixer_err_tbl[5], phy->pdata->rssi_mixer_err_tbl[6], phy->pdata->rssi_mixer_err_tbl[7], phy->pdata->rssi_mixer_err_tbl[8], phy->pdata->rssi_mixer_err_tbl[9], phy->pdata->rssi_mixer_err_tbl[10], phy->pdata->rssi_mixer_err_tbl[11], phy->pdata->rssi_mixer_err_tbl[12], phy->pdata->rssi_mixer_err_tbl[13], phy->pdata->rssi_mixer_err_tbl[14], phy->pdata->rssi_mixer_err_tbl[15], phy->pdata->rssi_gain_step_calib_reg_val[0], phy->pdata->rssi_gain_step_calib_reg_val[1], phy->pdata->rssi_gain_step_calib_reg_val[2], phy->pdata->rssi_gain_step_calib_reg_val[3], phy->pdata->rssi_gain_step_calib_reg_val[4]); break; case AD9361_BBDC_OFFS_ENABLE: ret = sprintf(buf, "%d\n", st->bbdc_track_en); break; case AD9361_RFDC_OFFS_ENABLE: ret = sprintf(buf, "%d\n", st->rfdc_track_en); break; case AD9361_QUAD_ENABLE: ret = sprintf(buf, "%d\n", st->quad_track_en); break; case AD9361_DCXO_TUNE_COARSE: if (phy->pdata->use_extclk) ret = -ENODEV; else ret = sprintf(buf, "%d\n", phy->pdata->dcxo_coarse); break; case AD9361_DCXO_TUNE_FINE: if (phy->pdata->use_extclk) ret = -ENODEV; else ret = sprintf(buf, "%d\n", phy->pdata->dcxo_fine); break; case AD9361_DCXO_TUNE_COARSE_AVAILABLE: ret = sprintf(buf, "%s\n", phy->pdata->use_extclk ? "[0 0 0]" : "[0 1 63]"); break; case AD9361_DCXO_TUNE_FINE_AVAILABLE: ret = sprintf(buf, "%s\n", phy->pdata->use_extclk ? "[0 0 0]" : "[0 1 8191]"); break; case AD9361_XO_CORRECTION: ret = sprintf(buf, "%lu\n", clk_get_rate(phy->clk_refin)); break; case AD9361_XO_CORRECTION_AVAILABLE: clk[0] = clk_get_rate(phy->clk_refin); delta = (u64) clk[0] * (u64) clk_get_accuracy(phy->clk_refin); do_div(delta, 1000000000U); ret = sprintf(buf, "[%llu 1 %llu]\n", clk[0] - delta, clk[0] + delta); break; default: ret = -EINVAL; } mutex_unlock(&indio_dev->mlock); return ret; } static IIO_DEVICE_ATTR(in_voltage_rf_bandwidth, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_RF_RX_BANDWIDTH); static IIO_CONST_ATTR(in_voltage_rf_bandwidth_available, "[200000 1 56000000]"); static IIO_DEVICE_ATTR(out_voltage_rf_bandwidth, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_RF_TX_BANDWIDTH); static IIO_CONST_ATTR(out_voltage_rf_bandwidth_available, "[200000 1 40000000]"); static IIO_DEVICE_ATTR(ensm_mode, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_ENSM_MODE); static IIO_DEVICE_ATTR(ensm_mode_available, S_IRUGO, ad9361_phy_show, NULL, AD9361_ENSM_MODE_AVAIL); static IIO_DEVICE_ATTR(calib_mode, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_CALIB_MODE); static IIO_DEVICE_ATTR(calib_mode_available, S_IRUGO, ad9361_phy_show, NULL, AD9361_CALIB_MODE_AVAIL); static IIO_DEVICE_ATTR(rssi_gain_step_error, S_IRUGO, ad9361_phy_show, ad9361_phy_store, AD9361_RSSI_GAIN_STEP_ERROR); static IIO_DEVICE_ATTR(rx_path_rates, S_IRUGO, ad9361_phy_show, NULL, AD9361_RX_PATH_FREQ); static IIO_DEVICE_ATTR(tx_path_rates, S_IRUGO, ad9361_phy_show, NULL, AD9361_TX_PATH_FREQ); static IIO_DEVICE_ATTR(trx_rate_governor, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_TRX_RATE_GOV); static IIO_DEVICE_ATTR(trx_rate_governor_available, S_IRUGO, ad9361_phy_show, NULL, AD9361_TRX_RATE_GOV_AVAIL); static IIO_DEVICE_ATTR(in_voltage_filter_fir_en, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_FIR_RX_ENABLE); static IIO_DEVICE_ATTR(out_voltage_filter_fir_en, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_FIR_TX_ENABLE); static IIO_DEVICE_ATTR(in_out_voltage_filter_fir_en, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_FIR_TRX_ENABLE); static IIO_DEVICE_ATTR(in_voltage_bb_dc_offset_tracking_en, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_BBDC_OFFS_ENABLE); static IIO_DEVICE_ATTR(in_voltage_rf_dc_offset_tracking_en, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_RFDC_OFFS_ENABLE); static IIO_DEVICE_ATTR(in_voltage_quadrature_tracking_en, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_QUAD_ENABLE); static IIO_DEVICE_ATTR(dcxo_tune_coarse, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_DCXO_TUNE_COARSE); static IIO_DEVICE_ATTR(dcxo_tune_coarse_available, S_IRUGO, ad9361_phy_show, NULL, AD9361_DCXO_TUNE_COARSE_AVAILABLE); static IIO_DEVICE_ATTR(dcxo_tune_fine, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_DCXO_TUNE_FINE); static IIO_DEVICE_ATTR(dcxo_tune_fine_available, S_IRUGO, ad9361_phy_show, NULL, AD9361_DCXO_TUNE_FINE_AVAILABLE); static IIO_DEVICE_ATTR(xo_correction, S_IRUGO | S_IWUSR, ad9361_phy_show, ad9361_phy_store, AD9361_XO_CORRECTION); static IIO_DEVICE_ATTR(xo_correction_available, S_IRUGO, ad9361_phy_show, NULL, AD9361_XO_CORRECTION_AVAILABLE); static IIO_DEVICE_ATTR(multichip_sync, S_IWUSR, NULL, ad9361_phy_store, AD9361_MCS_SYNC); static struct attribute *ad9361_phy_attributes[] = { &iio_dev_attr_in_voltage_filter_fir_en.dev_attr.attr, &iio_dev_attr_out_voltage_filter_fir_en.dev_attr.attr, &iio_dev_attr_in_out_voltage_filter_fir_en.dev_attr.attr, &iio_dev_attr_in_voltage_rf_bandwidth.dev_attr.attr, &iio_dev_attr_out_voltage_rf_bandwidth.dev_attr.attr, &iio_dev_attr_ensm_mode.dev_attr.attr, &iio_dev_attr_ensm_mode_available.dev_attr.attr, &iio_dev_attr_calib_mode.dev_attr.attr, &iio_dev_attr_calib_mode_available.dev_attr.attr, &iio_dev_attr_rssi_gain_step_error.dev_attr.attr, &iio_dev_attr_tx_path_rates.dev_attr.attr, &iio_dev_attr_rx_path_rates.dev_attr.attr, &iio_dev_attr_trx_rate_governor.dev_attr.attr, &iio_dev_attr_trx_rate_governor_available.dev_attr.attr, &iio_dev_attr_in_voltage_bb_dc_offset_tracking_en.dev_attr.attr, &iio_dev_attr_in_voltage_rf_dc_offset_tracking_en.dev_attr.attr, &iio_dev_attr_in_voltage_quadrature_tracking_en.dev_attr.attr, &iio_dev_attr_dcxo_tune_coarse.dev_attr.attr, &iio_dev_attr_dcxo_tune_fine.dev_attr.attr, &iio_dev_attr_xo_correction.dev_attr.attr, &iio_dev_attr_multichip_sync.dev_attr.attr, &iio_const_attr_in_voltage_rf_bandwidth_available.dev_attr.attr, &iio_const_attr_out_voltage_rf_bandwidth_available.dev_attr.attr, &iio_dev_attr_dcxo_tune_coarse_available.dev_attr.attr, &iio_dev_attr_dcxo_tune_fine_available.dev_attr.attr, &iio_dev_attr_xo_correction_available.dev_attr.attr, NULL, }; static const struct attribute_group ad9361_phy_attribute_group = { .attrs = ad9361_phy_attributes, }; static int ad9361_phy_reg_access(struct iio_dev *indio_dev, u32 reg, u32 writeval, u32 *readval) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); int ret; mutex_lock(&indio_dev->mlock); if (readval == NULL) { ret = ad9361_spi_write(phy->spi, reg, writeval); } else { *readval = ad9361_spi_read(phy->spi, reg); ret = 0; } mutex_unlock(&indio_dev->mlock); return ret; } enum lo_ext_info { LOEXT_FREQ, LOEXT_FREQ_AVAILABLE, LOEXT_STORE, LOEXT_RECALL, LOEXT_LOAD, LOEXT_SAVE, LOEXT_EXTERNAL, LOEXT_PD, }; static ssize_t ad9361_phy_lo_write(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, const char *buf, size_t len) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; u64 readin; bool res; int ret = 0; if (st->curr_ensm_state == ENSM_STATE_SLEEP) return -EINVAL; if (private != LOEXT_LOAD) { } switch (private) { case LOEXT_FREQ: case LOEXT_STORE: case LOEXT_RECALL: case LOEXT_SAVE: ret = kstrtoull(buf, 10, &readin); if (ret) return ret; break; case LOEXT_EXTERNAL: case LOEXT_PD: ret = strtobool(buf, &res); if (ret < 0) return ret; break; case LOEXT_LOAD: break; } mutex_lock(&indio_dev->mlock); switch (private) { case LOEXT_FREQ: switch (chan->channel) { case 0: ret = clk_set_rate(phy->clks[RX_RFPLL], ad9361_to_clk(readin)); break; case 1: ret = clk_set_rate(phy->clks[TX_RFPLL], ad9361_to_clk(readin)); if (test_bit(0, &st->flags)) wait_for_completion(&phy->complete); break; default: ret = -EINVAL; } break; case LOEXT_STORE: ret = ad9361_fastlock_store(phy, chan->channel == 1, readin); break; case LOEXT_RECALL: ret = ad9361_fastlock_recall(phy, chan->channel == 1, readin); break; case LOEXT_LOAD: { char *line, *ptr = (char*) buf; u8 faslock_vals[16]; u32 profile = 0, val, val2, i = 0; while ((line = strsep(&ptr, ","))) { if (line >= buf + len) break; ret = sscanf(line, "%u %u", &val, &val2); if (ret == 1) { faslock_vals[i++] = val; continue; } else if (ret == 2) { profile = val; faslock_vals[i++] = val2; continue; } } if (i == 16) ret = ad9361_fastlock_load(phy, chan->channel == 1, profile, faslock_vals); else ret = -EINVAL; break; } case LOEXT_SAVE: st->fastlock.save_profile = readin; break; case LOEXT_EXTERNAL: switch(spi_get_device_id(phy->spi)->driver_data) { case ID_AD9363A: ret = -ENODEV; break; default: switch (chan->channel) { case 0: if (phy->clk_ext_lo_rx) ret = clk_set_parent(phy->clks[RX_RFPLL], res ? phy->clk_ext_lo_rx : phy->clks[RX_RFPLL_INT]); else ret = -ENODEV; break; case 1: if (phy->clk_ext_lo_tx) ret = clk_set_parent(phy->clks[TX_RFPLL], res ? phy->clk_ext_lo_tx : phy->clks[TX_RFPLL_INT]); else ret = -ENODEV; break; default: ret = -EINVAL; } } break; case LOEXT_PD: switch (chan->channel) { case 0: ret = ad9361_synth_lo_powerdown(phy, res ? LO_OFF : LO_ON, LO_DONTCARE); break; case 1: ret = ad9361_synth_lo_powerdown(phy, LO_DONTCARE, res ? LO_OFF : LO_ON); break; } break; } mutex_unlock(&indio_dev->mlock); return ret ? ret : len; } static ssize_t ad9361_phy_lo_read(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; u64 val = 0; size_t len; int ret = 0; mutex_lock(&indio_dev->mlock); switch (private) { case LOEXT_FREQ: val = ad9361_from_clk(clk_get_rate(phy->clks[chan->channel ? TX_RFPLL : RX_RFPLL])); break; case LOEXT_SAVE: { u8 faslock_vals[16]; int i; ret = ad9361_fastlock_save(phy, chan->channel == 1, st->fastlock.save_profile, faslock_vals); len = sprintf(buf, "%u ", st->fastlock.save_profile); for (i = 0; i < RX_FAST_LOCK_CONFIG_WORD_NUM; i++) len += sprintf(buf + len, "%u%c", faslock_vals[i], i == 15 ? '\n' : ','); mutex_unlock(&indio_dev->mlock); return len; } case LOEXT_RECALL: ret = st->fastlock.current_profile[chan->channel == 1]; if (ret == 0) ret = -EINVAL; else val = ret - 1; break; case LOEXT_EXTERNAL: switch (chan->channel) { case 0: val = clk_get_parent(phy->clks[RX_RFPLL]) != phy->clks[RX_RFPLL_INT]; break; case 1: val = clk_get_parent(phy->clks[TX_RFPLL]) != phy->clks[TX_RFPLL_INT]; break; default: ret = -EINVAL; } break; case LOEXT_PD: val = !!(st->cached_synth_pd[chan->channel ? 0 : 1] & RX_LO_POWER_DOWN); break; case LOEXT_FREQ_AVAILABLE: { u64 min, max; switch(spi_get_device_id(phy->spi)->driver_data) { case ID_AD9363A: min = AD9363A_MIN_CARRIER_FREQ_HZ; max = AD9363A_MAX_CARRIER_FREQ_HZ; break; default: min = chan->channel ? MIN_TX_CARRIER_FREQ_HZ : MIN_RX_CARRIER_FREQ_HZ; max = MAX_CARRIER_FREQ_HZ; break; } len = sprintf(buf, "[%llu 1 %llu]\n", min, max); mutex_unlock(&indio_dev->mlock); return len; } default: ret = 0; } mutex_unlock(&indio_dev->mlock); return ret < 0 ? ret : sprintf(buf, "%llu\n", val); } #define _AD9361_EXT_LO_INFO(_name, _ident) { \ .name = _name, \ .read = ad9361_phy_lo_read, \ .write = ad9361_phy_lo_write, \ .private = _ident, \ } #define _AD9361_EXT_LO_INFO_RO(_name, _ident) { \ .name = _name, \ .read = ad9361_phy_lo_read, \ .private = _ident, \ } static const struct iio_chan_spec_ext_info ad9361_phy_ext_info[] = { /* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are * values > 2^32 in order to support the entire frequency range * in Hz. Using scale is a bit ugly. */ _AD9361_EXT_LO_INFO("frequency", LOEXT_FREQ), _AD9361_EXT_LO_INFO_RO("frequency_available", LOEXT_FREQ_AVAILABLE), _AD9361_EXT_LO_INFO("fastlock_store", LOEXT_STORE), _AD9361_EXT_LO_INFO("fastlock_recall", LOEXT_RECALL), _AD9361_EXT_LO_INFO("fastlock_load", LOEXT_LOAD), _AD9361_EXT_LO_INFO("fastlock_save", LOEXT_SAVE), _AD9361_EXT_LO_INFO("external", LOEXT_EXTERNAL), _AD9361_EXT_LO_INFO("powerdown", LOEXT_PD), { }, }; static int ad9361_set_agc_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, u32 mode) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; struct rf_gain_ctrl gc = {0}; if (st->agc_mode[chan->channel] == mode) return 0; gc.ant = ad9361_1rx1tx_channel_map(phy, false, chan->channel + 1); gc.mode = st->agc_mode[chan->channel] = mode; return ad9361_set_gain_ctrl_mode(phy, &gc); } static int ad9361_get_agc_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; return st->agc_mode[chan->channel]; } static const char * const ad9361_agc_modes[] = {"manual", "fast_attack", "slow_attack", "hybrid"}; static const struct iio_enum ad9361_agc_modes_available = { .items = ad9361_agc_modes, .num_items = ARRAY_SIZE(ad9361_agc_modes), .get = ad9361_get_agc_mode, .set = ad9361_set_agc_mode, }; static int ad9361_set_rf_port(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, u32 mode) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; if (chan->output) { if (phy->pdata->rf_tx_output_sel_lock && mode != st->rf_tx_output_sel) return -EINVAL; return ad9361_set_tx_port(phy, mode); } else { if (phy->pdata->rf_rx_input_sel_lock && mode != st->rf_rx_input_sel) return -EINVAL; return ad9361_set_rx_port(phy, mode); } } static int ad9361_get_rf_port(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; if (chan->output) return st->rf_tx_output_sel; else return st->rf_rx_input_sel; } static const char * const ad9361_rf_rx_port[] = {"A_BALANCED", "B_BALANCED", "C_BALANCED", "A_N", "A_P", "B_N", "B_P", "C_N", "C_P", "TX_MONITOR1", "TX_MONITOR2", "TX_MONITOR1_2"}; static const struct iio_enum ad9361_rf_rx_port_available = { .items = ad9361_rf_rx_port, .num_items = ARRAY_SIZE(ad9361_rf_rx_port), .get = ad9361_get_rf_port, .set = ad9361_set_rf_port, }; static const char * const ad9361_rf_tx_port[] = {"A", "B"}; static const struct iio_enum ad9361_rf_tx_port_available = { .items = ad9361_rf_tx_port, .num_items = ARRAY_SIZE(ad9361_rf_tx_port), .get = ad9361_get_rf_port, .set = ad9361_set_rf_port, }; static ssize_t ad9361_phy_rx_write(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, const char *buf, size_t len) { // struct ad9361_rf_phy *phy = iio_priv(indio_dev); u64 readin; int ret = 0; ret = kstrtoull(buf, 10, &readin); if (ret) return ret; mutex_lock(&indio_dev->mlock); switch (chan->channel) { case 0: break; case 1: break; default: ret = -EINVAL; ret = 0; } mutex_unlock(&indio_dev->mlock); return ret ? ret : len; } static ssize_t ad9361_phy_rx_read(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct rf_rssi rssi = {0}; int val; int ret = 0; mutex_lock(&indio_dev->mlock); rssi.ant = ad9361_1rx1tx_channel_map(phy, false, chan->channel + 1); rssi.duration = 1; ret = ad9361_read_rssi(phy, &rssi); val = rssi.symbol; mutex_unlock(&indio_dev->mlock); return ret < 0 ? ret : sprintf(buf, "%u.%02u dB\n", val / rssi.multiplier, val % rssi.multiplier); } #define _AD9361_EXT_RX_INFO(_name, _ident) { \ .name = _name, \ .read = ad9361_phy_rx_read, \ .write = ad9361_phy_rx_write, \ .private = _ident, \ } static ssize_t ad9361_phy_tx_read(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); u8 reg_val_buf[3]; u32 val; int ret; mutex_lock(&indio_dev->mlock); ret = ad9361_spi_readm(phy->spi, REG_TX_RSSI_LSB, reg_val_buf, ARRAY_SIZE(reg_val_buf)); switch (chan->channel) { case 0: val = (reg_val_buf[2] << 1) | (reg_val_buf[0] & TX_RSSI_1); break; case 1: val = (reg_val_buf[1] << 1) | ((reg_val_buf[0] & TX_RSSI_2) >> 1); break; default: ret = -EINVAL; } mutex_unlock(&indio_dev->mlock); val *= RSSI_RESOLUTION; return ret < 0 ? ret : sprintf(buf, "%u.%02u dB\n", val / RSSI_MULTIPLIER, val % RSSI_MULTIPLIER); } #define _AD9361_EXT_TX_INFO(_name, _ident) { \ .name = _name, \ .read = ad9361_phy_tx_read, \ .private = _ident, \ } static const struct iio_chan_spec_ext_info ad9361_phy_rx_ext_info[] = { /* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are * values > 2^32 in order to support the entire frequency range * in Hz. Using scale is a bit ugly. */ IIO_ENUM_AVAILABLE("gain_control_mode", &ad9361_agc_modes_available), IIO_ENUM("gain_control_mode", false, &ad9361_agc_modes_available), _AD9361_EXT_RX_INFO("rssi", 1), IIO_ENUM_AVAILABLE("rf_port_select", &ad9361_rf_rx_port_available), IIO_ENUM("rf_port_select", false, &ad9361_rf_rx_port_available), { }, }; static const struct iio_chan_spec_ext_info ad9361_phy_tx_ext_info[] = { IIO_ENUM_AVAILABLE("rf_port_select", &ad9361_rf_tx_port_available), IIO_ENUM("rf_port_select", false, &ad9361_rf_tx_port_available), _AD9361_EXT_TX_INFO("rssi", 0), { }, }; static int ad9361_phy_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long m) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); int ret; mutex_lock(&indio_dev->mlock); switch (m) { case IIO_CHAN_INFO_HARDWAREGAIN: if (chan->output) { ret = ad9361_get_tx_atten(phy, ad9361_1rx1tx_channel_map(phy, true, chan->channel + 1)); if (ret < 0) { ret = -EINVAL; goto out_unlock; } *val = -1 * (ret / 1000); *val2 = (ret % 1000) * 1000; if (!*val) *val2 *= -1; } else { struct rf_rx_gain rx_gain = {0}; ret = ad9361_get_rx_gain(phy, ad9361_1rx1tx_channel_map(phy, false, chan->channel + 1), &rx_gain); *val = rx_gain.gain_db; *val2 = 0; } ret = IIO_VAL_INT_PLUS_MICRO_DB; break; case IIO_CHAN_INFO_SAMP_FREQ: if (chan->output) *val = (int)clk_get_rate(phy->clks[TX_SAMPL_CLK]); else *val = (int)clk_get_rate(phy->clks[RX_SAMPL_CLK]); ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_PROCESSED: *val = ad9361_get_temp(phy); ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_RAW: if (chan->output) { if (chan->channel == 2) ret = ad9361_auxdac_get(phy, 1); else if (chan->channel == 3) ret = ad9361_auxdac_get(phy, 2); else ret = -EINVAL; if (ret >= 0) { *val = ret; ret = IIO_VAL_INT; } } else { ret = ad9361_get_auxadc(phy); if (ret >= 0) { *val = ret; ret = IIO_VAL_INT; } } break; case IIO_CHAN_INFO_OFFSET: *val = 57; /* AuxADC */ ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: if (chan->output) { *val = 1; /* AuxDAC */ *val2 = 0; } else { *val = 0; /* AuxADC */ *val2 = 305250; } ret = IIO_VAL_INT_PLUS_MICRO; break; default: ret = -EINVAL; } out_unlock: mutex_unlock(&indio_dev->mlock); return ret; }; static int ad9361_phy_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; u32 code; int ret; if (st->curr_ensm_state == ENSM_STATE_SLEEP) return -EINVAL; mutex_lock(&indio_dev->mlock); switch (mask) { case IIO_CHAN_INFO_HARDWAREGAIN: if (chan->output) { int ch; if (val > 0 || (val == 0 && val2 > 0)) { ret = -EINVAL; goto out; } code = ((abs(val) * 1000) + (abs(val2) / 1000)); ch = ad9361_1rx1tx_channel_map(phy, true, chan->channel); ret = ad9361_set_tx_atten(phy, code, ch == 0, ch == 1, !phy->pdata->update_tx_gain_via_alert); } else { struct rf_rx_gain rx_gain = {0}; rx_gain.gain_db = val; ret = ad9361_set_rx_gain(phy, ad9361_1rx1tx_channel_map(phy, false, chan->channel + 1), &rx_gain); } break; case IIO_CHAN_INFO_SAMP_FREQ: if (st->rx_eq_2tx && (chan->output == 0)) { ret = 0; break; } ret = ad9361_set_trx_clock_chain_freq(phy, val); if (ret < 0) goto out; ret = ad9361_update_rf_bandwidth(phy, st->current_rx_bw_Hz, st->current_tx_bw_Hz); break; case IIO_CHAN_INFO_RAW: if (chan->output) { if (chan->channel == 2) ret = ad9361_auxdac_set(phy, 1, val); else if (chan->channel == 3) ret = ad9361_auxdac_set(phy, 2, val); else ret = -EINVAL; } else { ret = -EINVAL; } break; default: ret = -EINVAL; } out: mutex_unlock(&indio_dev->mlock); return ret; } static int ad9361_phy_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long mask) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; switch (mask) { case IIO_CHAN_INFO_HARDWAREGAIN: if (chan->output) { static const int tx_hw_gain[] = { 89, -750000, 0, 250000, 0, 0 }; *vals = tx_hw_gain; *type = IIO_VAL_INT_PLUS_MICRO; return IIO_AVAIL_RANGE; } else { st->rx_gain_avail[0] = phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[0]; st->rx_gain_avail[1] = 1; st->rx_gain_avail[2] = phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[phy->gt_info[ad9361_gt(phy)].max_index - 1]; *vals = st->rx_gain_avail; *type = IIO_VAL_INT; return IIO_AVAIL_RANGE; } break; case IIO_CHAN_INFO_SAMP_FREQ: { int int_dec, max; if (phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) max = 61440000U; else max = 61440000U / (phy->pdata->rx2tx2 ? 2 : 1); if (chan->output) { if (st->bypass_tx_fir) int_dec = 1; else int_dec = st->tx_fir_int; if (int_dec == 4) max = MAX_TX_HB1 / 4; st->tx_sampl_freq_avail[0] = MIN_ADC_CLK / (12 * int_dec); st->tx_sampl_freq_avail[1] = 1; st->tx_sampl_freq_avail[2] = max; *vals = st->tx_sampl_freq_avail; *type = IIO_VAL_INT; return IIO_AVAIL_RANGE; } else { if (st->bypass_rx_fir) int_dec = 1; else int_dec = st->rx_fir_dec; if (int_dec == 4) max = MAX_RX_HB1 / 4; st->rx_sampl_freq_avail[0] = MIN_ADC_CLK / (12 * int_dec); st->rx_sampl_freq_avail[1] = 1; st->rx_sampl_freq_avail[2] = max; *vals = st->rx_sampl_freq_avail; *type = IIO_VAL_INT; return IIO_AVAIL_RANGE; } break; } } return -EINVAL; } static const struct iio_chan_spec ad9361_phy_chan[] = { { .type = IIO_TEMP, .indexed = 1, .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), }, { /* RX LO */ .type = IIO_ALTVOLTAGE, .indexed = 1, .output = 1, .channel = 0, .extend_name = "RX_LO", .ext_info = ad9361_phy_ext_info, }, { /* TX LO */ .type = IIO_ALTVOLTAGE, .indexed = 1, .output = 1, .channel = 1, .extend_name = "TX_LO", .ext_info = ad9361_phy_ext_info, }, { /* TX1 */ .type = IIO_VOLTAGE, .indexed = 1, .output = 1, .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), .info_mask_separate_available = BIT(IIO_CHAN_INFO_HARDWAREGAIN), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9361_phy_tx_ext_info, }, { /* RX1 */ .type = IIO_VOLTAGE, .indexed = 1, .channel = 0, .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), .info_mask_separate_available = BIT(IIO_CHAN_INFO_HARDWAREGAIN), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9361_phy_rx_ext_info, }, { /* AUXDAC1 */ .type = IIO_VOLTAGE, .indexed = 1, .output = 1, .channel = 2, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), }, { /* AUXDAC2 */ .type = IIO_VOLTAGE, .indexed = 1, .output = 1, .channel = 3, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), }, { /* AUXADC1 */ .type = IIO_VOLTAGE, .indexed = 1, .channel = 2, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), }, { /* TX2 */ .type = IIO_VOLTAGE, .indexed = 1, .output = 1, .channel = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), .info_mask_separate_available = BIT(IIO_CHAN_INFO_HARDWAREGAIN), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9361_phy_tx_ext_info, }, { /* RX2 */ .type = IIO_VOLTAGE, .indexed = 1, .channel = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), .info_mask_separate_available = BIT(IIO_CHAN_INFO_HARDWAREGAIN), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9361_phy_rx_ext_info, }}; static const struct iio_info ad9361_phy_info = { .read_raw = &ad9361_phy_read_raw, .write_raw = &ad9361_phy_write_raw, .read_avail = ad9361_phy_read_avail, .debugfs_reg_access = &ad9361_phy_reg_access, .attrs = &ad9361_phy_attribute_group, .driver_module = THIS_MODULE, }; #ifdef CONFIG_OF static ssize_t ad9361_debugfs_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct ad9361_debugfs_entry *entry = file->private_data; struct ad9361_rf_phy *phy = entry->phy; char buf[700]; u32 val = 0; ssize_t len = 0; int ret; if (entry->out_value) { switch (entry->size){ case 1: val = *(u8*)entry->out_value; break; case 2: val = *(u16*)entry->out_value; break; case 4: val = *(u32*)entry->out_value; break; case 5: val = *(bool*)entry->out_value; break; default: ret = -EINVAL; } } else if (entry->cmd == DBGFS_RXGAIN_1 || entry->cmd == DBGFS_RXGAIN_2) { struct rf_rx_gain rx_gain = {0}; mutex_lock(&phy->indio_dev->mlock); ret = ad9361_get_rx_gain(phy, (entry->cmd == DBGFS_RXGAIN_1) ? 1 : 2, &rx_gain); mutex_unlock(&phy->indio_dev->mlock); if (ret < 0) return ret; len = snprintf(buf, sizeof(buf), "%d %u %u %u %u %u %u %u\n", rx_gain.gain_db, rx_gain.fgt_lmt_index, rx_gain.digital_gain, rx_gain.lmt_gain, rx_gain.lpf_gain, rx_gain.lna_index, rx_gain.tia_index, rx_gain.mixer_index); } else if (entry->cmd == DBGFS_BIST_DT_ANALYSIS) { if (entry->val) len = ad9361_dig_interface_timing_analysis(phy, buf, sizeof(buf)); entry->val = 0; } else if (entry->cmd) { val = entry->val; } else return -EFAULT; if (!len) len = snprintf(buf, sizeof(buf), "%u\n", val); return simple_read_from_buffer(userbuf, count, ppos, buf, len); } static ssize_t ad9361_debugfs_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct ad9361_debugfs_entry *entry = file->private_data; struct ad9361_rf_phy *phy = entry->phy; struct gpo_control *ctrl = &phy->pdata->gpo_ctrl; u32 val, val2, val3, val4, mask; char buf[80]; int ret; count = min_t(size_t, count, (sizeof(buf)-1)); if (copy_from_user(buf, userbuf, count)) return -EFAULT; buf[count] = 0; ret = sscanf(buf, "%i %i %i %i", &val, &val2, &val3, &val4); if (ret < 1) return -EINVAL; switch (entry->cmd) { case DBGFS_INIT: if (!(ret == 1 && val == 1)) return -EINVAL; mutex_lock(&phy->indio_dev->mlock); clk_set_rate(phy->clks[TX_SAMPL_CLK], 1); clk_set_parent(phy->clks[RX_RFPLL], phy->clk_ext_lo_rx); clk_set_parent(phy->clks[TX_RFPLL], phy->clk_ext_lo_tx); ad9361_reset(phy); ad9361_clks_resync(phy); ad9361_clks_disable(phy); ad9361_clear_state(phy); ret = ad9361_setup(phy); mutex_unlock(&phy->indio_dev->mlock); if (ret < 0) return ret; return count; case DBGFS_LOOPBACK: if (ret != 1) return -EINVAL; mutex_lock(&phy->indio_dev->mlock); ret = ad9361_bist_loopback(phy, val); mutex_unlock(&phy->indio_dev->mlock); if (ret < 0) return ret; entry->val = val; return count; case DBGFS_BIST_PRBS: if (ret != 1) return -EINVAL; mutex_lock(&phy->indio_dev->mlock); ret = ad9361_bist_prbs(phy, val); mutex_unlock(&phy->indio_dev->mlock); if (ret < 0) return ret; entry->val = val; return count; case DBGFS_BIST_TONE: if (ret != 4) return -EINVAL; mutex_lock(&phy->indio_dev->mlock); ret = ad9361_bist_tone(phy, val, val2, val3, val4); mutex_unlock(&phy->indio_dev->mlock); if (ret < 0) return ret; entry->val = val; return count; case DBGFS_MCS: if (ret != 1) return -EINVAL; mutex_lock(&phy->indio_dev->mlock); ret = ad9361_mcs(phy, val); mutex_unlock(&phy->indio_dev->mlock); if (ret < 0) return ret; entry->val = val; return count; case DBGFS_CAL_SW_CTRL: if (ret != 1) return -EINVAL; if (phy->pdata->cal_sw1_gpio && phy->pdata->cal_sw2_gpio) { mutex_lock(&phy->indio_dev->mlock); gpiod_set_value(phy->pdata->cal_sw1_gpio, !!(val & BIT(0))); gpiod_set_value(phy->pdata->cal_sw2_gpio, !!(val & BIT(1))); mutex_unlock(&phy->indio_dev->mlock); } else { return -ENODEV; } entry->val = val; return count; case DBGFS_DIGITAL_TUNE: if (ret != 2) return -EINVAL; mutex_lock(&phy->indio_dev->mlock); ret = ad9361_dig_tune(phy, val, val2); mutex_unlock(&phy->indio_dev->mlock); if (ret < 0) return ret; entry->val = val; return count; case DBGFS_BIST_DT_ANALYSIS: entry->val = val; return count; case DBGFS_GPO_SET: if (ret != 2) return -EINVAL; if (!ctrl->gpo_manual_mode_en) { dev_warn(&phy->spi->dev, "GPO manual mode not enabled!"); return -EINVAL; } switch (val) { case 0: case 1: case 2: case 3: mask = BIT(val); if (val2) val3 = mask; else val3 = 0; break; case 0xF: mask = 0xF; val3 = val2 & 0xF; break; default: return -EINVAL; } mutex_lock(&phy->indio_dev->mlock); ctrl->gpo_manual_mode_enable_mask &= ~mask; ctrl->gpo_manual_mode_enable_mask |= val3; ret = ad9361_spi_write(phy->spi, REG_GPO_FORCE_AND_INIT, GPO_MANUAL_CTRL(ctrl->gpo_manual_mode_enable_mask) | GPO_INIT_STATE(ctrl->gpo0_inactive_state_high_en | (ctrl->gpo1_inactive_state_high_en << 1) | (ctrl->gpo2_inactive_state_high_en << 2) | (ctrl->gpo3_inactive_state_high_en << 3))); /* * GPO manual mode conflicts with automatic ENSM slave * and eLNA mode */ val3 = ad9361_spi_read(phy->spi, REG_EXTERNAL_LNA_CTRL); if (!(val3 & GPO_MANUAL_SELECT)) ad9361_spi_write(phy->spi, REG_EXTERNAL_LNA_CTRL, val3 | GPO_MANUAL_SELECT); mutex_unlock(&phy->indio_dev->mlock); if (ret < 0) return ret; entry->val = val; return count; default: break; } if (entry->out_value) { switch (entry->size){ case 1: *(u8*)entry->out_value = val; break; case 2: *(u16*)entry->out_value = val; break; case 4: *(u32*)entry->out_value = val; break; case 5: *(bool*)entry->out_value = val; break; default: ret = -EINVAL; } } return count; } static const struct file_operations ad9361_debugfs_reg_fops = { .open = simple_open, .read = ad9361_debugfs_read, .write = ad9361_debugfs_write, }; static void ad9361_add_debugfs_entry(struct ad9361_rf_phy *phy, const char *propname, unsigned int cmd) { unsigned int i = phy->ad9361_debugfs_entry_index; if (WARN_ON(i >= ARRAY_SIZE(phy->debugfs_entry))) return; phy->debugfs_entry[i].phy = phy; phy->debugfs_entry[i].propname = propname; phy->debugfs_entry[i].cmd = cmd; phy->ad9361_debugfs_entry_index++; } static int ad9361_register_debugfs(struct iio_dev *indio_dev) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct dentry *d; int i; if (!iio_get_debugfs_dentry(indio_dev)) return -ENODEV; ad9361_add_debugfs_entry(phy, "initialize", DBGFS_INIT); ad9361_add_debugfs_entry(phy, "loopback", DBGFS_LOOPBACK); ad9361_add_debugfs_entry(phy, "bist_prbs", DBGFS_BIST_PRBS); ad9361_add_debugfs_entry(phy, "bist_tone", DBGFS_BIST_TONE); ad9361_add_debugfs_entry(phy, "gpo_set", DBGFS_GPO_SET); ad9361_add_debugfs_entry(phy, "bist_timing_analysis", DBGFS_BIST_DT_ANALYSIS); ad9361_add_debugfs_entry(phy, "gaininfo_rx1", DBGFS_RXGAIN_1); ad9361_add_debugfs_entry(phy, "gaininfo_rx2", DBGFS_RXGAIN_2); ad9361_add_debugfs_entry(phy, "multichip_sync", DBGFS_MCS); ad9361_add_debugfs_entry(phy, "calibration_switch_control", DBGFS_CAL_SW_CTRL); ad9361_add_debugfs_entry(phy, "digital_tune", DBGFS_DIGITAL_TUNE); for (i = 0; i < phy->ad9361_debugfs_entry_index; i++) d = debugfs_create_file( phy->debugfs_entry[i].propname, 0644, iio_get_debugfs_dentry(indio_dev), &phy->debugfs_entry[i], &ad9361_debugfs_reg_fops); return 0; } struct ad9361_dport_config { u8 reg; u8 offset; char name[40]; }; static const struct ad9361_dport_config ad9361_dport_config[] = { {1, 7, "adi,pp-tx-swap-enable"}, {1, 6, "adi,pp-rx-swap-enable"}, {1, 5, "adi,tx-channel-swap-enable"}, {1, 4, "adi,rx-channel-swap-enable"}, {1, 3, "adi,rx-frame-pulse-mode-enable"}, {1, 2, "adi,2t2r-timing-enable"}, {1, 1, "adi,invert-data-bus-enable"}, {1, 0, "adi,invert-data-clk-enable"}, {2, 7, "adi,fdd-alt-word-order-enable"}, {2, 2, "adi,invert-rx-frame-enable"}, {3, 7, "adi,fdd-rx-rate-2tx-enable"}, {3, 6, "adi,swap-ports-enable"}, {3, 5, "adi,single-data-rate-enable"}, {3, 4, "adi,lvds-mode-enable"}, {3, 3, "adi,half-duplex-mode-enable"}, {3, 2, "adi,single-port-mode-enable"}, {3, 1, "adi,full-port-enable"}, {3, 0, "adi,full-duplex-swap-bits-enable"}, }; static int __ad9361_of_get_u32(struct iio_dev *indio_dev, struct device_node *np, const char *propname, u32 defval, void *out_value, u32 size) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); u32 tmp = defval; int ret; ret = of_property_read_u32(np, propname, &tmp); if (out_value) { switch (size){ case 1: *(u8*)out_value = tmp; break; case 2: *(u16*)out_value = tmp; break; case 4: *(u32*)out_value = tmp; break; default: ret = -EINVAL; } } if (WARN_ON(phy->ad9361_debugfs_entry_index >= ARRAY_SIZE(phy->debugfs_entry))) return ret; phy->debugfs_entry[phy->ad9361_debugfs_entry_index++] = (struct ad9361_debugfs_entry) { .out_value = out_value, .propname = propname, .size = size, .phy = phy, }; return ret; } #define ad9361_of_get_u32(iodev, dnp, name, def, outp) \ __ad9361_of_get_u32(iodev, dnp, name, def, outp, sizeof(*outp)) static void ad9361_of_get_bool(struct iio_dev *indio_dev, struct device_node *np, const char *propname, bool *out_value) { struct ad9361_rf_phy *phy = iio_priv(indio_dev); *out_value = of_property_read_bool(np, propname); if (WARN_ON(phy->ad9361_debugfs_entry_index >= ARRAY_SIZE(phy->debugfs_entry))) return; phy->debugfs_entry[phy->ad9361_debugfs_entry_index++] = (struct ad9361_debugfs_entry) { .out_value = out_value, .propname = propname, .phy = phy, .size = 5, }; } static struct ad9361_phy_platform_data *ad9361_phy_parse_dt(struct iio_dev *iodev, struct device *dev) { struct device_node *np = dev->of_node; struct ad9361_rf_phy *phy = iio_priv(iodev); struct ad9361_rf_phy_state *st = phy->state; struct ad9361_phy_platform_data *pdata; u32 tx_path_clks[NUM_TX_CLOCKS]; u32 rx_path_clks[NUM_RX_CLOCKS]; u32 tmp; u64 tmpl; u32 array[6] = {0}; int ret, i; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { dev_err(dev, "could not allocate memory for platform data\n"); return NULL; } ad9361_of_get_bool(iodev, np, "adi,frequency-division-duplex-mode-enable", &pdata->fdd); ad9361_of_get_bool(iodev, np, "adi,frequency-division-duplex-independent-mode-enable", &pdata->fdd_independent_mode); ad9361_of_get_bool(iodev, np, "adi,ensm-enable-pin-pulse-mode-enable", &pdata->ensm_pin_pulse_mode); ad9361_of_get_bool(iodev, np, "adi,ensm-enable-txnrx-control-enable", &pdata->ensm_pin_ctrl); ad9361_of_get_bool(iodev, np, "adi,debug-mode-enable", &pdata->debug_mode); ad9361_of_get_bool(iodev, np, "adi,tdd-use-dual-synth-mode-enable", &pdata->tdd_use_dual_synth); ad9361_of_get_bool(iodev, np, "adi,tdd-skip-vco-cal-enable", &pdata->tdd_skip_vco_cal); ad9361_of_get_u32(iodev, np, "adi,tx-fastlock-delay-ns", 0, &pdata->rx_fastlock_delay_ns); ad9361_of_get_u32(iodev, np, "adi,rx-fastlock-delay-ns", 0, &pdata->tx_fastlock_delay_ns); ad9361_of_get_bool(iodev, np, "adi,rx-fastlock-pincontrol-enable", &pdata->trx_fastlock_pinctrl_en[0]); ad9361_of_get_bool(iodev, np, "adi,tx-fastlock-pincontrol-enable", &pdata->trx_fastlock_pinctrl_en[1]); for (i = 0; i < ARRAY_SIZE(ad9361_dport_config); i++) pdata->port_ctrl.pp_conf[ad9361_dport_config[i].reg - 1] |= (of_property_read_bool(np, ad9361_dport_config[i].name) << ad9361_dport_config[i].offset); tmp = 0; of_property_read_u32(np, "adi,delay-rx-data", &tmp); pdata->port_ctrl.pp_conf[1] |= (tmp & 0x3); tmp = 0; of_property_read_u32(np, "adi,rx-data-clock-delay", &tmp); pdata->port_ctrl.rx_clk_data_delay = DATA_CLK_DELAY(tmp); tmp = 0; of_property_read_u32(np, "adi,rx-data-delay", &tmp); pdata->port_ctrl.rx_clk_data_delay |= RX_DATA_DELAY(tmp); tmp = 0; of_property_read_u32(np, "adi,tx-fb-clock-delay", &tmp); pdata->port_ctrl.tx_clk_data_delay = FB_CLK_DELAY(tmp); tmp = 0; of_property_read_u32(np, "adi,tx-data-delay", &tmp); pdata->port_ctrl.tx_clk_data_delay |= TX_DATA_DELAY(tmp); tmp = 75; of_property_read_u32(np, "adi,lvds-bias-mV", &tmp); pdata->port_ctrl.lvds_bias_ctrl = ((tmp - 75) / 75) & 0x7; pdata->port_ctrl.lvds_bias_ctrl |= (of_property_read_bool(np, "adi,lvds-rx-onchip-termination-enable") << 5); tmp = 0xFF; of_property_read_u32(np, "adi,lvds-invert1-control", &tmp); pdata->port_ctrl.lvds_invert[0] = tmp; tmp = 0x0F; of_property_read_u32(np, "adi,lvds-invert2-control", &tmp); pdata->port_ctrl.lvds_invert[1] = tmp; ad9361_of_get_u32(iodev, np, "adi,digital-interface-tune-skip-mode", 0, &pdata->dig_interface_tune_skipmode); ad9361_of_get_bool(iodev, np, "adi,digital-interface-tune-fir-disable", &pdata->dig_interface_tune_fir_disable); ad9361_of_get_bool(iodev, np, "adi,2rx-2tx-mode-enable", &pdata->rx2tx2); ad9361_of_get_u32(iodev, np, "adi,1rx-1tx-mode-use-rx-num", 1, &pdata->rx1tx1_mode_use_rx_num); ad9361_of_get_u32(iodev, np, "adi,1rx-1tx-mode-use-tx-num", 1, &pdata->rx1tx1_mode_use_tx_num); ad9361_of_get_bool(iodev, np, "adi,split-gain-table-mode-enable", &pdata->split_gt); ad9361_of_get_u32(iodev, np, "adi,rx-rf-port-input-select", 0, &st->rf_rx_input_sel); ad9361_of_get_u32(iodev, np, "adi,tx-rf-port-input-select", 0, &st->rf_tx_output_sel); ad9361_of_get_bool(iodev, np, "adi,rx-rf-port-input-select-lock-enable", &pdata->rf_rx_input_sel_lock); ad9361_of_get_bool(iodev, np, "adi,tx-rf-port-input-select-lock-enable", &pdata->rf_tx_output_sel_lock); ad9361_of_get_bool(iodev, np, "adi,rx1-rx2-phase-inversion-enable", &pdata->rx1rx2_phase_inversion_en); ad9361_of_get_u32(iodev, np, "adi,trx-synthesizer-target-fref-overwrite-hz", MAX_SYNTH_FREF, &pdata->trx_synth_max_fref); ad9361_of_get_bool(iodev, np, "adi,tx-lo-powerdown-managed-enable", &pdata->lo_powerdown_managed_en); tmpl = 2400000000ULL; of_property_read_u64(np, "adi,rx-synthesizer-frequency-hz", &tmpl); pdata->rx_synth_freq = tmpl; tmpl = 2440000000ULL; of_property_read_u64(np, "adi,tx-synthesizer-frequency-hz", &tmpl); pdata->tx_synth_freq = tmpl; ret = of_property_read_u32_array(np, "adi,dcxo-coarse-and-fine-tune", array, 2); pdata->dcxo_coarse = (ret < 0) ? 8 : array[0]; pdata->dcxo_fine = (ret < 0) ? 5920 : array[1]; switch(spi_get_device_id(phy->spi)->driver_data) { case ID_AD9363A: pdata->use_extclk = true; pdata->use_ext_tx_lo = false; pdata->use_ext_rx_lo = false; break; default: ad9361_of_get_bool(iodev, np, "adi,xo-disable-use-ext-refclk-enable", &pdata->use_extclk); ad9361_of_get_bool(iodev, np, "adi,external-tx-lo-enable", &pdata->use_ext_tx_lo); ad9361_of_get_bool(iodev, np, "adi,external-rx-lo-enable", &pdata->use_ext_rx_lo); } ad9361_of_get_u32(iodev, np, "adi,clk-output-mode-select", CLKOUT_DISABLE, &pdata->ad9361_clkout_mode); /* * adi,dc-offset-tracking-update-event-mask: * BIT(0) Apply a new tracking word when a gain change occurs. * BIT(1) Apply a new tracking word when the received signal is * less than the SOI Threshold. * BIT(2) Apply a new tracking word after the device exits the * receive state. */ ad9361_of_get_u32(iodev, np, "adi,dc-offset-tracking-update-event-mask", 5, &pdata->dc_offset_update_events); ad9361_of_get_u32(iodev, np, "adi,dc-offset-attenuation-high-range", 6, &pdata->dc_offset_attenuation_high); ad9361_of_get_u32(iodev, np, "adi,dc-offset-attenuation-low-range", 5, &pdata->dc_offset_attenuation_low); ad9361_of_get_u32(iodev, np, "adi,dc-offset-count-high-range", 0x28, &pdata->rf_dc_offset_count_high); ad9361_of_get_u32(iodev, np, "adi,dc-offset-count-low-range", 0x32, &pdata->rf_dc_offset_count_low); ad9361_of_get_bool(iodev, np, "adi,qec-tracking-slow-mode-enable", &pdata->qec_tracking_slow_mode_en); ret = of_property_read_u32_array(np, "adi,rx-path-clock-frequencies", rx_path_clks, ARRAY_SIZE(rx_path_clks)); if (ret < 0) return NULL; for (i = 0; i < ARRAY_SIZE(rx_path_clks); i++) pdata->rx_path_clks[i] = rx_path_clks[i]; ret = of_property_read_u32_array(np, "adi,tx-path-clock-frequencies", tx_path_clks, ARRAY_SIZE(tx_path_clks)); if (ret < 0) return NULL; for (i = 0; i < ARRAY_SIZE(tx_path_clks); i++) pdata->tx_path_clks[i] = tx_path_clks[i]; ad9361_of_get_u32(iodev, np, "adi,rf-rx-bandwidth-hz", 18000000UL, &pdata->rf_rx_bandwidth_Hz); ad9361_of_get_u32(iodev, np, "adi,rf-tx-bandwidth-hz", 18000000UL, &pdata->rf_tx_bandwidth_Hz); ad9361_of_get_u32(iodev, np, "adi,tx-attenuation-mdB", 10000, &pdata->tx_atten); ad9361_of_get_bool(iodev, np, "adi,update-tx-gain-in-alert-enable", &pdata->update_tx_gain_via_alert); /* Gain Control */ ad9361_of_get_u32(iodev, np, "adi,gc-rx1-mode", 0, &pdata->gain_ctrl.rx1_mode); ad9361_of_get_u32(iodev, np, "adi,gc-rx2-mode", 0, &pdata->gain_ctrl.rx2_mode); ad9361_of_get_u32(iodev, np, "adi,gc-adc-ovr-sample-size", 4, &pdata->gain_ctrl.adc_ovr_sample_size); ad9361_of_get_u32(iodev, np, "adi,gc-adc-small-overload-thresh", 47, &pdata->gain_ctrl.adc_small_overload_thresh); ad9361_of_get_u32(iodev, np, "adi,gc-adc-large-overload-thresh", 58, &pdata->gain_ctrl.adc_large_overload_thresh); ad9361_of_get_u32(iodev, np, "adi,gc-lmt-overload-high-thresh", 800, &pdata->gain_ctrl.lmt_overload_high_thresh); ad9361_of_get_u32(iodev, np, "adi,gc-lmt-overload-low-thresh", 704, &pdata->gain_ctrl.lmt_overload_low_thresh); ad9361_of_get_u32(iodev, np, "adi,gc-dec-pow-measurement-duration", 8192, &pdata->gain_ctrl.dec_pow_measuremnt_duration); ad9361_of_get_u32(iodev, np, "adi,gc-low-power-thresh", 24, &pdata->gain_ctrl.low_power_thresh); ad9361_of_get_bool(iodev, np, "adi,gc-dig-gain-enable", &pdata->gain_ctrl.dig_gain_en); ad9361_of_get_u32(iodev, np, "adi,gc-max-dig-gain", 15, &pdata->gain_ctrl.max_dig_gain); ad9361_of_get_bool(iodev, np, "adi,gc-use-rx-fir-out-for-dec-pwr-meas-enable", &pdata->gain_ctrl.use_rx_fir_out_for_dec_pwr_meas); ad9361_of_get_bool(iodev, np, "adi,mgc-rx1-ctrl-inp-enable", &pdata->gain_ctrl.mgc_rx1_ctrl_inp_en); ad9361_of_get_bool(iodev, np, "adi,mgc-rx2-ctrl-inp-enable", &pdata->gain_ctrl.mgc_rx2_ctrl_inp_en); ad9361_of_get_u32(iodev, np, "adi,mgc-inc-gain-step", 2, &pdata->gain_ctrl.mgc_inc_gain_step); ad9361_of_get_u32(iodev, np, "adi,mgc-dec-gain-step", 2, &pdata->gain_ctrl.mgc_dec_gain_step); ad9361_of_get_u32(iodev, np, "adi,mgc-split-table-ctrl-inp-gain-mode", 0, &pdata->gain_ctrl.mgc_split_table_ctrl_inp_gain_mode); ad9361_of_get_u32(iodev, np, "adi,agc-attack-delay-extra-margin-us", 1, &pdata->gain_ctrl.agc_attack_delay_extra_margin_us); ad9361_of_get_u32(iodev, np, "adi,agc-outer-thresh-high", 5, &pdata->gain_ctrl.agc_outer_thresh_high); ad9361_of_get_u32(iodev, np, "adi,agc-outer-thresh-high-dec-steps", 2, &pdata->gain_ctrl.agc_outer_thresh_high_dec_steps); ad9361_of_get_u32(iodev, np, "adi,agc-inner-thresh-high", 10, &pdata->gain_ctrl.agc_inner_thresh_high); ad9361_of_get_u32(iodev, np, "adi,agc-inner-thresh-high-dec-steps", 1, &pdata->gain_ctrl.agc_inner_thresh_high_dec_steps); ad9361_of_get_u32(iodev, np, "adi,agc-inner-thresh-low", 12, &pdata->gain_ctrl.agc_inner_thresh_low); ad9361_of_get_u32(iodev, np, "adi,agc-inner-thresh-low-inc-steps", 1, &pdata->gain_ctrl.agc_inner_thresh_low_inc_steps); ad9361_of_get_u32(iodev, np, "adi,agc-outer-thresh-low", 18, &pdata->gain_ctrl.agc_outer_thresh_low); ad9361_of_get_u32(iodev, np, "adi,agc-outer-thresh-low-inc-steps", 2, &pdata->gain_ctrl.agc_outer_thresh_low_inc_steps); ad9361_of_get_u32(iodev, np, "adi,agc-adc-small-overload-exceed-counter", 10, &pdata->gain_ctrl.adc_small_overload_exceed_counter); ad9361_of_get_u32(iodev, np, "adi,agc-adc-large-overload-exceed-counter", 10, &pdata->gain_ctrl.adc_large_overload_exceed_counter); ad9361_of_get_u32(iodev, np, "adi,agc-adc-large-overload-inc-steps", 2, &pdata->gain_ctrl.adc_large_overload_inc_steps); /* Name is misleading should be dec-steps */ ad9361_of_get_bool(iodev, np, "adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable", &pdata->gain_ctrl.adc_lmt_small_overload_prevent_gain_inc); ad9361_of_get_u32(iodev, np, "adi,agc-lmt-overload-large-exceed-counter", 10, &pdata->gain_ctrl.lmt_overload_large_exceed_counter); ad9361_of_get_u32(iodev, np, "adi,agc-lmt-overload-small-exceed-counter", 10, &pdata->gain_ctrl.lmt_overload_small_exceed_counter); ad9361_of_get_u32(iodev, np, "adi,agc-lmt-overload-large-inc-steps", 2, &pdata->gain_ctrl.lmt_overload_large_inc_steps); ad9361_of_get_u32(iodev, np, "adi,agc-dig-saturation-exceed-counter", 3, &pdata->gain_ctrl.dig_saturation_exceed_counter); ad9361_of_get_u32(iodev, np, "adi,agc-dig-gain-step-size", 4, &pdata->gain_ctrl.dig_gain_step_size); ad9361_of_get_bool(iodev, np, "adi,agc-sync-for-gain-counter-enable", &pdata->gain_ctrl.sync_for_gain_counter_en); ad9361_of_get_u32(iodev, np, "adi,agc-gain-update-interval-us", 1000, &pdata->gain_ctrl.gain_update_interval_us); ad9361_of_get_bool(iodev, np, "adi,agc-immed-gain-change-if-large-adc-overload-enable", &pdata->gain_ctrl.immed_gain_change_if_large_adc_overload); ad9361_of_get_bool(iodev, np, "adi,agc-immed-gain-change-if-large-lmt-overload-enable", &pdata->gain_ctrl.immed_gain_change_if_large_lmt_overload); /* * Fast AGC */ ad9361_of_get_u32(iodev, np, "adi,fagc-dec-pow-measurement-duration", 64, &pdata->gain_ctrl.f_agc_dec_pow_measuremnt_duration); ad9361_of_get_u32(iodev, np, "adi,fagc-state-wait-time-ns", 260, &pdata->gain_ctrl.f_agc_state_wait_time_ns); /* 0x117 0..31 RX samples -> time-ns */ /* Fast AGC - Low Power */ ad9361_of_get_bool(iodev, np, "adi,fagc-allow-agc-gain-increase-enable", &pdata->gain_ctrl.f_agc_allow_agc_gain_increase); /* 0x110:1 */ ad9361_of_get_u32(iodev, np, "adi,fagc-lp-thresh-increment-time", 5, &pdata->gain_ctrl.f_agc_lp_thresh_increment_time); /* 0x11B RX samples */ ad9361_of_get_u32(iodev, np, "adi,fagc-lp-thresh-increment-steps", 1, &pdata->gain_ctrl.f_agc_lp_thresh_increment_steps); /* 0x117 1..8 */ ad9361_of_get_bool(iodev, np, "adi,fagc-lock-level-lmt-gain-increase-enable", &pdata->gain_ctrl.f_agc_lock_level_lmt_gain_increase_en); /* 0x111:6 (split table)*/ ad9361_of_get_u32(iodev, np, "adi,fagc-lock-level-gain-increase-upper-limit", 5, &pdata->gain_ctrl.f_agc_lock_level_gain_increase_upper_limit); /* 0x118 0..63 */ /* Fast AGC - Peak Detectors and Final Settling */ ad9361_of_get_u32(iodev, np, "adi,fagc-lpf-final-settling-steps", 1, &pdata->gain_ctrl.f_agc_lpf_final_settling_steps); /* 0x112:6 0..3 (Post Lock Level Step)*/ ad9361_of_get_u32(iodev, np, "adi,fagc-lmt-final-settling-steps", 1, &pdata->gain_ctrl.f_agc_lmt_final_settling_steps); /* 0x113:6 0..3 (Post Lock Level Step)*/ ad9361_of_get_u32(iodev, np, "adi,fagc-final-overrange-count", 3, &pdata->gain_ctrl.f_agc_final_overrange_count); /* 0x116:5 0..7 */ /* Fast AGC - Final Power Test */ ad9361_of_get_bool(iodev, np, "adi,fagc-gain-increase-after-gain-lock-enable", &pdata->gain_ctrl.f_agc_gain_increase_after_gain_lock_en); /* 0x110:7 */ /* Fast AGC - Unlocking the Gain */ /* 0 = MAX Gain, 1 = Optimized Gain, 2 = Set Gain */ ad9361_of_get_u32(iodev, np, "adi,fagc-gain-index-type-after-exit-rx-mode", 0, &pdata->gain_ctrl.f_agc_gain_index_type_after_exit_rx_mode); /* 0x110:[4,2] */ ad9361_of_get_bool(iodev, np, "adi,fagc-use-last-lock-level-for-set-gain-enable", &pdata->gain_ctrl.f_agc_use_last_lock_level_for_set_gain_en); /* 0x111:7 */ ad9361_of_get_bool(iodev, np, "adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable", &pdata->gain_ctrl.f_agc_rst_gla_stronger_sig_thresh_exceeded_en); /* 0x111:7 */ ad9361_of_get_u32(iodev, np, "adi,fagc-optimized-gain-offset", 5, &pdata->gain_ctrl.f_agc_optimized_gain_offset); /*0x116 0..15 steps */ ad9361_of_get_u32(iodev, np, "adi,fagc-rst-gla-stronger-sig-thresh-above-ll", 10, &pdata->gain_ctrl.f_agc_rst_gla_stronger_sig_thresh_above_ll); /*0x113 0..63 dbFS */ ad9361_of_get_bool(iodev, np, "adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable", &pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en); /* 0x110:6 */ ad9361_of_get_bool(iodev, np, "adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable", &pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_goto_optim_gain_en); /* 0x110:6 */ ad9361_of_get_u32(iodev, np, "adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll", 10, &pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_sig_thresh_below_ll); /* 0x112 */ ad9361_of_get_u32(iodev, np, "adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt", 8, &pdata->gain_ctrl.f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt); /* 0x119 0..63 RX samples */ ad9361_of_get_bool(iodev, np, "adi,fagc-rst-gla-large-adc-overload-enable", &pdata->gain_ctrl.f_agc_rst_gla_large_adc_overload_en); /*0x110:~1 and 0x114:~7 */ ad9361_of_get_bool(iodev, np, "adi,fagc-rst-gla-large-lmt-overload-enable", &pdata->gain_ctrl.f_agc_rst_gla_large_lmt_overload_en); /*0x110:~1 */ ad9361_of_get_bool(iodev, np, "adi,fagc-rst-gla-en-agc-pulled-high-enable", &pdata->gain_ctrl.f_agc_rst_gla_en_agc_pulled_high_en); ad9361_of_get_u32(iodev, np, "adi,fagc-rst-gla-if-en-agc-pulled-high-mode", 0, &pdata->gain_ctrl.f_agc_rst_gla_if_en_agc_pulled_high_mode); /* 0x0FB, 0x111 */ ad9361_of_get_u32(iodev, np, "adi,fagc-power-measurement-duration-in-state5", 64, &pdata->gain_ctrl.f_agc_power_measurement_duration_in_state5); /* 0x109, 0x10a RX samples 0..524288 */ ad9361_of_get_u32(iodev, np, "adi,fagc-adc-large-overload-inc-steps", 2, /* 0x106 [D6:D4] 0..7 */ &pdata->gain_ctrl.f_agc_large_overload_inc_steps); /* Name is misleading should be dec-steps */ /* RSSI Control */ ad9361_of_get_u32(iodev, np, "adi,rssi-restart-mode", 3, &pdata->rssi_ctrl.restart_mode); ad9361_of_get_bool(iodev, np, "adi,rssi-unit-is-rx-samples-enable", &pdata->rssi_ctrl.rssi_unit_is_rx_samples); ad9361_of_get_u32(iodev, np, "adi,rssi-delay", 1, &pdata->rssi_ctrl.rssi_delay); ad9361_of_get_u32(iodev, np, "adi,rssi-wait", 1, &pdata->rssi_ctrl.rssi_wait); ad9361_of_get_u32(iodev, np, "adi,rssi-duration", 1000, &pdata->rssi_ctrl.rssi_duration); /* RSSI Gain Step Error Tables */ ret = of_property_read_u32_array(np, "adi,rssi-gain-step-lna-error-table", pdata->rssi_lna_err_tbl, 4); ret |= of_property_read_u32_array(np, "adi,rssi-gain-step-mixer-error-table", pdata->rssi_mixer_err_tbl, 16); ret |= of_property_read_u32_array(np, "adi,rssi-gain-step-calibration-register-values", pdata->rssi_gain_step_calib_reg_val, 5); if (ret) pdata->rssi_skip_calib = true; else pdata->rssi_skip_calib = false; /* Control Outs Control */ ad9361_of_get_u32(iodev, np, "adi,ctrl-outs-index", 0, &pdata->ctrl_outs_ctrl.index); ad9361_of_get_u32(iodev, np, "adi,ctrl-outs-enable-mask", 0xFF, &pdata->ctrl_outs_ctrl.en_mask); /* eLNA Control */ ad9361_of_get_u32(iodev, np, "adi,elna-settling-delay-ns", 0, &pdata->elna_ctrl.settling_delay_ns); ad9361_of_get_u32(iodev, np, "adi,elna-gain-mdB", 0, &pdata->elna_ctrl.gain_mdB); ad9361_of_get_u32(iodev, np, "adi,elna-bypass-loss-mdB", 0, &pdata->elna_ctrl.bypass_loss_mdB); ad9361_of_get_bool(iodev, np, "adi,elna-rx1-gpo0-control-enable", &pdata->elna_ctrl.elna_1_control_en); ad9361_of_get_bool(iodev, np, "adi,elna-rx2-gpo1-control-enable", &pdata->elna_ctrl.elna_2_control_en); ad9361_of_get_bool(iodev, np, "adi,elna-gaintable-all-index-enable", &pdata->elna_ctrl.elna_in_gaintable_all_index_en); /* AuxADC Temp Sense Control */ ad9361_of_get_u32(iodev, np, "adi,temp-sense-measurement-interval-ms", 1000, &pdata->auxadc_ctrl.temp_time_inteval_ms); ad9361_of_get_u32(iodev, np, "adi,temp-sense-offset-signed", 0xBD, &pdata->auxadc_ctrl.offset); /* signed */ ad9361_of_get_bool(iodev, np, "adi,temp-sense-periodic-measurement-enable", &pdata->auxadc_ctrl.periodic_temp_measuremnt); ad9361_of_get_u32(iodev, np, "adi,temp-sense-decimation", 256, &pdata->auxadc_ctrl.temp_sensor_decimation); ad9361_of_get_u32(iodev, np, "adi,aux-adc-rate", 40000000UL, &pdata->auxadc_ctrl.auxadc_clock_rate); ad9361_of_get_u32(iodev, np, "adi,aux-adc-decimation", 256, &pdata->auxadc_ctrl.auxadc_decimation); /* AuxDAC Control */ ad9361_of_get_bool(iodev, np, "adi,aux-dac-manual-mode-enable", &pdata->auxdac_ctrl.auxdac_manual_mode_en); ad9361_of_get_u32(iodev, np, "adi,aux-dac1-default-value-mV", 0, &pdata->auxdac_ctrl.dac1_default_value); ad9361_of_get_bool(iodev, np, "adi,aux-dac1-active-in-rx-enable", &pdata->auxdac_ctrl.dac1_in_rx_en); ad9361_of_get_bool(iodev, np, "adi,aux-dac1-active-in-tx-enable", &pdata->auxdac_ctrl.dac1_in_tx_en); ad9361_of_get_bool(iodev, np, "adi,aux-dac1-active-in-alert-enable", &pdata->auxdac_ctrl.dac1_in_alert_en); ad9361_of_get_u32(iodev, np, "adi,aux-dac1-rx-delay-us", 0, &pdata->auxdac_ctrl.dac1_rx_delay_us); ad9361_of_get_u32(iodev, np, "adi,aux-dac1-tx-delay-us", 0, &pdata->auxdac_ctrl.dac1_tx_delay_us); ad9361_of_get_u32(iodev, np, "adi,aux-dac2-default-value-mV", 0, &pdata->auxdac_ctrl.dac2_default_value); ad9361_of_get_bool(iodev, np, "adi,aux-dac2-active-in-rx-enable", &pdata->auxdac_ctrl.dac2_in_rx_en); ad9361_of_get_bool(iodev, np, "adi,aux-dac2-active-in-tx-enable", &pdata->auxdac_ctrl.dac2_in_tx_en); ad9361_of_get_bool(iodev, np, "adi,aux-dac2-active-in-alert-enable", &pdata->auxdac_ctrl.dac2_in_alert_en); ad9361_of_get_u32(iodev, np, "adi,aux-dac2-rx-delay-us", 0, &pdata->auxdac_ctrl.dac2_rx_delay_us); ad9361_of_get_u32(iodev, np, "adi,aux-dac2-tx-delay-us", 0, &pdata->auxdac_ctrl.dac2_tx_delay_us); /* GPO Control */ ad9361_of_get_bool(iodev, np, "adi,gpo-manual-mode-enable", &pdata->gpo_ctrl.gpo_manual_mode_en); ad9361_of_get_u32(iodev, np, "adi,gpo-manual-mode-enable-mask", 0, &pdata->gpo_ctrl.gpo_manual_mode_enable_mask); ad9361_of_get_bool(iodev, np, "adi,gpo0-inactive-state-high-enable", &pdata->gpo_ctrl.gpo0_inactive_state_high_en); ad9361_of_get_bool(iodev, np, "adi,gpo1-inactive-state-high-enable", &pdata->gpo_ctrl.gpo1_inactive_state_high_en); ad9361_of_get_bool(iodev, np, "adi,gpo2-inactive-state-high-enable", &pdata->gpo_ctrl.gpo2_inactive_state_high_en); ad9361_of_get_bool(iodev, np, "adi,gpo3-inactive-state-high-enable", &pdata->gpo_ctrl.gpo3_inactive_state_high_en); ad9361_of_get_bool(iodev, np, "adi,gpo0-slave-rx-enable", &pdata->gpo_ctrl.gpo0_slave_rx_en); ad9361_of_get_bool(iodev, np, "adi,gpo0-slave-tx-enable", &pdata->gpo_ctrl.gpo0_slave_tx_en); ad9361_of_get_bool(iodev, np, "adi,gpo1-slave-rx-enable", &pdata->gpo_ctrl.gpo1_slave_rx_en); ad9361_of_get_bool(iodev, np, "adi,gpo1-slave-tx-enable", &pdata->gpo_ctrl.gpo1_slave_tx_en); ad9361_of_get_bool(iodev, np, "adi,gpo2-slave-rx-enable", &pdata->gpo_ctrl.gpo2_slave_rx_en); ad9361_of_get_bool(iodev, np, "adi,gpo2-slave-tx-enable", &pdata->gpo_ctrl.gpo2_slave_tx_en); ad9361_of_get_bool(iodev, np, "adi,gpo3-slave-rx-enable", &pdata->gpo_ctrl.gpo3_slave_rx_en); ad9361_of_get_bool(iodev, np, "adi,gpo3-slave-tx-enable", &pdata->gpo_ctrl.gpo3_slave_tx_en); ad9361_of_get_u32(iodev, np, "adi,gpo0-rx-delay-us", 0, &pdata->gpo_ctrl.gpo0_rx_delay_us); ad9361_of_get_u32(iodev, np, "adi,gpo0-tx-delay-us", 0, &pdata->gpo_ctrl.gpo0_tx_delay_us); ad9361_of_get_u32(iodev, np, "adi,gpo1-rx-delay-us", 0, &pdata->gpo_ctrl.gpo1_rx_delay_us); ad9361_of_get_u32(iodev, np, "adi,gpo1-tx-delay-us", 0, &pdata->gpo_ctrl.gpo1_tx_delay_us); ad9361_of_get_u32(iodev, np, "adi,gpo2-rx-delay-us", 0, &pdata->gpo_ctrl.gpo2_rx_delay_us); ad9361_of_get_u32(iodev, np, "adi,gpo2-tx-delay-us", 0, &pdata->gpo_ctrl.gpo2_tx_delay_us); ad9361_of_get_u32(iodev, np, "adi,gpo3-rx-delay-us", 0, &pdata->gpo_ctrl.gpo3_rx_delay_us); ad9361_of_get_u32(iodev, np, "adi,gpo3-tx-delay-us", 0, &pdata->gpo_ctrl.gpo3_tx_delay_us); /* Tx Monitor Control */ ad9361_of_get_u32(iodev, np, "adi,txmon-low-high-thresh", 37000, &pdata->txmon_ctrl.low_high_gain_threshold_mdB); ad9361_of_get_u32(iodev, np, "adi,txmon-low-gain", 0, &pdata->txmon_ctrl.low_gain_dB); ad9361_of_get_u32(iodev, np, "adi,txmon-high-gain", 24, &pdata->txmon_ctrl.high_gain_dB); ad9361_of_get_bool(iodev, np, "adi,txmon-dc-tracking-enable", &pdata->txmon_ctrl.tx_mon_track_en); ad9361_of_get_bool(iodev, np, "adi,txmon-one-shot-mode-enable", &pdata->txmon_ctrl.one_shot_mode_en); ad9361_of_get_u32(iodev, np, "adi,txmon-delay", 511, &pdata->txmon_ctrl.tx_mon_delay); ad9361_of_get_u32(iodev, np, "adi,txmon-duration", 8192, &pdata->txmon_ctrl.tx_mon_duration); ad9361_of_get_u32(iodev, np, "adi,txmon-1-front-end-gain", 2, &pdata->txmon_ctrl.tx1_mon_front_end_gain); ad9361_of_get_u32(iodev, np, "adi,txmon-2-front-end-gain", 2, &pdata->txmon_ctrl.tx2_mon_front_end_gain); ad9361_of_get_u32(iodev, np, "adi,txmon-1-lo-cm", 48, &pdata->txmon_ctrl.tx1_mon_lo_cm); ad9361_of_get_u32(iodev, np, "adi,txmon-2-lo-cm", 48, &pdata->txmon_ctrl.tx2_mon_lo_cm); return pdata; } #else static inline struct ad9361_phy_platform_data *ad9361_phy_parse_dt(struct iio_dev *iodev, struct device *dev) { return NULL; } #endif static ssize_t ad9361_fir_bin_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); struct ad9361_rf_phy *phy = iio_priv(indio_dev); return ad9361_parse_fir(phy, buf, count); } static ssize_t ad9361_fir_bin_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; if (off) return 0; return sprintf(buf, "FIR Rx: %d,%d Tx: %d,%d\n", st->rx_fir_ntaps, st->rx_fir_dec, st->tx_fir_ntaps, st->tx_fir_int); } static void ad9361_free_gt(struct ad9361_rf_phy *phy, struct gain_table_info *table) { int i; if (!table || table == ad9361_adi_gt_info) return; for (i = 0; i < MAX_NUM_GAIN_TABLES; i++) if (table[i].abs_gain_tbl) { devm_kfree(&phy->spi->dev, table[i].abs_gain_tbl); } devm_kfree(&phy->spi->dev, table); } static struct gain_table_info * ad9361_parse_gt(struct ad9361_rf_phy *phy, char *data, u32 size) { struct gain_table_info *table; bool header_found; int i = 0, ret, table_num = 0; char *line, *ptr = data; u8 *p; header_found = false; table = devm_kzalloc(&phy->spi->dev, sizeof(struct gain_table_info) * MAX_NUM_GAIN_TABLES, GFP_KERNEL); if (!table) { ret = -ENOMEM; goto out; } while ((line = strsep(&ptr, "\n"))) { if (line >= data + size) { break; } if (line[0] == '#') /* skip comment lines */ continue; if (strstr(line, "list>")) /* skip <[/]list> */ continue; if (!header_found) { char type[40]; unsigned model, dest; u64 start; u64 end; ret = sscanf(line, " ", &model , type, &dest, &start, &end); if (ret == 5) { if (!(model == 9361 || model == 9364)) { ret = -EINVAL; goto out; } if (start >= end) { ret = -EINVAL; goto out; } p = devm_kzalloc(&phy->spi->dev, sizeof(u8[MAX_GAIN_TABLE_SIZE]) + sizeof(u8[3][MAX_GAIN_TABLE_SIZE]), GFP_KERNEL); if (!p) { ret = -ENOMEM; goto out; } table[table_num].abs_gain_tbl = (s8 *) p; table[table_num].tab = (u8 (*) [3]) (p + sizeof(u8[MAX_GAIN_TABLE_SIZE])); table[table_num].split_table = sysfs_streq(type, "SPLIT"); table[table_num].start = start; table[table_num].end = end; header_found = true; i = 0; continue; } else { header_found = false; } } if (header_found) { int a,b,c,d; ret = sscanf(line, " %i,%i,%i,%i", &a, &b, &c, &d); if (ret == 4) { if (i >= MAX_GAIN_TABLE_SIZE) goto out; if ((i > 0) && (a < table[table_num].abs_gain_tbl[i - 1])) dev_warn(&phy->spi->dev, "Gain table must be monotonic"); table[table_num].abs_gain_tbl[i] = a; table[table_num].tab[i][0] = b; table[table_num].tab[i][1] = c; table[table_num].tab[i][2] = d; i++; continue; } else if (strstr(line, "")) { table[table_num].max_index = i; header_found = false; table_num++; if (table_num >= (MAX_NUM_GAIN_TABLES - 2)) { dev_warn(&phy->spi->dev, "Skipping tables"); goto done; } continue; } else { dev_err(&phy->spi->dev, "ERROR: Malformed gain table"); goto out_free_tables; } } } done: dev_dbg(&phy->spi->dev, "%s: table_num %d header_found %d", __func__, table_num, header_found); if (table_num > 0 && !header_found) return table; else return ERR_PTR(-EFAULT); out_free_tables: ad9361_free_gt(phy, table); out: return ERR_PTR(ret); } static int ad9361_request_gt(struct ad9361_rf_phy *phy, char *filename) { struct ad9361_rf_phy_state *st = phy->state; const struct firmware *fw; struct gain_table_info *table; const char *name; char *cpy; int ret; if (filename == NULL) { if (phy->spi->dev.of_node) { if (of_property_read_string(phy->spi->dev.of_node, "adi,gaintable-name", &name)) return -ENOENT; } else { return -ENOENT; } } else { name = filename; } dev_dbg(&phy->spi->dev, "request gaintable: %s\n", name); ret = request_firmware(&fw, name, &phy->spi->dev); if (ret) { dev_err(&phy->spi->dev, "request_firmware(%s) failed with %i\n", name, ret); return ret; } cpy = kzalloc(fw->size, GFP_KERNEL); if (!cpy) goto out; memcpy(cpy, fw->data, fw->size); table = ad9361_parse_gt(phy, cpy, fw->size); if (IS_ERR_OR_NULL(table)) { ret = PTR_ERR(table); goto out_free; } ad9361_free_gt(phy, phy->gt_info); st->current_table = -1; phy->gt_info = table; out_free: kfree(cpy); out: release_firmware(fw); return ret; } static ssize_t ad9361_gt_bin_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); struct ad9361_rf_phy *phy = iio_priv(indio_dev); struct ad9361_rf_phy_state *st = phy->state; struct gain_table_info *table; if (off == 0) { if (phy->bin_attr_buf == NULL) { phy->bin_attr_buf = devm_kzalloc(&phy->spi->dev, bin_attr->size, GFP_KERNEL); if (!phy->bin_attr_buf) return -ENOMEM; } else { memset(phy->bin_attr_buf, 0, bin_attr->size); } } memcpy(phy->bin_attr_buf + off, buf, count); if (strnstr(phy->bin_attr_buf, "", off + count) == NULL) return count; table = ad9361_parse_gt(phy, phy->bin_attr_buf, off + count); if (IS_ERR_OR_NULL(table)) return PTR_ERR(table); mutex_lock(&phy->indio_dev->mlock); ad9361_free_gt(phy, phy->gt_info); st->current_table = -1; phy->gt_info = table; ad9361_load_gt(phy, ad9361_from_clk( clk_get_rate(phy->clks[RX_RFPLL])), GT_RX1 + GT_RX2); mutex_unlock(&phy->indio_dev->mlock); return count; } static ssize_t ad9361_gt_bin_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); struct ad9361_rf_phy *phy = iio_priv(indio_dev); int ret, j, len = 0; char *tab; tab = kzalloc(bin_attr->size, GFP_KERNEL); if (tab == NULL) return -ENOMEM; len += snprintf(tab + len, bin_attr->size - len, "\n", 9361, phy->gt_info[ad9361_gt(phy)].split_table ? "SPLIT" : "FULL", 3, phy->gt_info[ad9361_gt(phy)].start, phy->gt_info[ad9361_gt(phy)].end); for (j = 0; j < phy->gt_info[ad9361_gt(phy)].max_index; j++) len += snprintf(tab + len, bin_attr->size - len, "%d, 0x%.2X, 0x%.2X, 0x%.2X\n", phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[j], phy->gt_info[ad9361_gt(phy)].tab[j][0], phy->gt_info[ad9361_gt(phy)].tab[j][1], phy->gt_info[ad9361_gt(phy)].tab[j][2]); len += snprintf(tab + len, bin_attr->size - len, "\n"); ret = memory_read_from_buffer(buf, count, &off, tab, bin_attr->size); kfree(tab); return ret; } static int ad9361_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct ad9361_rf_phy_state *st; struct ad9361_rf_phy *phy; struct clk *clk = NULL; int ret, rev; dev_info(&spi->dev, "%s : enter (%s)", __func__, spi_get_device_id(spi)->name); clk = devm_clk_get(&spi->dev, NULL); if (IS_ERR(clk)) { return -EPROBE_DEFER; } indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*phy)); if (indio_dev == NULL) return -ENOMEM; st = devm_kzalloc(&spi->dev, sizeof(*st), GFP_KERNEL); if (st == NULL) return -ENOMEM; phy = iio_priv(indio_dev); phy->state = st; phy->indio_dev = indio_dev; phy->spi = spi; phy->clk_refin = clk; ad9361_init_state(phy); phy->pdata = ad9361_phy_parse_dt(indio_dev, &spi->dev); if (phy->pdata == NULL) return -EINVAL; phy->pdata->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(phy->pdata->reset_gpio)) return PTR_ERR(phy->pdata->reset_gpio); /* Optional: next three used for MCS synchronization */ phy->pdata->sync_gpio = devm_gpiod_get_optional(&spi->dev, "sync", GPIOD_OUT_LOW); if (IS_ERR(phy->pdata->sync_gpio)) return PTR_ERR(phy->pdata->sync_gpio); phy->pdata->cal_sw1_gpio = devm_gpiod_get_optional(&spi->dev, "cal-sw1", GPIOD_OUT_LOW); if (IS_ERR(phy->pdata->cal_sw1_gpio)) return PTR_ERR(phy->pdata->cal_sw1_gpio); phy->pdata->cal_sw2_gpio = devm_gpiod_get_optional(&spi->dev, "cal-sw2", GPIOD_OUT_LOW); if (IS_ERR(phy->pdata->cal_sw2_gpio)) return PTR_ERR(phy->pdata->cal_sw2_gpio); ret = ad9361_register_ext_band_control(phy); if (ret < 0) dev_warn(&spi->dev, "%s: failed to initialize ext band control\n", __func__); phy->gt_info = ad9361_adi_gt_info; ad9361_request_gt(phy, NULL); ad9361_reset(phy); ret = ad9361_spi_read(spi, REG_PRODUCT_ID); if ((ret & PRODUCT_ID_MASK) != PRODUCT_ID_9361) { dev_err(&spi->dev, "%s : Unsupported PRODUCT_ID 0x%X", __func__, ret); return -ENODEV; } rev = ret & REV_MASK; if (spi_get_device_id(spi)->driver_data == ID_AD9364) { phy->pdata->rx2tx2 = false; phy->pdata->rx1tx1_mode_use_rx_num = 1; phy->pdata->rx1tx1_mode_use_tx_num = 1; } INIT_WORK(&phy->work, ad9361_work_func); init_completion(&phy->complete); ret = register_clocks(phy); if (ret < 0) return ret; ret = ad9361_setup(phy); if (ret < 0) goto out_unregister_notifier; ret = of_clk_add_provider(spi->dev.of_node, of_clk_src_onecell_get, &phy->clk_data); if (ret) goto out_disable_clocks; sysfs_bin_attr_init(&phy->bin); phy->bin.attr.name = "filter_fir_config"; phy->bin.attr.mode = S_IWUSR | S_IRUGO; phy->bin.write = ad9361_fir_bin_write; phy->bin.read = ad9361_fir_bin_read; phy->bin.size = 4096; sysfs_bin_attr_init(&phy->bin_gt); phy->bin_gt.attr.name = "gain_table_config"; phy->bin_gt.attr.mode = S_IWUSR | S_IRUGO; phy->bin_gt.write = ad9361_gt_bin_write; phy->bin_gt.read = ad9361_gt_bin_read; phy->bin_gt.size = 4096; indio_dev->dev.parent = &spi->dev; if (spi->dev.of_node) indio_dev->name = spi->dev.of_node->name; else indio_dev->name = "ad9361-phy"; indio_dev->info = &ad9361_phy_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad9361_phy_chan; indio_dev->num_channels = ARRAY_SIZE(ad9361_phy_chan) - (phy->pdata->rx2tx2 ? 0 : 2); ret = iio_device_register(indio_dev); if (ret < 0) goto out_clk_del_provider; ret = ad9361_register_axi_converter(phy); if (ret < 0) goto out_iio_device_unregister; ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &phy->bin); if (ret < 0) goto out_iio_device_unregister; ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &phy->bin_gt); if (ret < 0) goto out_iio_device_unregister; ret = ad9361_register_debugfs(indio_dev); if (ret < 0) dev_warn(&spi->dev, "%s: failed to register debugfs", __func__); dev_info(&spi->dev, "%s : AD936x Rev %d successfully initialized", __func__, rev); return 0; out_iio_device_unregister: iio_device_unregister(indio_dev); out_clk_del_provider: of_clk_del_provider(spi->dev.of_node); out_disable_clocks: ad9361_clks_disable(phy); out_unregister_notifier: clk_notifier_unregister(phy->clks[RX_RFPLL], &phy->clk_nb_rx); clk_notifier_unregister(phy->clks[TX_RFPLL], &phy->clk_nb_tx); return ret; } static int ad9361_remove(struct spi_device *spi) { struct ad9361_rf_phy *phy = ad9361_spi_to_phy(spi); ad9361_unregister_ext_band_control(phy); sysfs_remove_bin_file(&phy->indio_dev->dev.kobj, &phy->bin_gt); sysfs_remove_bin_file(&phy->indio_dev->dev.kobj, &phy->bin); iio_device_unregister(phy->indio_dev); of_clk_del_provider(spi->dev.of_node); clk_notifier_unregister(phy->clks[RX_RFPLL], &phy->clk_nb_rx); clk_notifier_unregister(phy->clks[TX_RFPLL], &phy->clk_nb_tx); ad9361_clks_disable(phy); return 0; } static const struct spi_device_id ad9361_id[] = { {"ad9361", ID_AD9361}, /* 2RX2TX */ {"ad9364", ID_AD9364}, /* 1RX1TX */ {"ad9361-2x", ID_AD9361_2}, /* 2 x 2RX2TX */ {"ad9363a", ID_AD9363A}, /* 2RX2TX */ {} }; MODULE_DEVICE_TABLE(spi, ad9361_id); static struct spi_driver ad9361_driver = { .driver = { .name = "ad9361", .owner = THIS_MODULE, }, .probe = ad9361_probe, .remove = ad9361_remove, .id_table = ad9361_id, }; module_spi_driver(ad9361_driver); MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD9361 ADC"); MODULE_LICENSE("GPL v2");