function [exactMatch, closest_pllcr, closest_clkin_div] = autoSetPLLSettings(cpu_clock_freq, osc_freq, SubFamily, max_allowed_clock_freq) %#ok<INUSD>
    [max_clkin_div, min_clkin_div] = check_divsel(SubFamily);
    [~, max_pllcr] = get_closest_pllcr(1, SubFamily);
    closest_pllcr = max_pllcr;
    closest_clkin_div = max_clkin_div;
    variance_clock = inf;

    while (max_clkin_div >= min_clkin_div) && (variance_clock ~= 0)
        % Check validity of the PLL output calculated based on DIVSEL
        [vco_clock_valid, vco_clock] = check_valid_vcoclk(cpu_clock_freq, max_clkin_div, SubFamily);
        if vco_clock_valid
            % Calculate PLLCR
            pllcr = get_closest_pllcr(vco_clock / osc_freq, SubFamily);

            % Record closest PLLCR and closest DIVSEL when variance in the
            % clock is minimum. zero variance indicates correct match.
            raw_closest_freq = osc_freq * pllcr / max_clkin_div;
            if (abs(cpu_clock_freq - raw_closest_freq) < variance_clock)
                variance_clock = abs(cpu_clock_freq - raw_closest_freq);
                closest_pllcr = pllcr;
                closest_clkin_div = max_clkin_div;
            end
        end
        switch SubFamily
            case {'2837xS','2837xD', '2807x'}
                if (max_clkin_div==2)
                    max_clkin_div = 1;
                else
                    max_clkin_div = max_clkin_div - 2;
                end
            otherwise
                max_clkin_div = max_clkin_div / 2;
        end
    end

    % Zero Variance indicates correct match else the closest match
    if (variance_clock == 0)
        % exact match
        exactMatch = true;
    else
        % Closest match
        exactMatch = false;
    end
% end %autoSetPLLSettings

function [closest_pllcr, max_pllcr] = get_closest_pllcr(pllcr, SubFamily)
    switch SubFamily
        case {'2802x', '2803x', '2805x'}
            max_pllcr = 12;
        case '2806x'
            max_pllcr = 16;
        case {'2837xS','2837xD', '2807x'}
            max_pllcr = 127.75;
        case {'2833x', '2823x', '28x3x'}
            max_pllcr = 10;
        case '2834x'
            max_pllcr = 32;
        case {'280x', '2801x'}
            max_pllcr = 10;
        case {'2804x', '28044'}
            max_pllcr = 10;
        case '281x'
            max_pllcr = 10;
        otherwise
            max_pllcr = 10;
    end
    
    if pllcr > max_pllcr
        closest_pllcr = max_pllcr;
    else
        switch SubFamily
            case {'2837xS','2837xD', '2807x'}
                closest_pllcr = (round(pllcr * 4))/4;                
            otherwise
                closest_pllcr = round(pllcr);
        end
        if closest_pllcr <= 0
            closest_pllcr = 1;
        end
    end
% end %check_valid_pllcr

function [max_clkin_div, min_clkin_div] = check_divsel(SubFamily)

    switch SubFamily
        case {'2802x', '2803x', '2805x', '2806x'}
            max_clkin_div = 4;
            min_clkin_div = 1;
        case {'2833x', '2823x', '28x3x'}
            max_clkin_div = 4;
            min_clkin_div = 1;
        case '2834x'
            max_clkin_div = 8;
            min_clkin_div = 1;
        case {'280x', '2801x'}
            max_clkin_div = 2;
            min_clkin_div = 1;
        case {'2804x', '28044'}
            max_clkin_div = 2;
            min_clkin_div = 1;
        case {'281x'}
            max_clkin_div = 2;
            min_clkin_div = 2;
        case {'2837xS','2837xD', '2807x'}
            max_clkin_div = 126;
            min_clkin_div = 1;
        otherwise
            max_clkin_div = 4;
            min_clkin_div = 1;
    end
    
% end %check_valid_divsel

% CPU Clock Calculation formula
% CPU_Frequency = VCO_CLOCK_Frequency / DIVSEL
% where VCO_CLOCK_Frequency = Oscillator_Frequency * PLLCR
% The VCO_CLOCK_Frequency should within some limit and this changes from
% processor to processor. Hence, This function checks valid
% VCO_CLOCK_Frequency based on PLLCR or DIVSEL (clkin_div).
function [vco_clock_valid, vco_clock] = check_valid_vcoclk(cpu_clock_freq, clkin_div, SubFamily)
    vco_clock = cpu_clock_freq * clkin_div;
    
    switch SubFamily
        case {'2802x', '2803x', '2805x', '2806x'}
            if vco_clock >= 50 %MHz (minimum VCOCLK freq required)
                vco_clock_valid = true;
            else
                vco_clock_valid = false;%'greater than or equal to 50MHz';
            end
        case '28x3x'
            if vco_clock <= 300 %MHz
                %vco_clock_warn = '';
                vco_clock_valid = true;
            else
                %vco_clock_warn = 'less than or equal to 300MHz';
                vco_clock_valid = false;
            end
        case '2834x'
            if vco_clock >= 400 && ... %MHz
                    vco_clock <= 600; %MHz
                %vco_clock_warn = '';
                vco_clock_valid = true;
            else
                %vco_clock_warn = 'in between 400MHz to 600MHz';
                vco_clock_valid = false;
            end
        case {'2837xS','2837xD', '2807x'}
            if vco_clock >= 110 && ... %MHz
                    vco_clock <= 550; %MHz
                vco_clock_valid = true;
            else
                vco_clock_valid = false;
            end
        otherwise
            %vco_clock_warn = '';
            vco_clock_valid = true;
    end
% end %check_valid_vcoclk
