diff options
Diffstat (limited to 'drivers')
23 files changed, 2343 insertions, 70 deletions
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 8094420548..93e27f131c 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -29,6 +29,16 @@ config ADC_SANDBOX - 16-bit resolution - single and multi-channel conversion mode +config SARADC_MESON + bool "Enable Amlogic Meson SARADC driver" + imply REGMAP + help + This enables driver for Amlogic Meson SARADC. + It provides: + - 8 analog input channels + - 1O or 12 bits resolution + - Up to 1MSPS of sample rate + config SARADC_ROCKCHIP bool "Enable Rockchip SARADC driver" help diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile index 4b5aa693ec..95c93d4c57 100644 --- a/drivers/adc/Makefile +++ b/drivers/adc/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_ADC) += adc-uclass.o obj-$(CONFIG_ADC_EXYNOS) += exynos-adc.o obj-$(CONFIG_ADC_SANDBOX) += sandbox.o obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o +obj-$(CONFIG_SARADC_MESON) += meson-saradc.o diff --git a/drivers/adc/meson-saradc.c b/drivers/adc/meson-saradc.c new file mode 100644 index 0000000000..bcab76d050 --- /dev/null +++ b/drivers/adc/meson-saradc.c @@ -0,0 +1,723 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + * Copyright (C) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * Amlogic Meson Successive Approximation Register (SAR) A/D Converter + */ + +#include <common.h> +#include <adc.h> +#include <clk.h> +#include <dm.h> +#include <regmap.h> +#include <errno.h> +#include <asm/io.h> +#include <linux/math64.h> +#include <linux/bitfield.h> + +#define MESON_SAR_ADC_REG0 0x00 + #define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31) + #define MESON_SAR_ADC_REG0_BUSY_MASK GENMASK(30, 28) + #define MESON_SAR_ADC_REG0_DELTA_BUSY BIT(30) + #define MESON_SAR_ADC_REG0_AVG_BUSY BIT(29) + #define MESON_SAR_ADC_REG0_SAMPLE_BUSY BIT(28) + #define MESON_SAR_ADC_REG0_FIFO_FULL BIT(27) + #define MESON_SAR_ADC_REG0_FIFO_EMPTY BIT(26) + #define MESON_SAR_ADC_REG0_FIFO_COUNT_MASK GENMASK(25, 21) + #define MESON_SAR_ADC_REG0_ADC_BIAS_CTRL_MASK GENMASK(20, 19) + #define MESON_SAR_ADC_REG0_CURR_CHAN_ID_MASK GENMASK(18, 16) + #define MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL BIT(15) + #define MESON_SAR_ADC_REG0_SAMPLING_STOP BIT(14) + #define MESON_SAR_ADC_REG0_CHAN_DELTA_EN_MASK GENMASK(13, 12) + #define MESON_SAR_ADC_REG0_DETECT_IRQ_POL BIT(10) + #define MESON_SAR_ADC_REG0_DETECT_IRQ_EN BIT(9) + #define MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK GENMASK(8, 4) + #define MESON_SAR_ADC_REG0_FIFO_IRQ_EN BIT(3) + #define MESON_SAR_ADC_REG0_SAMPLING_START BIT(2) + #define MESON_SAR_ADC_REG0_CONTINUOUS_EN BIT(1) + #define MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE BIT(0) + +#define MESON_SAR_ADC_CHAN_LIST 0x04 + #define MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK GENMASK(26, 24) + #define MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(_chan) \ + (GENMASK(2, 0) << ((_chan) * 3)) + +#define MESON_SAR_ADC_AVG_CNTL 0x08 + #define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(_chan) \ + (16 + ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(_chan) \ + (GENMASK(17, 16) << ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(_chan) \ + (0 + ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(_chan) \ + (GENMASK(1, 0) << ((_chan) * 2)) + +#define MESON_SAR_ADC_REG3 0x0c + #define MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY BIT(31) + #define MESON_SAR_ADC_REG3_CLK_EN BIT(30) + #define MESON_SAR_ADC_REG3_BL30_INITIALIZED BIT(28) + #define MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN BIT(27) + #define MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE BIT(26) + #define MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_REG3_DETECT_EN BIT(22) + #define MESON_SAR_ADC_REG3_ADC_EN BIT(21) + #define MESON_SAR_ADC_REG3_PANEL_DETECT_COUNT_MASK GENMASK(20, 18) + #define MESON_SAR_ADC_REG3_PANEL_DETECT_FILTER_TB_MASK GENMASK(17, 16) + #define MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT 10 + #define MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH 5 + #define MESON_SAR_ADC_REG3_BLOCK_DLY_SEL_MASK GENMASK(9, 8) + #define MESON_SAR_ADC_REG3_BLOCK_DLY_MASK GENMASK(7, 0) + +#define MESON_SAR_ADC_DELAY 0x10 + #define MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK GENMASK(25, 24) + #define MESON_SAR_ADC_DELAY_BL30_BUSY BIT(15) + #define MESON_SAR_ADC_DELAY_KERNEL_BUSY BIT(14) + #define MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK GENMASK(23, 16) + #define MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK GENMASK(9, 8) + #define MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK GENMASK(7, 0) + +#define MESON_SAR_ADC_LAST_RD 0x14 + #define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL1_MASK GENMASK(23, 16) + #define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL0_MASK GENMASK(9, 0) + +#define MESON_SAR_ADC_FIFO_RD 0x18 + #define MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK GENMASK(14, 12) + #define MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK GENMASK(11, 0) + +#define MESON_SAR_ADC_AUX_SW 0x1c + #define MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(_chan) \ + (8 + (((_chan) - 2) * 3)) + #define MESON_SAR_ADC_AUX_SW_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_AUX_SW_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_AUX_SW_MODE_SEL BIT(4) + #define MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_AUX_SW_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_AUX_SW_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_CHAN_10_SW 0x20 + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_P_MUX BIT(22) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_N_MUX BIT(21) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MODE_SEL BIT(20) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW BIT(19) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW BIT(18) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YM_DRIVE_SW BIT(17) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XM_DRIVE_SW BIT(16) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK GENMASK(9, 7) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MODE_SEL BIT(4) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_DETECT_IDLE_SW 0x24 + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_SW_EN BIT(26) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_P_MUX BIT(22) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_N_MUX BIT(21) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MODE_SEL BIT(20) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YP_DRIVE_SW BIT(19) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XP_DRIVE_SW BIT(18) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YM_DRIVE_SW BIT(17) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XM_DRIVE_SW BIT(16) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK GENMASK(9, 7) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MODE_SEL BIT(4) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_DELTA_10 0x28 + #define MESON_SAR_ADC_DELTA_10_TEMP_SEL BIT(27) + #define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26) + #define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16) + #define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15) + #define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11 + #define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11) + #define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10) + #define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0) + +/* + * NOTE: registers from here are undocumented (the vendor Linux kernel driver + * and u-boot source served as reference). These only seem to be relevant on + * GXBB and newer. + */ +#define MESON_SAR_ADC_REG11 0x2c + #define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13) + +#define MESON_SAR_ADC_REG13 0x34 + #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8) + +#define MESON_SAR_ADC_MAX_FIFO_SIZE 32 +#define MESON_SAR_ADC_TIMEOUT 100 /* ms */ + +#define NUM_CHANNELS 8 + +#define MILLION 1000000 + +struct meson_saradc_data { + int num_bits; +}; + +struct meson_saradc_priv { + const struct meson_saradc_data *data; + struct regmap *regmap; + struct clk core_clk; + struct clk adc_clk; + bool initialized; + int active_channel; + int calibbias; + int calibscale; +}; + +static unsigned int +meson_saradc_get_fifo_count(struct meson_saradc_priv *priv) +{ + u32 regval; + + regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); + + return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval); +} + +static int meson_saradc_lock(struct meson_saradc_priv *priv) +{ + uint val, timeout = 10000; + + /* prevent BL30 from using the SAR ADC while we are using it */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_KERNEL_BUSY, + MESON_SAR_ADC_DELAY_KERNEL_BUSY); + + /* + * wait until BL30 releases it's lock (so we can use the SAR ADC) + */ + do { + udelay(1); + regmap_read(priv->regmap, MESON_SAR_ADC_DELAY, &val); + } while (val & MESON_SAR_ADC_DELAY_BL30_BUSY && timeout--); + + if (timeout < 0) { + printf("Timeout while waiting for BL30 unlock\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void meson_saradc_unlock(struct meson_saradc_priv *priv) +{ + /* allow BL30 to use the SAR ADC again */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0); +} + +static void meson_saradc_clear_fifo(struct meson_saradc_priv *priv) +{ + unsigned int count, tmp; + + for (count = 0; count < MESON_SAR_ADC_MAX_FIFO_SIZE; count++) { + if (!meson_saradc_get_fifo_count(priv)) + break; + + regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, &tmp); + } +} + +static int meson_saradc_calib_val(struct meson_saradc_priv *priv, int val) +{ + int tmp; + + /* use val_calib = scale * val_raw + offset calibration function */ + tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias; + + return clamp(tmp, 0, (1 << priv->data->num_bits) - 1); +} + +static int meson_saradc_wait_busy_clear(struct meson_saradc_priv *priv) +{ + uint regval, timeout = 10000; + + /* + * NOTE: we need a small delay before reading the status, otherwise + * the sample engine may not have started internally (which would + * seem to us that sampling is already finished). + */ + do { + udelay(1); + regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); + } while (FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, regval) && timeout--); + + if (timeout < 0) + return -ETIMEDOUT; + + return 0; +} + +static int meson_saradc_read_raw_sample(struct meson_saradc_priv *priv, + unsigned int channel, uint *val) +{ + uint regval, fifo_chan, fifo_val, count; + int ret; + + ret = meson_saradc_wait_busy_clear(priv); + if (ret) + return ret; + + count = meson_saradc_get_fifo_count(priv); + if (count != 1) { + printf("ADC FIFO has %d element(s) instead of one\n", count); + return -EINVAL; + } + + regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val); + fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval); + if (fifo_chan != channel) { + printf("ADC FIFO entry belongs to channel %d instead of %d\n", + fifo_chan, channel); + return -EINVAL; + } + + fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval); + fifo_val &= GENMASK(priv->data->num_bits - 1, 0); + *val = meson_saradc_calib_val(priv, fifo_val); + + return 0; +} + +static void meson_saradc_start_sample_engine(struct meson_saradc_priv *priv) +{ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLING_START, + MESON_SAR_ADC_REG0_SAMPLING_START); +} + +static void meson_saradc_stop_sample_engine(struct meson_saradc_priv *priv) +{ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN, 0); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLING_STOP, + MESON_SAR_ADC_REG0_SAMPLING_STOP); + + /* wait until all modules are stopped */ + meson_saradc_wait_busy_clear(priv); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, 0); +} + +enum meson_saradc_avg_mode { + NO_AVERAGING = 0x0, + MEAN_AVERAGING = 0x1, + MEDIAN_AVERAGING = 0x2, +}; + +enum meson_saradc_num_samples { + ONE_SAMPLE = 0x0, + TWO_SAMPLES = 0x1, + FOUR_SAMPLES = 0x2, + EIGHT_SAMPLES = 0x3, +}; + +static void meson_saradc_set_averaging(struct meson_saradc_priv *priv, + unsigned int channel, + enum meson_saradc_avg_mode mode, + enum meson_saradc_num_samples samples) +{ + int val; + + val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL, + MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel), + val); + + val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL, + MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val); +} + +static void meson_saradc_enable_channel(struct meson_saradc_priv *priv, + unsigned int channel) +{ + uint regval; + + /* + * the SAR ADC engine allows sampling multiple channels at the same + * time. to keep it simple we're only working with one *internal* + * channel, which starts counting at index 0 (which means: count = 1). + */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST, + MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, regval); + + /* map channel index 0 to the channel which we want to read */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), channel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST, + MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval); + + regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK, + channel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW, + MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK, + regval); + + regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, + channel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW, + MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, + regval); + + if (channel == 6) + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0); +} + +static int meson_saradc_get_sample(struct meson_saradc_priv *priv, + int chan, uint *val) +{ + int ret; + + ret = meson_saradc_lock(priv); + if (ret) + return ret; + + /* clear the FIFO to make sure we're not reading old values */ + meson_saradc_clear_fifo(priv); + + meson_saradc_set_averaging(priv, chan, MEAN_AVERAGING, EIGHT_SAMPLES); + + meson_saradc_enable_channel(priv, chan); + + meson_saradc_start_sample_engine(priv); + ret = meson_saradc_read_raw_sample(priv, chan, val); + meson_saradc_stop_sample_engine(priv); + + meson_saradc_unlock(priv); + + if (ret) { + printf("failed to read sample for channel %d: %d\n", + chan, ret); + return ret; + } + + return 0; +} + +static int meson_saradc_channel_data(struct udevice *dev, int channel, + unsigned int *data) +{ + struct meson_saradc_priv *priv = dev_get_priv(dev); + + if (channel != priv->active_channel) { + pr_err("Requested channel is not active!"); + return -EINVAL; + } + + return meson_saradc_get_sample(priv, channel, data); +} + +enum meson_saradc_chan7_mux_sel { + CHAN7_MUX_VSS = 0x0, + CHAN7_MUX_VDD_DIV4 = 0x1, + CHAN7_MUX_VDD_DIV2 = 0x2, + CHAN7_MUX_VDD_MUL3_DIV4 = 0x3, + CHAN7_MUX_VDD = 0x4, + CHAN7_MUX_CH7_INPUT = 0x7, +}; + +static void meson_saradc_set_chan7_mux(struct meson_saradc_priv *priv, + enum meson_saradc_chan7_mux_sel sel) +{ + u32 regval; + + regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval); + + udelay(20); +} + +static int meson_saradc_calib(struct meson_saradc_priv *priv) +{ + uint nominal0, nominal1, value0, value1; + int ret; + + /* use points 25% and 75% for calibration */ + nominal0 = (1 << priv->data->num_bits) / 4; + nominal1 = (1 << priv->data->num_bits) * 3 / 4; + + meson_saradc_set_chan7_mux(priv, CHAN7_MUX_VDD_DIV4); + udelay(20); + ret = meson_saradc_get_sample(priv, 7, &value0); + if (ret < 0) + goto out; + + meson_saradc_set_chan7_mux(priv, CHAN7_MUX_VDD_MUL3_DIV4); + udelay(20); + ret = meson_saradc_get_sample(priv, 7, &value1); + if (ret < 0) + goto out; + + if (value1 <= value0) { + ret = -EINVAL; + goto out; + } + + priv->calibscale = div_s64((nominal1 - nominal0) * (s64)MILLION, + value1 - value0); + priv->calibbias = nominal0 - div_s64((s64)value0 * priv->calibscale, + MILLION); + ret = 0; +out: + meson_saradc_set_chan7_mux(priv, CHAN7_MUX_CH7_INPUT); + + return ret; +} + +static int meson_saradc_init(struct meson_saradc_priv *priv) +{ + uint regval; + int ret, i; + + priv->calibscale = MILLION; + + /* + * make sure we start at CH7 input since the other muxes are only used + * for internal calibration. + */ + meson_saradc_set_chan7_mux(priv, CHAN7_MUX_CH7_INPUT); + + /* + * leave sampling delay and the input clocks as configured by + * BL30 to make sure BL30 gets the values it expects when + * reading the temperature sensor. + */ + regmap_read(priv->regmap, MESON_SAR_ADC_REG3, ®val); + if (regval & MESON_SAR_ADC_REG3_BL30_INITIALIZED) + return 0; + + meson_saradc_stop_sample_engine(priv); + + /* update the channel 6 MUX to select the temperature sensor */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL, + MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL); + + /* disable all channels by default */ + regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY, + MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY); + + /* delay between two samples = (10+1) * 1uS */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK, + 10)); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK, + 0)); + + /* delay between two samples = (10+1) * 1uS */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + 10)); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK, + 1)); + + /* + * set up the input channel muxes in MESON_SAR_ADC_CHAN_10_SW + * (0 = SAR_ADC_CH0, 1 = SAR_ADC_CH1) + */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK, + regval); + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, 1); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, + regval); + + /* + * set up the input channel muxes in MESON_SAR_ADC_AUX_SW + * (2 = SAR_ADC_CH2, 3 = SAR_ADC_CH3, ...) and enable + * MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW and + * MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW like the vendor driver. + */ + regval = 0; + for (i = 2; i <= 7; i++) + regval |= i << MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(i); + regval |= MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW; + regval |= MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW; + regmap_write(priv->regmap, MESON_SAR_ADC_AUX_SW, regval); + + ret = meson_saradc_lock(priv); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_enable(&priv->core_clk); + if (ret) + return ret; +#endif + + regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_BANDGAP_EN, + MESON_SAR_ADC_REG11_BANDGAP_EN); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_ADC_EN, + MESON_SAR_ADC_REG3_ADC_EN); + + udelay(5); + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_enable(&priv->adc_clk); + if (ret) + return ret; +#endif + + meson_saradc_unlock(priv); + + ret = meson_saradc_calib(priv); + if (ret) { + printf("calibration failed\n"); + return -EIO; + } + + return 0; +} + +static int meson_saradc_start_channel(struct udevice *dev, int channel) +{ + struct meson_saradc_priv *priv = dev_get_priv(dev); + + if (channel < 0 || channel >= NUM_CHANNELS) { + printf("Requested channel is invalid!"); + return -EINVAL; + } + + if (!priv->initialized) { + int ret; + + ret = meson_saradc_init(priv); + if (ret) + return ret; + + priv->initialized = true; + } + + priv->active_channel = channel; + + return 0; +} + +static int meson_saradc_stop(struct udevice *dev) +{ + struct meson_saradc_priv *priv = dev_get_priv(dev); + + priv->active_channel = -1; + + return 0; +} + +static int meson_saradc_probe(struct udevice *dev) +{ + struct meson_saradc_priv *priv = dev_get_priv(dev); + int ret; + + ret = regmap_init_mem(dev, &priv->regmap); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_get_by_name(dev, "core", &priv->core_clk); + if (ret) + return ret; + + ret = clk_get_by_name(dev, "adc_clk", &priv->adc_clk); + if (ret) + return ret; +#endif + + priv->active_channel = -1; + + return 0; +} + +int meson_saradc_ofdata_to_platdata(struct udevice *dev) +{ + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); + struct meson_saradc_priv *priv = dev_get_priv(dev); + + priv->data = (struct meson_saradc_data *)dev_get_driver_data(dev); + + uc_pdata->data_mask = GENMASK(priv->data->num_bits - 1, 0); + uc_pdata->data_format = ADC_DATA_FORMAT_BIN; + uc_pdata->data_timeout_us = MESON_SAR_ADC_TIMEOUT * 1000; + uc_pdata->channel_mask = GENMASK(NUM_CHANNELS - 1, 0); + + return 0; +} + +static const struct adc_ops meson_saradc_ops = { + .start_channel = meson_saradc_start_channel, + .channel_data = meson_saradc_channel_data, + .stop = meson_saradc_stop, +}; + +static const struct meson_saradc_data gxbb_saradc_data = { + .num_bits = 10, +}; + +static const struct meson_saradc_data gxl_saradc_data = { + .num_bits = 12, +}; + +static const struct udevice_id meson_saradc_ids[] = { + { .compatible = "amlogic,meson-gxbb-saradc", + .data = (ulong)&gxbb_saradc_data }, + { .compatible = "amlogic,meson-gxl-saradc", + .data = (ulong)&gxl_saradc_data }, + { .compatible = "amlogic,meson-gxm-saradc", + .data = (ulong)&gxl_saradc_data }, + { } +}; + +U_BOOT_DRIVER(meson_saradc) = { + .name = "meson_saradc", + .id = UCLASS_ADC, + .of_match = meson_saradc_ids, + .ops = &meson_saradc_ops, + .probe = meson_saradc_probe, + .ofdata_to_platdata = meson_saradc_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct meson_saradc_priv), +}; diff --git a/drivers/clk/clk_stm32f.c b/drivers/clk/clk_stm32f.c index 6c1e2575ff..cbcfe3a89d 100644 --- a/drivers/clk/clk_stm32f.c +++ b/drivers/clk/clk_stm32f.c @@ -133,6 +133,7 @@ struct stm32_clk { struct stm32_pwr_regs *pwr_regs; struct stm32_clk_info info; unsigned long hse_rate; + bool pllsaip; }; #ifdef CONFIG_VIDEO_STM32 @@ -179,8 +180,12 @@ static int configure_clocks(struct udevice *dev) /* configure SDMMC clock */ if (priv->info.v2) { /*stm32f7 case */ - /* select PLLQ as 48MHz clock source */ - clrbits_le32(®s->dckcfgr2, RCC_DCKCFGRX_CK48MSEL); + if (priv->pllsaip) + /* select PLLSAIP as 48MHz clock source */ + setbits_le32(®s->dckcfgr2, RCC_DCKCFGRX_CK48MSEL); + else + /* select PLLQ as 48MHz clock source */ + clrbits_le32(®s->dckcfgr2, RCC_DCKCFGRX_CK48MSEL); /* select 48MHz as SDMMC1 clock source */ clrbits_le32(®s->dckcfgr2, RCC_DCKCFGRX_SDMMC1SEL); @@ -188,17 +193,23 @@ static int configure_clocks(struct udevice *dev) /* select 48MHz as SDMMC2 clock source */ clrbits_le32(®s->dckcfgr2, RCC_DCKCFGR2_SDMMC2SEL); } else { /* stm32f4 case */ - /* select PLLQ as 48MHz clock source */ - clrbits_le32(®s->dckcfgr, RCC_DCKCFGRX_CK48MSEL); + if (priv->pllsaip) + /* select PLLSAIP as 48MHz clock source */ + setbits_le32(®s->dckcfgr, RCC_DCKCFGRX_CK48MSEL); + else + /* select PLLQ as 48MHz clock source */ + clrbits_le32(®s->dckcfgr, RCC_DCKCFGRX_CK48MSEL); /* select 48MHz as SDMMC1 clock source */ clrbits_le32(®s->dckcfgr, RCC_DCKCFGRX_SDMMC1SEL); } -#ifdef CONFIG_VIDEO_STM32 /* - * Configure the SAI PLL to generate LTDC pixel clock + * Configure the SAI PLL to generate LTDC pixel clock and + * 48 Mhz for SDMMC and USB */ + clrsetbits_le32(®s->pllsaicfgr, RCC_PLLSAICFGR_PLLSAIP_MASK, + RCC_PLLSAICFGR_PLLSAIP_4); clrsetbits_le32(®s->pllsaicfgr, RCC_PLLSAICFGR_PLLSAIR_MASK, RCC_PLLSAICFGR_PLLSAIR_3); clrsetbits_le32(®s->pllsaicfgr, RCC_PLLSAICFGR_PLLSAIN_MASK, @@ -206,18 +217,16 @@ static int configure_clocks(struct udevice *dev) clrsetbits_le32(®s->dckcfgr, RCC_DCKCFGR_PLLSAIDIVR_MASK, RCC_DCKCFGR_PLLSAIDIVR_2 << RCC_DCKCFGR_PLLSAIDIVR_SHIFT); -#endif + /* Enable the main PLL */ setbits_le32(®s->cr, RCC_CR_PLLON); while (!(readl(®s->cr) & RCC_CR_PLLRDY)) ; -#ifdef CONFIG_VIDEO_STM32 -/* Enable the SAI PLL */ + /* Enable the SAI PLL */ setbits_le32(®s->cr, RCC_CR_PLLSAION); while (!(readl(®s->cr) & RCC_CR_PLLSAIRDY)) ; -#endif setbits_le32(®s->apb1enr, RCC_APB1ENR_PWREN); if (priv->info.has_overdrive) { @@ -617,12 +626,17 @@ static int stm32_clk_probe(struct udevice *dev) return -EINVAL; priv->base = (struct stm32_rcc_regs *)addr; + priv->pllsaip = true; switch (dev_get_driver_data(dev)) { - case STM32F4: + case STM32F42X: + priv->pllsaip = false; + /* fallback into STM32F469 case */ + case STM32F469: memcpy(&priv->info, &stm32f4_clk_info, sizeof(struct stm32_clk_info)); break; + case STM32F7: memcpy(&priv->info, &stm32f7_clk_info, sizeof(struct stm32_clk_info)); diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c index ed32585eb0..1a77eba39c 100644 --- a/drivers/clk/clk_stm32mp1.c +++ b/drivers/clk/clk_stm32mp1.c @@ -100,6 +100,7 @@ #define RCC_USBCKSELR 0x91C #define RCC_MP_APB1ENSETR 0xA00 #define RCC_MP_APB2ENSETR 0XA08 +#define RCC_MP_APB3ENSETR 0xA10 #define RCC_MP_AHB2ENSETR 0xA18 #define RCC_MP_AHB4ENSETR 0xA28 @@ -508,6 +509,8 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = { STM32MP1_CLK_SET_CLR(RCC_MP_APB2ENSETR, 13, USART6_K, _UART6_SEL), + STM32MP1_CLK_SET_CLR_F(RCC_MP_APB3ENSETR, 13, VREF, _PCLK3), + STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 8, DDRPERFM, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 15, IWDG2, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 16, USBPHY_K, _USBPHY_SEL), diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index fabcc5f53a..8e5c3bcf61 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -132,3 +132,17 @@ int regmap_write(struct regmap *map, uint offset, uint val) return 0; } + +int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val) +{ + uint reg; + int ret; + + ret = regmap_read(map, offset, ®); + if (ret) + return ret; + + reg &= ~mask; + + return regmap_write(map, offset, reg | val); +} diff --git a/drivers/gpio/atmel_pio4.c b/drivers/gpio/atmel_pio4.c index 2385dec961..95a189a50f 100644 --- a/drivers/gpio/atmel_pio4.c +++ b/drivers/gpio/atmel_pio4.c @@ -43,7 +43,7 @@ static struct atmel_pio4_port *atmel_pio4_port_base(u32 port) } static int atmel_pio4_config_io_func(u32 port, u32 pin, - u32 func, u32 use_pullup) + u32 func, u32 config) { struct atmel_pio4_port *port_base; u32 reg, mask; @@ -57,7 +57,7 @@ static int atmel_pio4_config_io_func(u32 port, u32 pin, mask = 1 << pin; reg = func; - reg |= use_pullup ? ATMEL_PIO_PUEN_MASK : 0; + reg |= config; writel(mask, &port_base->mskr); writel(reg, &port_base->cfgr); @@ -65,60 +65,60 @@ static int atmel_pio4_config_io_func(u32 port, u32 pin, return 0; } -int atmel_pio4_set_gpio(u32 port, u32 pin, u32 use_pullup) +int atmel_pio4_set_gpio(u32 port, u32 pin, u32 config) { return atmel_pio4_config_io_func(port, pin, ATMEL_PIO_CFGR_FUNC_GPIO, - use_pullup); + config); } -int atmel_pio4_set_a_periph(u32 port, u32 pin, u32 use_pullup) +int atmel_pio4_set_a_periph(u32 port, u32 pin, u32 config) { return atmel_pio4_config_io_func(port, pin, ATMEL_PIO_CFGR_FUNC_PERIPH_A, - use_pullup); + config); } -int atmel_pio4_set_b_periph(u32 port, u32 pin, u32 use_pullup) +int atmel_pio4_set_b_periph(u32 port, u32 pin, u32 config) { return atmel_pio4_config_io_func(port, pin, ATMEL_PIO_CFGR_FUNC_PERIPH_B, - use_pullup); + config); } -int atmel_pio4_set_c_periph(u32 port, u32 pin, u32 use_pullup) +int atmel_pio4_set_c_periph(u32 port, u32 pin, u32 config) { return atmel_pio4_config_io_func(port, pin, ATMEL_PIO_CFGR_FUNC_PERIPH_C, - use_pullup); + config); } -int atmel_pio4_set_d_periph(u32 port, u32 pin, u32 use_pullup) +int atmel_pio4_set_d_periph(u32 port, u32 pin, u32 config) { return atmel_pio4_config_io_func(port, pin, ATMEL_PIO_CFGR_FUNC_PERIPH_D, - use_pullup); + config); } -int atmel_pio4_set_e_periph(u32 port, u32 pin, u32 use_pullup) +int atmel_pio4_set_e_periph(u32 port, u32 pin, u32 config) { return atmel_pio4_config_io_func(port, pin, ATMEL_PIO_CFGR_FUNC_PERIPH_E, - use_pullup); + config); } -int atmel_pio4_set_f_periph(u32 port, u32 pin, u32 use_pullup) +int atmel_pio4_set_f_periph(u32 port, u32 pin, u32 config) { return atmel_pio4_config_io_func(port, pin, ATMEL_PIO_CFGR_FUNC_PERIPH_F, - use_pullup); + config); } -int atmel_pio4_set_g_periph(u32 port, u32 pin, u32 use_pullup) +int atmel_pio4_set_g_periph(u32 port, u32 pin, u32 config) { return atmel_pio4_config_io_func(port, pin, ATMEL_PIO_CFGR_FUNC_PERIPH_G, - use_pullup); + config); } int atmel_pio4_set_pio_output(u32 port, u32 pin, u32 value) diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c index b26f7a730c..a36942b934 100644 --- a/drivers/led/led_gpio.c +++ b/drivers/led/led_gpio.c @@ -10,6 +10,7 @@ #include <led.h> #include <asm/gpio.h> #include <dm/lists.h> +#include <dm/uclass-internal.h> struct led_gpio_priv { struct gpio_desc gpio; @@ -57,11 +58,25 @@ static int led_gpio_probe(struct udevice *dev) { struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev); struct led_gpio_priv *priv = dev_get_priv(dev); + const char *default_state; + int ret; /* Ignore the top-level LED node */ if (!uc_plat->label) return 0; - return gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT); + + ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT); + if (ret) + return ret; + + default_state = dev_read_string(dev, "default-state"); + if (default_state) { + if (!strncmp(default_state, "on", 2)) + gpio_led_set_state(dev, LEDST_ON); + else if (!strncmp(default_state, "off", 3)) + gpio_led_set_state(dev, LEDST_OFF); + } + return 0; } static int led_gpio_remove(struct udevice *dev) @@ -103,6 +118,14 @@ static int led_gpio_bind(struct udevice *parent) return ret; uc_plat = dev_get_uclass_platdata(dev); uc_plat->label = label; + + if (ofnode_read_bool(node, "default-state")) { + struct udevice *devp; + + ret = uclass_get_device_tail(dev, 0, &devp); + if (ret) + return ret; + } } return 0; diff --git a/drivers/misc/stm32_rcc.c b/drivers/misc/stm32_rcc.c index b436900d7c..dee82c0b7b 100644 --- a/drivers/misc/stm32_rcc.c +++ b/drivers/misc/stm32_rcc.c @@ -11,9 +11,14 @@ #include <dm/device-internal.h> #include <dm/lists.h> -struct stm32_rcc_clk stm32_rcc_clk_f4 = { +struct stm32_rcc_clk stm32_rcc_clk_f42x = { .drv_name = "stm32fx_rcc_clock", - .soc = STM32F4, + .soc = STM32F42X, +}; + +struct stm32_rcc_clk stm32_rcc_clk_f469 = { + .drv_name = "stm32fx_rcc_clock", + .soc = STM32F469, }; struct stm32_rcc_clk stm32_rcc_clk_f7 = { @@ -61,7 +66,8 @@ static const struct misc_ops stm32_rcc_ops = { }; static const struct udevice_id stm32_rcc_ids[] = { - {.compatible = "st,stm32f42xx-rcc", .data = (ulong)&stm32_rcc_clk_f4 }, + {.compatible = "st,stm32f42xx-rcc", .data = (ulong)&stm32_rcc_clk_f42x }, + {.compatible = "st,stm32f469-rcc", .data = (ulong)&stm32_rcc_clk_f469 }, {.compatible = "st,stm32f746-rcc", .data = (ulong)&stm32_rcc_clk_f7 }, {.compatible = "st,stm32h743-rcc", .data = (ulong)&stm32_rcc_clk_h7 }, { } diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index c20a0cc060..f59803dbd6 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -105,4 +105,11 @@ config PCIE_LAYERSCAPE PCIe controllers. The PCIe may works in RC or EP mode according to RCW[HOST_AGT_PEX] setting. +config PCIE_INTEL_FPGA + bool "Intel FPGA PCIe support" + depends on DM_PCI + help + Say Y here if you want to enable PCIe controller support on Intel + FPGA, example Stratix 10. + endif diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 72c09f4af8..4923641895 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -33,3 +33,4 @@ obj-$(CONFIG_PCIE_DW_MVEBU) += pcie_dw_mvebu.o obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape.o obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape_fixup.o obj-$(CONFIG_PCI_XILINX) += pcie_xilinx.o +obj-$(CONFIG_PCIE_INTEL_FPGA) += pcie_intel_fpga.o diff --git a/drivers/pci/pcie_intel_fpga.c b/drivers/pci/pcie_intel_fpga.c new file mode 100644 index 0000000000..3cdf05b314 --- /dev/null +++ b/drivers/pci/pcie_intel_fpga.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel FPGA PCIe host controller driver + * + * Copyright (C) 2013-2018 Intel Corporation. All rights reserved + * + */ + +#include <common.h> +#include <dm.h> +#include <pci.h> +#include <asm/io.h> + +#define RP_TX_REG0 0x2000 +#define RP_TX_CNTRL 0x2004 +#define RP_TX_SOP BIT(0) +#define RP_TX_EOP BIT(1) +#define RP_RXCPL_STATUS 0x200C +#define RP_RXCPL_SOP BIT(0) +#define RP_RXCPL_EOP BIT(1) +#define RP_RXCPL_REG 0x2008 +#define P2A_INT_STATUS 0x3060 +#define P2A_INT_STS_ALL 0xf +#define P2A_INT_ENABLE 0x3070 +#define RP_CAP_OFFSET 0x70 + +/* TLP configuration type 0 and 1 */ +#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ +#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ +#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */ +#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */ +#define TLP_PAYLOAD_SIZE 0x01 +#define TLP_READ_TAG 0x1d +#define TLP_WRITE_TAG 0x10 +#define RP_DEVFN 0 + +#define RP_CFG_ADDR(pcie, reg) \ + ((pcie->hip_base) + (reg) + (1 << 20)) +#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) + +#define TLP_CFGRD_DW0(pcie, bus) \ + ((((bus != pcie->first_busno) ? TLP_FMTTYPE_CFGRD0 \ + : TLP_FMTTYPE_CFGRD1) << 24) | \ + TLP_PAYLOAD_SIZE) + +#define TLP_CFGWR_DW0(pcie, bus) \ + ((((bus != pcie->first_busno) ? TLP_FMTTYPE_CFGWR0 \ + : TLP_FMTTYPE_CFGWR1) << 24) | \ + TLP_PAYLOAD_SIZE) + +#define TLP_CFG_DW1(pcie, tag, be) \ + (((TLP_REQ_ID(pcie->first_busno, RP_DEVFN)) << 16) | (tag << 8) | (be)) +#define TLP_CFG_DW2(bus, dev, fn, offset) \ + (((bus) << 24) | ((dev) << 19) | ((fn) << 16) | (offset)) + +#define TLP_COMP_STATUS(s) (((s) >> 13) & 7) +#define TLP_BYTE_COUNT(s) (((s) >> 0) & 0xfff) +#define TLP_HDR_SIZE 3 +#define TLP_LOOP 500 +#define DWORD_MASK 3 + +#define IS_ROOT_PORT(pcie, bdf) \ + ((PCI_BUS(bdf) == pcie->first_busno) ? true : false) + +#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ + +/** + * struct intel_fpga_pcie - Intel FPGA PCIe controller state + * @bus: Pointer to the PCI bus + * @cra_base: The base address of CRA register space + * @hip_base: The base address of Rootport configuration space + * @first_busno: This driver supports multiple PCIe controllers. + * first_busno stores the bus number of the PCIe root-port + * number which may vary depending on the PCIe setup. + */ +struct intel_fpga_pcie { + struct udevice *bus; + void __iomem *cra_base; + void __iomem *hip_base; + int first_busno; +}; + +/** + * Intel FPGA PCIe port uses BAR0 of RC's configuration space as the + * translation from PCI bus to native BUS. Entire DDR region is mapped + * into PCIe space using these registers, so it can be reached by DMA from + * EP devices. + * The BAR0 of bridge should be hidden during enumeration to avoid the + * sizing and resource allocation by PCIe core. + */ +static bool intel_fpga_pcie_hide_rc_bar(struct intel_fpga_pcie *pcie, + pci_dev_t bdf, int offset) +{ + if (IS_ROOT_PORT(pcie, bdf) && PCI_DEV(bdf) == 0 && + PCI_FUNC(bdf) == 0 && offset == PCI_BASE_ADDRESS_0) + return true; + + return false; +} + +static inline void cra_writel(struct intel_fpga_pcie *pcie, const u32 value, + const u32 reg) +{ + writel(value, pcie->cra_base + reg); +} + +static inline u32 cra_readl(struct intel_fpga_pcie *pcie, const u32 reg) +{ + return readl(pcie->cra_base + reg); +} + +static bool intel_fpga_pcie_link_up(struct intel_fpga_pcie *pcie) +{ + return !!(readw(RP_CFG_ADDR(pcie, RP_CAP_OFFSET + PCI_EXP_LNKSTA)) + & PCI_EXP_LNKSTA_DLLLA); +} + +static bool intel_fpga_pcie_addr_valid(struct intel_fpga_pcie *pcie, + pci_dev_t bdf) +{ + /* If there is no link, then there is no device */ + if (!IS_ROOT_PORT(pcie, bdf) && !intel_fpga_pcie_link_up(pcie)) + return false; + + /* access only one slot on each root port */ + if (IS_ROOT_PORT(pcie, bdf) && PCI_DEV(bdf) > 0) + return false; + + if ((PCI_BUS(bdf) == pcie->first_busno + 1) && PCI_DEV(bdf) > 0) + return false; + + return true; +} + +static void tlp_write_tx(struct intel_fpga_pcie *pcie, u32 reg0, u32 ctrl) +{ + cra_writel(pcie, reg0, RP_TX_REG0); + cra_writel(pcie, ctrl, RP_TX_CNTRL); +} + +static int tlp_read_packet(struct intel_fpga_pcie *pcie, u32 *value) +{ + int i; + u32 ctrl; + u32 comp_status; + u32 dw[4]; + u32 count = 0; + + for (i = 0; i < TLP_LOOP; i++) { + ctrl = cra_readl(pcie, RP_RXCPL_STATUS); + if (!(ctrl & RP_RXCPL_SOP)) + continue; + + /* read first DW */ + dw[count++] = cra_readl(pcie, RP_RXCPL_REG); + + /* Poll for EOP */ + for (i = 0; i < TLP_LOOP; i++) { + ctrl = cra_readl(pcie, RP_RXCPL_STATUS); + dw[count++] = cra_readl(pcie, RP_RXCPL_REG); + if (ctrl & RP_RXCPL_EOP) { + comp_status = TLP_COMP_STATUS(dw[1]); + if (comp_status) + return -EFAULT; + + if (value && + TLP_BYTE_COUNT(dw[1]) == sizeof(u32) && + count >= 3) + *value = dw[3]; + + return 0; + } + } + + udelay(5); + } + + dev_err(pcie->dev, "read TLP packet timed out\n"); + return -ENODEV; +} + +static void tlp_write_packet(struct intel_fpga_pcie *pcie, u32 *headers, + u32 data) +{ + tlp_write_tx(pcie, headers[0], RP_TX_SOP); + + tlp_write_tx(pcie, headers[1], 0); + + tlp_write_tx(pcie, headers[2], 0); + + tlp_write_tx(pcie, data, RP_TX_EOP); +} + +static int tlp_cfg_dword_read(struct intel_fpga_pcie *pcie, pci_dev_t bdf, + int offset, u8 byte_en, u32 *value) +{ + u32 headers[TLP_HDR_SIZE]; + u8 busno = PCI_BUS(bdf); + + headers[0] = TLP_CFGRD_DW0(pcie, busno); + headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en); + headers[2] = TLP_CFG_DW2(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset); + + tlp_write_packet(pcie, headers, 0); + + return tlp_read_packet(pcie, value); +} + +static int tlp_cfg_dword_write(struct intel_fpga_pcie *pcie, pci_dev_t bdf, + int offset, u8 byte_en, u32 value) +{ + u32 headers[TLP_HDR_SIZE]; + u8 busno = PCI_BUS(bdf); + + headers[0] = TLP_CFGWR_DW0(pcie, busno); + headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en); + headers[2] = TLP_CFG_DW2(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset); + + tlp_write_packet(pcie, headers, value); + + return tlp_read_packet(pcie, NULL); +} + +int intel_fpga_rp_conf_addr(struct udevice *bus, pci_dev_t bdf, + uint offset, void **paddress) +{ + struct intel_fpga_pcie *pcie = dev_get_priv(bus); + + *paddress = RP_CFG_ADDR(pcie, offset); + + return 0; +} + +static int intel_fpga_pcie_rp_rd_conf(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + return pci_generic_mmap_read_config(bus, intel_fpga_rp_conf_addr, + bdf, offset, valuep, size); +} + +static int intel_fpga_pcie_rp_wr_conf(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + int ret; + struct intel_fpga_pcie *pcie = dev_get_priv(bus); + + ret = pci_generic_mmap_write_config(bus, intel_fpga_rp_conf_addr, + bdf, offset, value, size); + if (!ret) { + /* Monitor changes to PCI_PRIMARY_BUS register on root port + * and update local copy of root bus number accordingly. + */ + if (offset == PCI_PRIMARY_BUS) + pcie->first_busno = (u8)(value); + } + + return ret; +} + +static u8 pcie_get_byte_en(uint offset, enum pci_size_t size) +{ + switch (size) { + case PCI_SIZE_8: + return 1 << (offset & 3); + case PCI_SIZE_16: + return 3 << (offset & 3); + default: + return 0xf; + } +} + +static int _pcie_intel_fpga_read_config(struct intel_fpga_pcie *pcie, + pci_dev_t bdf, uint offset, + ulong *valuep, enum pci_size_t size) +{ + int ret; + u32 data; + u8 byte_en; + + /* Uses memory mapped method to read rootport config registers */ + if (IS_ROOT_PORT(pcie, bdf)) + return intel_fpga_pcie_rp_rd_conf(pcie->bus, bdf, + offset, valuep, size); + + byte_en = pcie_get_byte_en(offset, size); + ret = tlp_cfg_dword_read(pcie, bdf, offset & ~DWORD_MASK, + byte_en, &data); + if (ret) + return ret; + + dev_dbg(pcie->dev, "(addr,size,val)=(0x%04x, %d, 0x%08x)\n", + offset, size, data); + *valuep = pci_conv_32_to_size(data, offset, size); + + return 0; +} + +static int _pcie_intel_fpga_write_config(struct intel_fpga_pcie *pcie, + pci_dev_t bdf, uint offset, + ulong value, enum pci_size_t size) +{ + u32 data; + u8 byte_en; + + dev_dbg(pcie->dev, "PCIE CFG write: (b.d.f)=(%02d.%02d.%02d)\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); + dev_dbg(pcie->dev, "(addr,size,val)=(0x%04x, %d, 0x%08lx)\n", + offset, size, value); + + /* Uses memory mapped method to read rootport config registers */ + if (IS_ROOT_PORT(pcie, bdf)) + return intel_fpga_pcie_rp_wr_conf(pcie->bus, bdf, offset, + value, size); + + byte_en = pcie_get_byte_en(offset, size); + data = pci_conv_size_to_32(0, value, offset, size); + + return tlp_cfg_dword_write(pcie, bdf, offset & ~DWORD_MASK, + byte_en, data); +} + +static int pcie_intel_fpga_read_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + struct intel_fpga_pcie *pcie = dev_get_priv(bus); + + dev_dbg(pcie->dev, "PCIE CFG read: (b.d.f)=(%02d.%02d.%02d)\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); + + if (intel_fpga_pcie_hide_rc_bar(pcie, bdf, offset)) { + *valuep = (u32)pci_get_ff(size); + return 0; + } + + if (!intel_fpga_pcie_addr_valid(pcie, bdf)) { + *valuep = (u32)pci_get_ff(size); + return 0; + } + + return _pcie_intel_fpga_read_config(pcie, bdf, offset, valuep, size); +} + +static int pcie_intel_fpga_write_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + struct intel_fpga_pcie *pcie = dev_get_priv(bus); + + if (intel_fpga_pcie_hide_rc_bar(pcie, bdf, offset)) + return 0; + + if (!intel_fpga_pcie_addr_valid(pcie, bdf)) + return 0; + + return _pcie_intel_fpga_write_config(pcie, bdf, offset, value, + size); +} + +static int pcie_intel_fpga_probe(struct udevice *dev) +{ + struct intel_fpga_pcie *pcie = dev_get_priv(dev); + + pcie->bus = pci_get_controller(dev); + pcie->first_busno = dev->seq; + + /* clear all interrupts */ + cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS); + /* disable all interrupts */ + cra_writel(pcie, 0, P2A_INT_ENABLE); + + return 0; +} + +static int pcie_intel_fpga_ofdata_to_platdata(struct udevice *dev) +{ + struct intel_fpga_pcie *pcie = dev_get_priv(dev); + struct fdt_resource reg_res; + int node = dev_of_offset(dev); + int ret; + + DECLARE_GLOBAL_DATA_PTR; + + ret = fdt_get_named_resource(gd->fdt_blob, node, "reg", "reg-names", + "Cra", ®_res); + if (ret) { + dev_err(dev, "resource \"Cra\" not found\n"); + return ret; + } + + pcie->cra_base = map_physmem(reg_res.start, + fdt_resource_size(®_res), + MAP_NOCACHE); + + ret = fdt_get_named_resource(gd->fdt_blob, node, "reg", "reg-names", + "Hip", ®_res); + if (ret) { + dev_err(dev, "resource \"Hip\" not found\n"); + return ret; + } + + pcie->hip_base = map_physmem(reg_res.start, + fdt_resource_size(®_res), + MAP_NOCACHE); + + return 0; +} + +static const struct dm_pci_ops pcie_intel_fpga_ops = { + .read_config = pcie_intel_fpga_read_config, + .write_config = pcie_intel_fpga_write_config, +}; + +static const struct udevice_id pcie_intel_fpga_ids[] = { + { .compatible = "altr,pcie-root-port-2.0" }, + {}, +}; + +U_BOOT_DRIVER(pcie_intel_fpga) = { + .name = "pcie_intel_fpga", + .id = UCLASS_PCI, + .of_match = pcie_intel_fpga_ids, + .ops = &pcie_intel_fpga_ops, + .ofdata_to_platdata = pcie_intel_fpga_ofdata_to_platdata, + .probe = pcie_intel_fpga_probe, + .priv_auto_alloc_size = sizeof(struct intel_fpga_pcie), +}; diff --git a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c index 6e94d3bc28..a8e47e3c4e 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c +++ b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c @@ -13,7 +13,7 @@ #include "pinctrl-meson.h" -#define EE_OFF 14 +#define EE_OFF 15 static const unsigned int emmc_nand_d07_pins[] = { PIN(BOOT_0, EE_OFF), PIN(BOOT_1, EE_OFF), PIN(BOOT_2, EE_OFF), @@ -318,8 +318,6 @@ static const char * const gpio_periphs_groups[] = { "GPIOX_10", "GPIOX_11", "GPIOX_12", "GPIOX_13", "GPIOX_14", "GPIOX_15", "GPIOX_16", "GPIOX_17", "GPIOX_18", "GPIOX_19", "GPIOX_20", "GPIOX_21", "GPIOX_22", - - "GPIO_TEST_N", }; static const char * const emmc_groups[] = { @@ -354,6 +352,8 @@ static const char * const gpio_aobus_groups[] = { "GPIOAO_0", "GPIOAO_1", "GPIOAO_2", "GPIOAO_3", "GPIOAO_4", "GPIOAO_5", "GPIOAO_6", "GPIOAO_7", "GPIOAO_8", "GPIOAO_9", "GPIOAO_10", "GPIOAO_11", "GPIOAO_12", "GPIOAO_13", + + "GPIO_TEST_N", }; static const char * const uart_ao_groups[] = { @@ -409,11 +409,11 @@ static struct meson_bank meson_gxbb_aobus_banks[] = { struct meson_pinctrl_data meson_gxbb_periphs_pinctrl_data = { .name = "periphs-banks", - .pin_base = 14, + .pin_base = 15, .groups = meson_gxbb_periphs_groups, .funcs = meson_gxbb_periphs_functions, .banks = meson_gxbb_periphs_banks, - .num_pins = 120, + .num_pins = 119, .num_groups = ARRAY_SIZE(meson_gxbb_periphs_groups), .num_funcs = ARRAY_SIZE(meson_gxbb_periphs_functions), .num_banks = ARRAY_SIZE(meson_gxbb_periphs_banks), @@ -425,7 +425,7 @@ struct meson_pinctrl_data meson_gxbb_aobus_pinctrl_data = { .groups = meson_gxbb_aobus_groups, .funcs = meson_gxbb_aobus_functions, .banks = meson_gxbb_aobus_banks, - .num_pins = 14, + .num_pins = 15, .num_groups = ARRAY_SIZE(meson_gxbb_aobus_groups), .num_funcs = ARRAY_SIZE(meson_gxbb_aobus_functions), .num_banks = ARRAY_SIZE(meson_gxbb_aobus_banks), diff --git a/drivers/pinctrl/meson/pinctrl-meson-gxl.c b/drivers/pinctrl/meson/pinctrl-meson-gxl.c index fd60bc611d..ba6e3531d9 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-gxl.c +++ b/drivers/pinctrl/meson/pinctrl-meson-gxl.c @@ -13,7 +13,7 @@ #include "pinctrl-meson.h" -#define EE_OFF 10 +#define EE_OFF 11 static const unsigned int emmc_nand_d07_pins[] = { PIN(BOOT_0, EE_OFF), PIN(BOOT_1, EE_OFF), PIN(BOOT_2, EE_OFF), @@ -289,7 +289,7 @@ static struct meson_pmx_group meson_gxl_periphs_groups[] = { GPIO_GROUP(GPIOCLK_0, EE_OFF), GPIO_GROUP(GPIOCLK_1, EE_OFF), - GPIO_GROUP(GPIO_TEST_N, EE_OFF), + GPIO_GROUP(GPIO_TEST_N, 0), /* Bank X */ GROUP(sdio_d0, 5, 31), @@ -471,8 +471,6 @@ static const char * const gpio_periphs_groups[] = { "GPIOX_5", "GPIOX_6", "GPIOX_7", "GPIOX_8", "GPIOX_9", "GPIOX_10", "GPIOX_11", "GPIOX_12", "GPIOX_13", "GPIOX_14", "GPIOX_15", "GPIOX_16", "GPIOX_17", "GPIOX_18", - - "GPIO_TEST_N", }; static const char * const emmc_groups[] = { @@ -587,6 +585,8 @@ static const char * const tsin_a_groups[] = { static const char * const gpio_aobus_groups[] = { "GPIOAO_0", "GPIOAO_1", "GPIOAO_2", "GPIOAO_3", "GPIOAO_4", "GPIOAO_5", "GPIOAO_6", "GPIOAO_7", "GPIOAO_8", "GPIOAO_9", + + "GPIO_TEST_N", }; static const char * const uart_ao_groups[] = { @@ -691,11 +691,11 @@ static struct meson_bank meson_gxl_aobus_banks[] = { struct meson_pinctrl_data meson_gxl_periphs_pinctrl_data = { .name = "periphs-banks", - .pin_base = 10, + .pin_base = 11, .groups = meson_gxl_periphs_groups, .funcs = meson_gxl_periphs_functions, .banks = meson_gxl_periphs_banks, - .num_pins = 101, + .num_pins = 100, .num_groups = ARRAY_SIZE(meson_gxl_periphs_groups), .num_funcs = ARRAY_SIZE(meson_gxl_periphs_functions), .num_banks = ARRAY_SIZE(meson_gxl_periphs_banks), @@ -707,7 +707,7 @@ struct meson_pinctrl_data meson_gxl_aobus_pinctrl_data = { .groups = meson_gxl_aobus_groups, .funcs = meson_gxl_aobus_functions, .banks = meson_gxl_aobus_banks, - .num_pins = 10, + .num_pins = 11, .num_groups = ARRAY_SIZE(meson_gxl_aobus_groups), .num_funcs = ARRAY_SIZE(meson_gxl_aobus_functions), .num_banks = ARRAY_SIZE(meson_gxl_aobus_banks), diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c index 7b427d06ec..a5935e84de 100644 --- a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c @@ -16,11 +16,42 @@ #define UNIPHIER_PINCTRL_PINMUX_BASE 0x1000 #define UNIPHIER_PINCTRL_LOAD_PINMUX 0x1700 +#define UNIPHIER_PINCTRL_DRVCTRL_BASE 0x1800 +#define UNIPHIER_PINCTRL_DRV2CTRL_BASE 0x1900 +#define UNIPHIER_PINCTRL_DRV3CTRL_BASE 0x1980 #define UNIPHIER_PINCTRL_PUPDCTRL_BASE 0x1a00 #define UNIPHIER_PINCTRL_IECTRL 0x1d00 static const char *uniphier_pinctrl_dummy_name = "_dummy"; +static int uniphier_pinctrl_get_pins_count(struct udevice *dev) +{ + struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); + const struct uniphier_pinctrl_pin *pins = priv->socdata->pins; + int pins_count = priv->socdata->pins_count; + + /* + * We do not list all pins in the pin table to save memory footprint. + * Report the max pin number + 1 to fake the framework. + */ + return pins[pins_count - 1].number + 1; +} + +static const char *uniphier_pinctrl_get_pin_name(struct udevice *dev, + unsigned int selector) +{ + struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); + const struct uniphier_pinctrl_pin *pins = priv->socdata->pins; + int pins_count = priv->socdata->pins_count; + int i; + + for (i = 0; i < pins_count; i++) + if (pins[i].number == selector) + return pins[i].name; + + return uniphier_pinctrl_dummy_name; +} + static int uniphier_pinctrl_get_groups_count(struct udevice *dev) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); @@ -113,10 +144,25 @@ static const struct pinconf_param uniphier_pinconf_params[] = { { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, + { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 }, }; +static const struct uniphier_pinctrl_pin * +uniphier_pinctrl_pin_get(struct uniphier_pinctrl_priv *priv, unsigned int pin) +{ + const struct uniphier_pinctrl_pin *pins = priv->socdata->pins; + int pins_count = priv->socdata->pins_count; + int i; + + for (i = 0; i < pins_count; i++) + if (pins[i].number == pin) + return &pins[i]; + + return NULL; +} + static int uniphier_pinconf_bias_set(struct udevice *dev, unsigned int pin, unsigned int param, unsigned int arg) { @@ -157,8 +203,88 @@ static int uniphier_pinconf_bias_set(struct udevice *dev, unsigned int pin, return 0; } -static int uniphier_pinconf_set_one(struct udevice *dev, unsigned int pin, - unsigned int param, unsigned int arg) +static const unsigned int uniphier_pinconf_drv_strengths_1bit[] = { + 4, 8, +}; + +static const unsigned int uniphier_pinconf_drv_strengths_2bit[] = { + 8, 12, 16, 20, +}; + +static const unsigned int uniphier_pinconf_drv_strengths_3bit[] = { + 4, 5, 7, 9, 11, 12, 14, 16, +}; + +static int uniphier_pinconf_drive_set(struct udevice *dev, unsigned int pin, + unsigned int strength) +{ + struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); + const struct uniphier_pinctrl_pin *desc; + const unsigned int *strengths; + unsigned int base, stride, width, drvctrl, reg, shift; + u32 val, mask, tmp; + + desc = uniphier_pinctrl_pin_get(priv, pin); + if (WARN_ON(!desc)) + return -EINVAL; + + switch (uniphier_pin_get_drv_type(desc->data)) { + case UNIPHIER_PIN_DRV_1BIT: + strengths = uniphier_pinconf_drv_strengths_1bit; + base = UNIPHIER_PINCTRL_DRVCTRL_BASE; + stride = 1; + width = 1; + break; + case UNIPHIER_PIN_DRV_2BIT: + strengths = uniphier_pinconf_drv_strengths_2bit; + base = UNIPHIER_PINCTRL_DRV2CTRL_BASE; + stride = 2; + width = 2; + break; + case UNIPHIER_PIN_DRV_3BIT: + strengths = uniphier_pinconf_drv_strengths_3bit; + base = UNIPHIER_PINCTRL_DRV3CTRL_BASE; + stride = 4; + width = 3; + break; + default: + /* drive strength control is not supported for this pin */ + return -EINVAL; + } + + drvctrl = uniphier_pin_get_drvctrl(desc->data); + drvctrl *= stride; + + reg = base + drvctrl / 32 * 4; + shift = drvctrl % 32; + mask = (1U << width) - 1; + + for (val = 0; val <= mask; val++) { + if (strengths[val] > strength) + break; + } + + if (val == 0) { + dev_err(dev, "unsupported drive strength %u mA for pin %s\n", + strength, desc->name); + return -EINVAL; + } + + if (!mask) + return 0; + + val--; + + tmp = readl(priv->base + reg); + tmp &= ~(mask << shift); + tmp |= (mask & val) << shift; + writel(tmp, priv->base + reg); + + return 0; +} + +static int uniphier_pinconf_set(struct udevice *dev, unsigned int pin, + unsigned int param, unsigned int arg) { int ret; @@ -169,11 +295,14 @@ static int uniphier_pinconf_set_one(struct udevice *dev, unsigned int pin, case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: ret = uniphier_pinconf_bias_set(dev, pin, param, arg); break; + case PIN_CONFIG_DRIVE_STRENGTH: + ret = uniphier_pinconf_drive_set(dev, pin, arg); + break; case PIN_CONFIG_INPUT_ENABLE: ret = uniphier_pinconf_input_enable(dev, pin, arg); break; default: - printf("unsupported configuration parameter %u\n", param); + dev_err(dev, "unsupported configuration parameter %u\n", param); return -EINVAL; } @@ -190,7 +319,7 @@ static int uniphier_pinconf_group_set(struct udevice *dev, int i, ret; for (i = 0; i < grp->num_pins; i++) { - ret = uniphier_pinconf_set_one(dev, grp->pins[i], param, arg); + ret = uniphier_pinconf_set(dev, grp->pins[i], param, arg); if (ret) return ret; } @@ -268,6 +397,8 @@ static int uniphier_pinmux_group_set(struct udevice *dev, } const struct pinctrl_ops uniphier_pinctrl_ops = { + .get_pins_count = uniphier_pinctrl_get_pins_count, + .get_pin_name = uniphier_pinctrl_get_pin_name, .get_groups_count = uniphier_pinctrl_get_groups_count, .get_group_name = uniphier_pinctrl_get_group_name, .get_functions_count = uniphier_pinmux_get_functions_count, @@ -276,6 +407,7 @@ const struct pinctrl_ops uniphier_pinctrl_ops = { #if CONFIG_IS_ENABLED(PINCONF) .pinconf_num_params = ARRAY_SIZE(uniphier_pinconf_params), .pinconf_params = uniphier_pinconf_params, + .pinconf_set = uniphier_pinconf_set, .pinconf_group_set = uniphier_pinconf_group_set, #endif .set_state = pinctrl_generic_set_state, diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c index ecf4355000..8ec87f285e 100644 --- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c @@ -10,6 +10,15 @@ #include "pinctrl-uniphier.h" +static const struct uniphier_pinctrl_pin uniphier_ld20_pins[] = { + UNIPHIER_PINCTRL_PIN(40, "RGMII_TXCLK", 28, UNIPHIER_PIN_DRV_3BIT), + UNIPHIER_PINCTRL_PIN(41, "RGMII_TXD0", 29, UNIPHIER_PIN_DRV_3BIT), + UNIPHIER_PINCTRL_PIN(42, "RGMII_TXD1", 30, UNIPHIER_PIN_DRV_3BIT), + UNIPHIER_PINCTRL_PIN(43, "RGMII_TXD2", 31, UNIPHIER_PIN_DRV_3BIT), + UNIPHIER_PINCTRL_PIN(44, "RGMII_TXD3", 32, UNIPHIER_PIN_DRV_3BIT), + UNIPHIER_PINCTRL_PIN(45, "RGMII_TXCTL", 33, UNIPHIER_PIN_DRV_3BIT), +}; + static const unsigned emmc_pins[] = {18, 19, 20, 21, 22, 23, 24, 25}; static const int emmc_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0}; static const unsigned emmc_dat8_pins[] = {26, 27, 28, 29}; @@ -102,6 +111,8 @@ static const char * const uniphier_ld20_functions[] = { }; static struct uniphier_pinctrl_socdata uniphier_ld20_pinctrl_socdata = { + .pins = uniphier_ld20_pins, + .pins_count = ARRAY_SIZE(uniphier_ld20_pins), .groups = uniphier_ld20_groups, .groups_count = ARRAY_SIZE(uniphier_ld20_groups), .functions = uniphier_ld20_functions, diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c index b9076678b2..30d411694c 100644 --- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c @@ -10,17 +10,6 @@ #include "pinctrl-uniphier.h" -static const struct uniphier_pinctrl_pin uniphier_ld6b_pins[] = { - UNIPHIER_PINCTRL_PIN(113, 0), - UNIPHIER_PINCTRL_PIN(114, 0), - UNIPHIER_PINCTRL_PIN(115, 0), - UNIPHIER_PINCTRL_PIN(116, 0), - UNIPHIER_PINCTRL_PIN(217, 0), - UNIPHIER_PINCTRL_PIN(218, 0), - UNIPHIER_PINCTRL_PIN(219, 0), - UNIPHIER_PINCTRL_PIN(220, 0), -}; - static const unsigned emmc_pins[] = {36, 37, 38, 39, 40, 41, 42}; static const int emmc_muxvals[] = {1, 1, 1, 1, 1, 1, 1}; static const unsigned emmc_dat8_pins[] = {43, 44, 45, 46}; @@ -134,8 +123,6 @@ static const char * const uniphier_ld6b_functions[] = { }; static struct uniphier_pinctrl_socdata uniphier_ld6b_pinctrl_socdata = { - .pins = uniphier_ld6b_pins, - .pins_count = ARRAY_SIZE(uniphier_ld6b_pins), .groups = uniphier_ld6b_groups, .groups_count = ARRAY_SIZE(uniphier_ld6b_groups), .functions = uniphier_ld6b_functions, diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier.h b/drivers/pinctrl/uniphier/pinctrl-uniphier.h index 6557f6a6c7..8f83ecae7d 100644 --- a/drivers/pinctrl/uniphier/pinctrl-uniphier.h +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier.h @@ -8,15 +8,48 @@ #define __PINCTRL_UNIPHIER_H__ #include <linux/bitops.h> -#include <linux/bug.h> +#include <linux/build_bug.h> #include <linux/kernel.h> #include <linux/types.h> -#define UNIPHIER_PIN_ATTR_PACKED(iectrl) (iectrl) +/* drive strength control register number */ +#define UNIPHIER_PIN_DRVCTRL_SHIFT 0 +#define UNIPHIER_PIN_DRVCTRL_BITS 9 +#define UNIPHIER_PIN_DRVCTRL_MASK ((1U << (UNIPHIER_PIN_DRVCTRL_BITS)) \ + - 1) + +/* drive control type */ +#define UNIPHIER_PIN_DRV_TYPE_SHIFT ((UNIPHIER_PIN_DRVCTRL_SHIFT) + \ + (UNIPHIER_PIN_DRVCTRL_BITS)) +#define UNIPHIER_PIN_DRV_TYPE_BITS 2 +#define UNIPHIER_PIN_DRV_TYPE_MASK ((1U << (UNIPHIER_PIN_DRV_TYPE_BITS)) \ + - 1) + +/* drive control type */ +enum uniphier_pin_drv_type { + UNIPHIER_PIN_DRV_1BIT, /* 2 level control: 4/8 mA */ + UNIPHIER_PIN_DRV_2BIT, /* 4 level control: 8/12/16/20 mA */ + UNIPHIER_PIN_DRV_3BIT, /* 8 level control: 4/5/7/9/11/12/14/16 mA */ +}; + +#define UNIPHIER_PIN_DRVCTRL(x) \ + (((x) & (UNIPHIER_PIN_DRVCTRL_MASK)) << (UNIPHIER_PIN_DRVCTRL_SHIFT)) +#define UNIPHIER_PIN_DRV_TYPE(x) \ + (((x) & (UNIPHIER_PIN_DRV_TYPE_MASK)) << (UNIPHIER_PIN_DRV_TYPE_SHIFT)) -static inline unsigned int uniphier_pin_get_iectrl(unsigned long data) +#define UNIPHIER_PIN_ATTR_PACKED(drvctrl, drv_type) \ + UNIPHIER_PIN_DRVCTRL(drvctrl) | \ + UNIPHIER_PIN_DRV_TYPE(drv_type) + +static inline unsigned int uniphier_pin_get_drvctrl(unsigned int data) { - return data; + return (data >> UNIPHIER_PIN_DRVCTRL_SHIFT) & UNIPHIER_PIN_DRVCTRL_MASK; +} + +static inline unsigned int uniphier_pin_get_drv_type(unsigned int data) +{ + return (data >> UNIPHIER_PIN_DRV_TYPE_SHIFT) & + UNIPHIER_PIN_DRV_TYPE_MASK; } /** @@ -27,7 +60,8 @@ static inline unsigned int uniphier_pin_get_iectrl(unsigned long data) */ struct uniphier_pinctrl_pin { unsigned number; - unsigned long data; + const char *name; + unsigned int data; }; /** @@ -72,10 +106,11 @@ struct uniphier_pinctrl_socdata { #define UNIPHIER_PINCTRL_CAPS_MUX_4BIT BIT(0) }; -#define UNIPHIER_PINCTRL_PIN(a, b) \ +#define UNIPHIER_PINCTRL_PIN(a, b, c, d) \ { \ .number = a, \ - .data = UNIPHIER_PIN_ATTR_PACKED(b), \ + .name = b, \ + .data = UNIPHIER_PIN_ATTR_PACKED(c, d), \ } #define __UNIPHIER_PINCTRL_GROUP(grp) \ diff --git a/drivers/power/pmic/stpmu1.c b/drivers/power/pmic/stpmu1.c index a765c4f2e9..82351b6613 100644 --- a/drivers/power/pmic/stpmu1.c +++ b/drivers/power/pmic/stpmu1.c @@ -12,6 +12,17 @@ #define STMPU1_NUM_OF_REGS 0x100 +#ifndef CONFIG_SPL_BUILD +static const struct pmic_child_info stpmu1_children_info[] = { + { .prefix = "ldo", .driver = "stpmu1_ldo" }, + { .prefix = "buck", .driver = "stpmu1_buck" }, + { .prefix = "vref_ddr", .driver = "stpmu1_vref_ddr" }, + { .prefix = "pwr_sw", .driver = "stpmu1_pwr_sw" }, + { .prefix = "boost", .driver = "stpmu1_boost" }, + { }, +}; +#endif /* CONFIG_SPL_BUILD */ + static int stpmu1_reg_count(struct udevice *dev) { return STMPU1_NUM_OF_REGS; @@ -42,6 +53,28 @@ static int stpmu1_read(struct udevice *dev, uint reg, uint8_t *buff, int len) return ret; } +static int stpmu1_bind(struct udevice *dev) +{ +#ifndef CONFIG_SPL_BUILD + ofnode regulators_node; + int children; + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + dev_dbg(dev, "regulators subnode not found!"); + return -ENXIO; + } + dev_dbg(dev, "found regulators subnode\n"); + + children = pmic_bind_children(dev, regulators_node, + stpmu1_children_info); + if (!children) + dev_dbg(dev, "no child found\n"); +#endif /* CONFIG_SPL_BUILD */ + + return 0; +} + static struct dm_pmic_ops stpmu1_ops = { .reg_count = stpmu1_reg_count, .read = stpmu1_read, @@ -57,5 +90,6 @@ U_BOOT_DRIVER(pmic_stpmu1) = { .name = "stpmu1_pmic", .id = UCLASS_PMIC, .of_match = stpmu1_ids, + .bind = stpmu1_bind, .ops = &stpmu1_ops, }; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 5b4ac10462..414f4a53f7 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -197,6 +197,15 @@ config DM_REGULATOR_LP87565 be configured in multi phase modes. The driver implements get/set api for value and enable. +config DM_REGULATOR_STM32_VREFBUF + bool "Enable driver for STMicroelectronics STM32 VREFBUF" + depends on DM_REGULATOR && (STM32H7 || ARCH_STM32MP) + help + This driver supports STMicroelectronics STM32 VREFBUF (voltage + reference buffer) which can be used as voltage reference for + internal ADCs, DACs and also for external components through + dedicated Vref+ pin. + config DM_REGULATOR_TPS65910 bool "Enable driver for TPS65910 PMIC regulators" depends on DM_PMIC_TPS65910 @@ -204,3 +213,12 @@ config DM_REGULATOR_TPS65910 The TPS65910 PMIC provides 4 SMPSs and 8 LDOs. This driver supports all regulator types of the TPS65910 (BUCK, BOOST and LDO). It implements the get/set api for value and enable. + +config DM_REGULATOR_STPMU1 + bool "Enable driver for STPMU1 regulators" + depends on DM_REGULATOR && PMIC_STPMU1 + ---help--- + Enable support for the regulator functions of the STPMU1 PMIC. The + driver implements get/set api for the various BUCKS and LDOs supported + by the PMIC device. This driver is controlled by a device tree node + which includes voltage limits. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index f7873ad27a..16208af069 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -22,4 +22,6 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_PALMAS) += palmas_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_PBIAS) += pbias_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMU1) += stpmu1.o diff --git a/drivers/power/regulator/stm32-vrefbuf.c b/drivers/power/regulator/stm32-vrefbuf.c new file mode 100644 index 0000000000..0ad6833ed0 --- /dev/null +++ b/drivers/power/regulator/stm32-vrefbuf.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Author: Fabrice Gasnier <fabrice.gasnier@st.com> + * + * Originally based on the Linux kernel v4.16 drivers/regulator/stm32-vrefbuf.c + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <asm/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <power/regulator.h> + +/* STM32 VREFBUF registers */ +#define STM32_VREFBUF_CSR 0x00 + +/* STM32 VREFBUF CSR bitfields */ +#define STM32_VRS GENMASK(6, 4) +#define STM32_VRS_SHIFT 4 +#define STM32_VRR BIT(3) +#define STM32_HIZ BIT(1) +#define STM32_ENVR BIT(0) + +struct stm32_vrefbuf { + void __iomem *base; + struct clk clk; + struct udevice *vdda_supply; +}; + +static const unsigned int stm32_vrefbuf_voltages[] = { + /* Matches resp. VRS = 000b, 001b, 010b, 011b */ + 2500000, 2048000, 1800000, 1500000, +}; + +static int stm32_vrefbuf_set_enable(struct udevice *dev, bool enable) +{ + struct stm32_vrefbuf *priv = dev_get_priv(dev); + u32 val; + int ret; + + clrsetbits_le32(priv->base + STM32_VREFBUF_CSR, STM32_HIZ | STM32_ENVR, + enable ? STM32_ENVR : STM32_HIZ); + if (!enable) + return 0; + + /* + * Vrefbuf startup time depends on external capacitor: wait here for + * VRR to be set. That means output has reached expected value. + * ~650us sleep should be enough for caps up to 1.5uF. Use 10ms as + * arbitrary timeout. + */ + ret = readl_poll_timeout(priv->base + STM32_VREFBUF_CSR, val, + val & STM32_VRR, 10000); + if (ret < 0) { + dev_err(dev, "stm32 vrefbuf timed out: %d\n", ret); + clrsetbits_le32(priv->base + STM32_VREFBUF_CSR, STM32_ENVR, + STM32_HIZ); + return ret; + } + + return 0; +} + +static int stm32_vrefbuf_get_enable(struct udevice *dev) +{ + struct stm32_vrefbuf *priv = dev_get_priv(dev); + + return readl(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR; +} + +static int stm32_vrefbuf_set_value(struct udevice *dev, int uV) +{ + struct stm32_vrefbuf *priv = dev_get_priv(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(stm32_vrefbuf_voltages); i++) { + if (uV == stm32_vrefbuf_voltages[i]) { + clrsetbits_le32(priv->base + STM32_VREFBUF_CSR, + STM32_VRS, i << STM32_VRS_SHIFT); + return 0; + } + } + + return -EINVAL; +} + +static int stm32_vrefbuf_get_value(struct udevice *dev) +{ + struct stm32_vrefbuf *priv = dev_get_priv(dev); + u32 val; + + val = readl(priv->base + STM32_VREFBUF_CSR) & STM32_VRS; + val >>= STM32_VRS_SHIFT; + + return stm32_vrefbuf_voltages[val]; +} + +static const struct dm_regulator_ops stm32_vrefbuf_ops = { + .get_value = stm32_vrefbuf_get_value, + .set_value = stm32_vrefbuf_set_value, + .get_enable = stm32_vrefbuf_get_enable, + .set_enable = stm32_vrefbuf_set_enable, +}; + +static int stm32_vrefbuf_probe(struct udevice *dev) +{ + struct stm32_vrefbuf *priv = dev_get_priv(dev); + int ret; + + priv->base = dev_read_addr_ptr(dev); + + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret) { + dev_err(dev, "Can't get clock: %d\n", ret); + return ret; + } + + ret = clk_enable(&priv->clk); + if (ret) { + dev_err(dev, "Can't enable clock: %d\n", ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vdda-supply", + &priv->vdda_supply); + if (ret) { + dev_dbg(dev, "No vdda-supply: %d\n", ret); + return 0; + } + + ret = regulator_set_enable(priv->vdda_supply, true); + if (ret) { + dev_err(dev, "Can't enable vdda-supply: %d\n", ret); + clk_disable(&priv->clk); + } + + return ret; +} + +static const struct udevice_id stm32_vrefbuf_ids[] = { + { .compatible = "st,stm32-vrefbuf" }, + { } +}; + +U_BOOT_DRIVER(stm32_vrefbuf) = { + .name = "stm32-vrefbuf", + .id = UCLASS_REGULATOR, + .of_match = stm32_vrefbuf_ids, + .probe = stm32_vrefbuf_probe, + .ops = &stm32_vrefbuf_ops, + .priv_auto_alloc_size = sizeof(struct stm32_vrefbuf), +}; diff --git a/drivers/power/regulator/stpmu1.c b/drivers/power/regulator/stpmu1.c new file mode 100644 index 0000000000..2dedb80acc --- /dev/null +++ b/drivers/power/regulator/stpmu1.c @@ -0,0 +1,667 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Author: Christophe Kerello <christophe.kerello@st.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/stpmu1.h> + +struct stpmu1_range { + int min_uv; + int min_sel; + int max_sel; + int step; +}; + +struct stpmu1_output_range { + const struct stpmu1_range *ranges; + int nbranges; +}; + +#define STPMU1_MODE(_id, _val, _name) { \ + .id = _id, \ + .register_value = _val, \ + .name = _name, \ +} + +#define STPMU1_RANGE(_min_uv, _min_sel, _max_sel, _step) { \ + .min_uv = _min_uv, \ + .min_sel = _min_sel, \ + .max_sel = _max_sel, \ + .step = _step, \ +} + +#define STPMU1_OUTPUT_RANGE(_ranges, _nbranges) { \ + .ranges = _ranges, \ + .nbranges = _nbranges, \ +} + +static int stpmu1_output_find_uv(int sel, + const struct stpmu1_output_range *output_range) +{ + const struct stpmu1_range *range; + int i; + + for (i = 0, range = output_range->ranges; + i < output_range->nbranges; i++, range++) { + if (sel >= range->min_sel && sel <= range->max_sel) + return range->min_uv + + (sel - range->min_sel) * range->step; + } + + return -EINVAL; +} + +static int stpmu1_output_find_sel(int uv, + const struct stpmu1_output_range *output_range) +{ + const struct stpmu1_range *range; + int i; + + for (i = 0, range = output_range->ranges; + i < output_range->nbranges; i++, range++) { + if (uv == range->min_uv && !range->step) + return range->min_sel; + + if (uv >= range->min_uv && + uv <= range->min_uv + + (range->max_sel - range->min_sel) * range->step) + return range->min_sel + + (uv - range->min_uv) / range->step; + } + + return -EINVAL; +} + +/* + * BUCK regulators + */ + +static const struct stpmu1_range buck1_ranges[] = { + STPMU1_RANGE(600000, 0, 30, 25000), + STPMU1_RANGE(1350000, 31, 63, 0), +}; + +static const struct stpmu1_range buck2_ranges[] = { + STPMU1_RANGE(1000000, 0, 17, 0), + STPMU1_RANGE(1050000, 18, 19, 0), + STPMU1_RANGE(1100000, 20, 21, 0), + STPMU1_RANGE(1150000, 22, 23, 0), + STPMU1_RANGE(1200000, 24, 25, 0), + STPMU1_RANGE(1250000, 26, 27, 0), + STPMU1_RANGE(1300000, 28, 29, 0), + STPMU1_RANGE(1350000, 30, 31, 0), + STPMU1_RANGE(1400000, 32, 33, 0), + STPMU1_RANGE(1450000, 34, 35, 0), + STPMU1_RANGE(1500000, 36, 63, 0), +}; + +static const struct stpmu1_range buck3_ranges[] = { + STPMU1_RANGE(1000000, 0, 19, 0), + STPMU1_RANGE(1100000, 20, 23, 0), + STPMU1_RANGE(1200000, 24, 27, 0), + STPMU1_RANGE(1300000, 28, 31, 0), + STPMU1_RANGE(1400000, 32, 35, 0), + STPMU1_RANGE(1500000, 36, 55, 100000), + STPMU1_RANGE(3400000, 56, 63, 0), +}; + +static const struct stpmu1_range buck4_ranges[] = { + STPMU1_RANGE(600000, 0, 27, 25000), + STPMU1_RANGE(1300000, 28, 29, 0), + STPMU1_RANGE(1350000, 30, 31, 0), + STPMU1_RANGE(1400000, 32, 33, 0), + STPMU1_RANGE(1450000, 34, 35, 0), + STPMU1_RANGE(1500000, 36, 60, 100000), + STPMU1_RANGE(3900000, 61, 63, 0), +}; + +/* BUCK: 1,2,3,4 - voltage ranges */ +static const struct stpmu1_output_range buck_voltage_range[] = { + STPMU1_OUTPUT_RANGE(buck1_ranges, ARRAY_SIZE(buck1_ranges)), + STPMU1_OUTPUT_RANGE(buck2_ranges, ARRAY_SIZE(buck2_ranges)), + STPMU1_OUTPUT_RANGE(buck3_ranges, ARRAY_SIZE(buck3_ranges)), + STPMU1_OUTPUT_RANGE(buck4_ranges, ARRAY_SIZE(buck4_ranges)), +}; + +/* BUCK modes */ +static const struct dm_regulator_mode buck_modes[] = { + STPMU1_MODE(STPMU1_BUCK_MODE_HP, STPMU1_BUCK_MODE_HP, "HP"), + STPMU1_MODE(STPMU1_BUCK_MODE_LP, STPMU1_BUCK_MODE_LP, "LP"), +}; + +static int stpmu1_buck_get_uv(struct udevice *dev, int buck) +{ + int sel; + + sel = pmic_reg_read(dev, STPMU1_BUCKX_CTRL_REG(buck)); + if (sel < 0) + return sel; + + sel &= STPMU1_BUCK_OUTPUT_MASK; + sel >>= STPMU1_BUCK_OUTPUT_SHIFT; + + return stpmu1_output_find_uv(sel, &buck_voltage_range[buck]); +} + +static int stpmu1_buck_get_value(struct udevice *dev) +{ + return stpmu1_buck_get_uv(dev->parent, dev->driver_data - 1); +} + +static int stpmu1_buck_set_value(struct udevice *dev, int uv) +{ + int sel, buck = dev->driver_data - 1; + + sel = stpmu1_output_find_sel(uv, &buck_voltage_range[buck]); + if (sel < 0) + return sel; + + return pmic_clrsetbits(dev->parent, + STPMU1_BUCKX_CTRL_REG(buck), + STPMU1_BUCK_OUTPUT_MASK, + sel << STPMU1_BUCK_OUTPUT_SHIFT); +} + +static int stpmu1_buck_get_enable(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, + STPMU1_BUCKX_CTRL_REG(dev->driver_data - 1)); + if (ret < 0) + return false; + + return ret & STPMU1_BUCK_EN ? true : false; +} + +static int stpmu1_buck_set_enable(struct udevice *dev, bool enable) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + int ret, uv; + + /* if regulator is already in the wanted state, nothing to do */ + if (stpmu1_buck_get_enable(dev) == enable) + return 0; + + if (enable) { + uc_pdata = dev_get_uclass_platdata(dev); + uv = stpmu1_buck_get_value(dev); + if ((uv < uc_pdata->min_uV) || (uv > uc_pdata->max_uV)) + stpmu1_buck_set_value(dev, uc_pdata->min_uV); + } + + ret = pmic_clrsetbits(dev->parent, + STPMU1_BUCKX_CTRL_REG(dev->driver_data - 1), + STPMU1_BUCK_EN, enable ? STPMU1_BUCK_EN : 0); + if (enable) + mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + + return ret; +} + +static int stpmu1_buck_get_mode(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, + STPMU1_BUCKX_CTRL_REG(dev->driver_data - 1)); + if (ret < 0) + return ret; + + return ret & STPMU1_BUCK_MODE ? STPMU1_BUCK_MODE_LP : + STPMU1_BUCK_MODE_HP; +} + +static int stpmu1_buck_set_mode(struct udevice *dev, int mode) +{ + return pmic_clrsetbits(dev->parent, + STPMU1_BUCKX_CTRL_REG(dev->driver_data - 1), + STPMU1_BUCK_MODE, + mode ? STPMU1_BUCK_MODE : 0); +} + +static int stpmu1_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + if (!dev->driver_data || dev->driver_data > STPMU1_MAX_BUCK) + return -EINVAL; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode = (struct dm_regulator_mode *)buck_modes; + uc_pdata->mode_count = ARRAY_SIZE(buck_modes); + + return 0; +} + +static const struct dm_regulator_ops stpmu1_buck_ops = { + .get_value = stpmu1_buck_get_value, + .set_value = stpmu1_buck_set_value, + .get_enable = stpmu1_buck_get_enable, + .set_enable = stpmu1_buck_set_enable, + .get_mode = stpmu1_buck_get_mode, + .set_mode = stpmu1_buck_set_mode, +}; + +U_BOOT_DRIVER(stpmu1_buck) = { + .name = "stpmu1_buck", + .id = UCLASS_REGULATOR, + .ops = &stpmu1_buck_ops, + .probe = stpmu1_buck_probe, +}; + +/* + * LDO regulators + */ + +static const struct stpmu1_range ldo12_ranges[] = { + STPMU1_RANGE(1700000, 0, 7, 0), + STPMU1_RANGE(1700000, 8, 24, 100000), + STPMU1_RANGE(3300000, 25, 31, 0), +}; + +static const struct stpmu1_range ldo3_ranges[] = { + STPMU1_RANGE(1700000, 0, 7, 0), + STPMU1_RANGE(1700000, 8, 24, 100000), + STPMU1_RANGE(3300000, 25, 30, 0), + /* Sel 31 is special case when LDO3 is in mode sync_source (BUCK2/2) */ +}; + +static const struct stpmu1_range ldo5_ranges[] = { + STPMU1_RANGE(1700000, 0, 7, 0), + STPMU1_RANGE(1700000, 8, 30, 100000), + STPMU1_RANGE(3900000, 31, 31, 0), +}; + +static const struct stpmu1_range ldo6_ranges[] = { + STPMU1_RANGE(900000, 0, 24, 100000), + STPMU1_RANGE(3300000, 25, 31, 0), +}; + +/* LDO: 1,2,3,4,5,6 - voltage ranges */ +static const struct stpmu1_output_range ldo_voltage_range[] = { + STPMU1_OUTPUT_RANGE(ldo12_ranges, ARRAY_SIZE(ldo12_ranges)), + STPMU1_OUTPUT_RANGE(ldo12_ranges, ARRAY_SIZE(ldo12_ranges)), + STPMU1_OUTPUT_RANGE(ldo3_ranges, ARRAY_SIZE(ldo3_ranges)), + STPMU1_OUTPUT_RANGE(NULL, 0), + STPMU1_OUTPUT_RANGE(ldo5_ranges, ARRAY_SIZE(ldo5_ranges)), + STPMU1_OUTPUT_RANGE(ldo6_ranges, ARRAY_SIZE(ldo6_ranges)), +}; + +/* LDO modes */ +static const struct dm_regulator_mode ldo_modes[] = { + STPMU1_MODE(STPMU1_LDO_MODE_NORMAL, + STPMU1_LDO_MODE_NORMAL, "NORMAL"), + STPMU1_MODE(STPMU1_LDO_MODE_BYPASS, + STPMU1_LDO_MODE_BYPASS, "BYPASS"), + STPMU1_MODE(STPMU1_LDO_MODE_SINK_SOURCE, + STPMU1_LDO_MODE_SINK_SOURCE, "SINK SOURCE"), +}; + +static int stpmu1_ldo_get_value(struct udevice *dev) +{ + int sel, ldo = dev->driver_data - 1; + + sel = pmic_reg_read(dev->parent, STPMU1_LDOX_CTRL_REG(ldo)); + if (sel < 0) + return sel; + + /* ldo4 => 3,3V */ + if (ldo == STPMU1_LDO4) + return STPMU1_LDO4_UV; + + sel &= STPMU1_LDO12356_OUTPUT_MASK; + sel >>= STPMU1_LDO12356_OUTPUT_SHIFT; + + /* ldo3, sel = 31 => BUCK2/2 */ + if (ldo == STPMU1_LDO3 && sel == STPMU1_LDO3_DDR_SEL) + return stpmu1_buck_get_uv(dev->parent, STPMU1_BUCK2) / 2; + + return stpmu1_output_find_uv(sel, &ldo_voltage_range[ldo]); +} + +static int stpmu1_ldo_set_value(struct udevice *dev, int uv) +{ + int sel, ldo = dev->driver_data - 1; + + /* ldo4 => not possible */ + if (ldo == STPMU1_LDO4) + return -EINVAL; + + sel = stpmu1_output_find_sel(uv, &ldo_voltage_range[ldo]); + if (sel < 0) + return sel; + + return pmic_clrsetbits(dev->parent, + STPMU1_LDOX_CTRL_REG(ldo), + STPMU1_LDO12356_OUTPUT_MASK, + sel << STPMU1_LDO12356_OUTPUT_SHIFT); +} + +static int stpmu1_ldo_get_enable(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, + STPMU1_LDOX_CTRL_REG(dev->driver_data - 1)); + if (ret < 0) + return false; + + return ret & STPMU1_LDO_EN ? true : false; +} + +static int stpmu1_ldo_set_enable(struct udevice *dev, bool enable) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + int ret, uv; + + /* if regulator is already in the wanted state, nothing to do */ + if (stpmu1_ldo_get_enable(dev) == enable) + return 0; + + if (enable) { + uc_pdata = dev_get_uclass_platdata(dev); + uv = stpmu1_ldo_get_value(dev); + if ((uv < uc_pdata->min_uV) || (uv > uc_pdata->max_uV)) + stpmu1_ldo_set_value(dev, uc_pdata->min_uV); + } + + ret = pmic_clrsetbits(dev->parent, + STPMU1_LDOX_CTRL_REG(dev->driver_data - 1), + STPMU1_LDO_EN, enable ? STPMU1_LDO_EN : 0); + if (enable) + mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + + return ret; +} + +static int stpmu1_ldo_get_mode(struct udevice *dev) +{ + int ret, ldo = dev->driver_data - 1; + + if (ldo != STPMU1_LDO3) + return -EINVAL; + + ret = pmic_reg_read(dev->parent, STPMU1_LDOX_CTRL_REG(ldo)); + if (ret < 0) + return ret; + + if (ret & STPMU1_LDO3_MODE) + return STPMU1_LDO_MODE_BYPASS; + + ret &= STPMU1_LDO12356_OUTPUT_MASK; + ret >>= STPMU1_LDO12356_OUTPUT_SHIFT; + + return ret == STPMU1_LDO3_DDR_SEL ? STPMU1_LDO_MODE_SINK_SOURCE : + STPMU1_LDO_MODE_NORMAL; +} + +static int stpmu1_ldo_set_mode(struct udevice *dev, int mode) +{ + int ret, ldo = dev->driver_data - 1; + + if (ldo != STPMU1_LDO3) + return -EINVAL; + + ret = pmic_reg_read(dev->parent, STPMU1_LDOX_CTRL_REG(ldo)); + if (ret < 0) + return ret; + + switch (mode) { + case STPMU1_LDO_MODE_SINK_SOURCE: + ret &= ~STPMU1_LDO12356_OUTPUT_MASK; + ret |= STPMU1_LDO3_DDR_SEL << STPMU1_LDO12356_OUTPUT_SHIFT; + case STPMU1_LDO_MODE_NORMAL: + ret &= ~STPMU1_LDO3_MODE; + break; + case STPMU1_LDO_MODE_BYPASS: + ret |= STPMU1_LDO3_MODE; + break; + } + + return pmic_reg_write(dev->parent, STPMU1_LDOX_CTRL_REG(ldo), ret); +} + +static int stpmu1_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + if (!dev->driver_data || dev->driver_data > STPMU1_MAX_LDO) + return -EINVAL; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + if (dev->driver_data - 1 == STPMU1_LDO3) { + uc_pdata->mode = (struct dm_regulator_mode *)ldo_modes; + uc_pdata->mode_count = ARRAY_SIZE(ldo_modes); + } else { + uc_pdata->mode_count = 0; + } + + return 0; +} + +static const struct dm_regulator_ops stpmu1_ldo_ops = { + .get_value = stpmu1_ldo_get_value, + .set_value = stpmu1_ldo_set_value, + .get_enable = stpmu1_ldo_get_enable, + .set_enable = stpmu1_ldo_set_enable, + .get_mode = stpmu1_ldo_get_mode, + .set_mode = stpmu1_ldo_set_mode, +}; + +U_BOOT_DRIVER(stpmu1_ldo) = { + .name = "stpmu1_ldo", + .id = UCLASS_REGULATOR, + .ops = &stpmu1_ldo_ops, + .probe = stpmu1_ldo_probe, +}; + +/* + * VREF DDR regulator + */ + +static int stpmu1_vref_ddr_get_value(struct udevice *dev) +{ + /* BUCK2/2 */ + return stpmu1_buck_get_uv(dev->parent, STPMU1_BUCK2) / 2; +} + +static int stpmu1_vref_ddr_get_enable(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, STPMU1_VREF_CTRL_REG); + if (ret < 0) + return false; + + return ret & STPMU1_VREF_EN ? true : false; +} + +static int stpmu1_vref_ddr_set_enable(struct udevice *dev, bool enable) +{ + int ret; + + /* if regulator is already in the wanted state, nothing to do */ + if (stpmu1_vref_ddr_get_enable(dev) == enable) + return 0; + + ret = pmic_clrsetbits(dev->parent, STPMU1_VREF_CTRL_REG, + STPMU1_VREF_EN, enable ? STPMU1_VREF_EN : 0); + if (enable) + mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + + return ret; +} + +static int stpmu1_vref_ddr_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_FIXED; + uc_pdata->mode_count = 0; + + return 0; +} + +static const struct dm_regulator_ops stpmu1_vref_ddr_ops = { + .get_value = stpmu1_vref_ddr_get_value, + .get_enable = stpmu1_vref_ddr_get_enable, + .set_enable = stpmu1_vref_ddr_set_enable, +}; + +U_BOOT_DRIVER(stpmu1_vref_ddr) = { + .name = "stpmu1_vref_ddr", + .id = UCLASS_REGULATOR, + .ops = &stpmu1_vref_ddr_ops, + .probe = stpmu1_vref_ddr_probe, +}; + +/* + * BOOST regulator + */ + +static int stpmu1_boost_get_enable(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, STPMU1_USB_CTRL_REG); + if (ret < 0) + return false; + + return ret & STPMU1_USB_BOOST_EN ? true : false; +} + +static int stpmu1_boost_set_enable(struct udevice *dev, bool enable) +{ + int ret; + + ret = pmic_reg_read(dev->parent, STPMU1_USB_CTRL_REG); + if (ret < 0) + return ret; + + if (!enable && ret & STPMU1_USB_PWR_SW_EN) + return -EINVAL; + + /* if regulator is already in the wanted state, nothing to do */ + if (!!(ret & STPMU1_USB_BOOST_EN) == enable) + return 0; + + ret = pmic_clrsetbits(dev->parent, STPMU1_USB_CTRL_REG, + STPMU1_USB_BOOST_EN, + enable ? STPMU1_USB_BOOST_EN : 0); + if (enable) + mdelay(STPMU1_USB_BOOST_START_UP_DELAY_MS); + + return ret; +} + +static int stpmu1_boost_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_FIXED; + uc_pdata->mode_count = 0; + + return 0; +} + +static const struct dm_regulator_ops stpmu1_boost_ops = { + .get_enable = stpmu1_boost_get_enable, + .set_enable = stpmu1_boost_set_enable, +}; + +U_BOOT_DRIVER(stpmu1_boost) = { + .name = "stpmu1_boost", + .id = UCLASS_REGULATOR, + .ops = &stpmu1_boost_ops, + .probe = stpmu1_boost_probe, +}; + +/* + * USB power switch + */ + +static int stpmu1_pwr_sw_get_enable(struct udevice *dev) +{ + uint mask = 1 << dev->driver_data; + int ret; + + ret = pmic_reg_read(dev->parent, STPMU1_USB_CTRL_REG); + if (ret < 0) + return false; + + return ret & mask ? true : false; +} + +static int stpmu1_pwr_sw_set_enable(struct udevice *dev, bool enable) +{ + uint mask = 1 << dev->driver_data; + int ret; + + ret = pmic_reg_read(dev->parent, STPMU1_USB_CTRL_REG); + if (ret < 0) + return ret; + + /* if regulator is already in the wanted state, nothing to do */ + if (!!(ret & mask) == enable) + return 0; + + /* Boost management */ + if (enable && !(ret & STPMU1_USB_BOOST_EN)) { + pmic_clrsetbits(dev->parent, STPMU1_USB_CTRL_REG, + STPMU1_USB_BOOST_EN, STPMU1_USB_BOOST_EN); + mdelay(STPMU1_USB_BOOST_START_UP_DELAY_MS); + } else if (!enable && ret & STPMU1_USB_BOOST_EN && + (ret & STPMU1_USB_PWR_SW_EN) != STPMU1_USB_PWR_SW_EN) { + pmic_clrsetbits(dev->parent, STPMU1_USB_CTRL_REG, + STPMU1_USB_BOOST_EN, 0); + } + + ret = pmic_clrsetbits(dev->parent, STPMU1_USB_CTRL_REG, + mask, enable ? mask : 0); + if (enable) + mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + + return ret; +} + +static int stpmu1_pwr_sw_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + if (!dev->driver_data || dev->driver_data > STPMU1_MAX_PWR_SW) + return -EINVAL; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_FIXED; + uc_pdata->mode_count = 0; + + return 0; +} + +static const struct dm_regulator_ops stpmu1_pwr_sw_ops = { + .get_enable = stpmu1_pwr_sw_get_enable, + .set_enable = stpmu1_pwr_sw_set_enable, +}; + +U_BOOT_DRIVER(stpmu1_pwr_sw) = { + .name = "stpmu1_pwr_sw", + .id = UCLASS_REGULATOR, + .ops = &stpmu1_pwr_sw_ops, + .probe = stpmu1_pwr_sw_probe, +}; |