diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/adc/Kconfig | 16 | ||||
| -rw-r--r-- | drivers/adc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/adc/adc-uclass.c | 35 | ||||
| -rw-r--r-- | drivers/adc/stm32-adc-core.c | 209 | ||||
| -rw-r--r-- | drivers/adc/stm32-adc-core.h | 51 | ||||
| -rw-r--r-- | drivers/adc/stm32-adc.c | 257 | ||||
| -rw-r--r-- | drivers/bootcount/bootcount.c | 13 | ||||
| -rw-r--r-- | drivers/clk/clk-uclass.c | 8 | ||||
| -rw-r--r-- | drivers/clk/clk_sandbox_test.c | 13 | ||||
| -rw-r--r-- | drivers/gpio/hsdk-creg-gpio.c | 103 | ||||
| -rw-r--r-- | drivers/mmc/arm_pl180_mmci.c | 73 | ||||
| -rw-r--r-- | drivers/mmc/arm_pl180_mmci.h | 1 |
12 files changed, 705 insertions, 75 deletions
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 93e27f131ca..e719c38bb31 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -47,3 +47,19 @@ config SARADC_ROCKCHIP - 2~6 analog input channels - 1O or 12 bits resolution - Up to 1MSPS of sample rate + +config STM32_ADC + bool "Enable STMicroelectronics STM32 ADC driver" + depends on ADC && (STM32H7 || ARCH_STM32MP) + help + This enables driver for STMicroelectronics STM32 analog-to-digital + converter (ADC). + A STM32 ADC block can be composed of several individual ADCs. + Each has its own private registers, but shares some resources: + - clock selection and prescaler + - voltage reference + - common registers area. + STM32 ADC driver is composed of: + - core driver to deal with common resources + - child driver to deal with individual ADC resources (declare ADC + device and associated channels, start/stop conversions) diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile index 95c93d4c57a..cca0fecd597 100644 --- a/drivers/adc/Makefile +++ b/drivers/adc/Makefile @@ -10,3 +10,4 @@ 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 +obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o diff --git a/drivers/adc/adc-uclass.c b/drivers/adc/adc-uclass.c index 17c1a4e52aa..738c1eabdc3 100644 --- a/drivers/adc/adc-uclass.c +++ b/drivers/adc/adc-uclass.c @@ -264,10 +264,8 @@ static int adc_vdd_platdata_update(struct udevice *dev) * will bind before its supply regulator device, then the below 'get' * will return an error. */ - ret = device_get_supply_regulator(dev, "vdd-supply", - &uc_pdata->vdd_supply); - if (ret) - return ret; + if (!uc_pdata->vdd_supply) + return 0; ret = regulator_get_value(uc_pdata->vdd_supply); if (ret < 0) @@ -283,10 +281,8 @@ static int adc_vss_platdata_update(struct udevice *dev) struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); int ret; - ret = device_get_supply_regulator(dev, "vss-supply", - &uc_pdata->vss_supply); - if (ret) - return ret; + if (!uc_pdata->vss_supply) + return 0; ret = regulator_get_value(uc_pdata->vss_supply); if (ret < 0) @@ -302,14 +298,11 @@ int adc_vdd_value(struct udevice *dev, int *uV) struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); int ret, value_sign = uc_pdata->vdd_polarity_negative ? -1 : 1; - if (!uc_pdata->vdd_supply) - goto nodev; - /* Update the regulator Value. */ ret = adc_vdd_platdata_update(dev); if (ret) return ret; -nodev: + if (uc_pdata->vdd_microvolts == -ENODATA) return -ENODATA; @@ -323,14 +316,11 @@ int adc_vss_value(struct udevice *dev, int *uV) struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); int ret, value_sign = uc_pdata->vss_polarity_negative ? -1 : 1; - if (!uc_pdata->vss_supply) - goto nodev; - /* Update the regulator Value. */ ret = adc_vss_platdata_update(dev); if (ret) return ret; -nodev: + if (uc_pdata->vss_microvolts == -ENODATA) return -ENODATA; @@ -348,7 +338,12 @@ static int adc_vdd_platdata_set(struct udevice *dev) prop = "vdd-polarity-negative"; uc_pdata->vdd_polarity_negative = dev_read_bool(dev, prop); - ret = adc_vdd_platdata_update(dev); + /* Optionally get regulators */ + ret = device_get_supply_regulator(dev, "vdd-supply", + &uc_pdata->vdd_supply); + if (!ret) + return adc_vdd_platdata_update(dev); + if (ret != -ENOENT) return ret; @@ -368,7 +363,11 @@ static int adc_vss_platdata_set(struct udevice *dev) prop = "vss-polarity-negative"; uc_pdata->vss_polarity_negative = dev_read_bool(dev, prop); - ret = adc_vss_platdata_update(dev); + ret = device_get_supply_regulator(dev, "vss-supply", + &uc_pdata->vss_supply); + if (!ret) + return adc_vss_platdata_update(dev); + if (ret != -ENOENT) return ret; diff --git a/drivers/adc/stm32-adc-core.c b/drivers/adc/stm32-adc-core.c new file mode 100644 index 00000000000..a9aa143bfe7 --- /dev/null +++ b/drivers/adc/stm32-adc-core.c @@ -0,0 +1,209 @@ +// 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.18 drivers/iio/adc/stm32-adc-core.c. + */ + +#include <common.h> +#include <asm/io.h> +#include <power/regulator.h> +#include "stm32-adc-core.h" + +/* STM32H7 - common registers for all ADC instances */ +#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08) + +/* STM32H7_ADC_CCR - bit fields */ +#define STM32H7_PRESC_SHIFT 18 +#define STM32H7_PRESC_MASK GENMASK(21, 18) +#define STM32H7_CKMODE_SHIFT 16 +#define STM32H7_CKMODE_MASK GENMASK(17, 16) + +/* STM32 H7 maximum analog clock rate (from datasheet) */ +#define STM32H7_ADC_MAX_CLK_RATE 36000000 + +/** + * struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock + * @ckmode: ADC clock mode, Async or sync with prescaler. + * @presc: prescaler bitfield for async clock mode + * @div: prescaler division ratio + */ +struct stm32h7_adc_ck_spec { + u32 ckmode; + u32 presc; + int div; +}; + +static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = { + /* 00: CK_ADC[1..3]: Asynchronous clock modes */ + { 0, 0, 1 }, + { 0, 1, 2 }, + { 0, 2, 4 }, + { 0, 3, 6 }, + { 0, 4, 8 }, + { 0, 5, 10 }, + { 0, 6, 12 }, + { 0, 7, 16 }, + { 0, 8, 32 }, + { 0, 9, 64 }, + { 0, 10, 128 }, + { 0, 11, 256 }, + /* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */ + { 1, 0, 1 }, + { 2, 0, 2 }, + { 3, 0, 4 }, +}; + +static int stm32h7_adc_clk_sel(struct udevice *dev, + struct stm32_adc_common *common) +{ + u32 ckmode, presc; + unsigned long rate; + int i, div; + + /* stm32h7 bus clock is common for all ADC instances (mandatory) */ + if (!clk_valid(&common->bclk)) { + dev_err(dev, "No bclk clock found\n"); + return -ENOENT; + } + + /* + * stm32h7 can use either 'bus' or 'adc' clock for analog circuitry. + * So, choice is to have bus clock mandatory and adc clock optional. + * If optional 'adc' clock has been found, then try to use it first. + */ + if (clk_valid(&common->aclk)) { + /* + * Asynchronous clock modes (e.g. ckmode == 0) + * From spec: PLL output musn't exceed max rate + */ + rate = clk_get_rate(&common->aclk); + if (!rate) { + dev_err(dev, "Invalid aclk rate: 0\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) { + ckmode = stm32h7_adc_ckmodes_spec[i].ckmode; + presc = stm32h7_adc_ckmodes_spec[i].presc; + div = stm32h7_adc_ckmodes_spec[i].div; + + if (ckmode) + continue; + + if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE) + goto out; + } + } + + /* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */ + rate = clk_get_rate(&common->bclk); + if (!rate) { + dev_err(dev, "Invalid bus clock rate: 0\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) { + ckmode = stm32h7_adc_ckmodes_spec[i].ckmode; + presc = stm32h7_adc_ckmodes_spec[i].presc; + div = stm32h7_adc_ckmodes_spec[i].div; + + if (!ckmode) + continue; + + if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE) + goto out; + } + + dev_err(dev, "clk selection failed\n"); + return -EINVAL; + +out: + /* rate used later by each ADC instance to control BOOST mode */ + common->rate = rate / div; + + /* Set common clock mode and prescaler */ + clrsetbits_le32(common->base + STM32H7_ADC_CCR, + STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK, + ckmode << STM32H7_CKMODE_SHIFT | + presc << STM32H7_PRESC_SHIFT); + + dev_dbg(dev, "Using %s clock/%d source at %ld kHz\n", + ckmode ? "bus" : "adc", div, common->rate / 1000); + + return 0; +} + +static int stm32_adc_core_probe(struct udevice *dev) +{ + struct stm32_adc_common *common = dev_get_priv(dev); + int ret; + + common->base = dev_read_addr_ptr(dev); + if (!common->base) { + dev_err(dev, "can't get address\n"); + return -ENOENT; + } + + ret = device_get_supply_regulator(dev, "vref-supply", &common->vref); + if (ret) { + dev_err(dev, "can't get vref-supply: %d\n", ret); + return ret; + } + + ret = regulator_get_value(common->vref); + if (ret < 0) { + dev_err(dev, "can't get vref-supply value: %d\n", ret); + return ret; + } + common->vref_uv = ret; + + ret = clk_get_by_name(dev, "adc", &common->aclk); + if (!ret) { + ret = clk_enable(&common->aclk); + if (ret) { + dev_err(dev, "Can't enable aclk: %d\n", ret); + return ret; + } + } + + ret = clk_get_by_name(dev, "bus", &common->bclk); + if (!ret) { + ret = clk_enable(&common->bclk); + if (ret) { + dev_err(dev, "Can't enable bclk: %d\n", ret); + goto err_aclk_disable; + } + } + + ret = stm32h7_adc_clk_sel(dev, common); + if (ret) + goto err_bclk_disable; + + return ret; + +err_bclk_disable: + if (clk_valid(&common->bclk)) + clk_disable(&common->bclk); + +err_aclk_disable: + if (clk_valid(&common->aclk)) + clk_disable(&common->aclk); + + return ret; +} + +static const struct udevice_id stm32_adc_core_ids[] = { + { .compatible = "st,stm32h7-adc-core" }, + { .compatible = "st,stm32mp1-adc-core" }, + {} +}; + +U_BOOT_DRIVER(stm32_adc_core) = { + .name = "stm32-adc-core", + .id = UCLASS_SIMPLE_BUS, + .of_match = stm32_adc_core_ids, + .probe = stm32_adc_core_probe, + .priv_auto_alloc_size = sizeof(struct stm32_adc_common), +}; diff --git a/drivers/adc/stm32-adc-core.h b/drivers/adc/stm32-adc-core.h new file mode 100644 index 00000000000..ba0e10e6cc2 --- /dev/null +++ b/drivers/adc/stm32-adc-core.h @@ -0,0 +1,51 @@ +/* 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.18 drivers/iio/adc/stm32-adc-core.h. + */ + +#ifndef __STM32_ADC_H +#define __STM32_ADC_H + +/* + * STM32 - ADC global register map + * ________________________________________________________ + * | Offset | Register | + * -------------------------------------------------------- + * | 0x000 | Master ADC1 | + * -------------------------------------------------------- + * | 0x100 | Slave ADC2 | + * -------------------------------------------------------- + * | 0x200 | Slave ADC3 | + * -------------------------------------------------------- + * | 0x300 | Master & Slave common regs | + * -------------------------------------------------------- + */ +#define STM32_ADC_MAX_ADCS 3 +#define STM32_ADCX_COMN_OFFSET 0x300 + +#include <common.h> +#include <clk.h> +#include <dm.h> + +/** + * struct stm32_adc_common - stm32 ADC driver common data (for all instances) + * @base: control registers base cpu addr + * @rate: clock rate used for analog circuitry + * @aclk: clock for the analog circuitry + * @bclk: bus clock common for all ADCs + * @vref: regulator reference + * @vref_uv: reference supply voltage (uV) + */ +struct stm32_adc_common { + void __iomem *base; + unsigned long rate; + struct clk aclk; + struct clk bclk; + struct udevice *vref; + int vref_uv; +}; + +#endif diff --git a/drivers/adc/stm32-adc.c b/drivers/adc/stm32-adc.c new file mode 100644 index 00000000000..e108062f2fe --- /dev/null +++ b/drivers/adc/stm32-adc.c @@ -0,0 +1,257 @@ +// 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.18 drivers/iio/adc/stm32-adc.c. + */ + +#include <common.h> +#include <adc.h> +#include <asm/io.h> +#include <linux/iopoll.h> +#include "stm32-adc-core.h" + +/* STM32H7 - Registers for each ADC instance */ +#define STM32H7_ADC_ISR 0x00 +#define STM32H7_ADC_CR 0x08 +#define STM32H7_ADC_CFGR 0x0C +#define STM32H7_ADC_SMPR1 0x14 +#define STM32H7_ADC_SMPR2 0x18 +#define STM32H7_ADC_PCSEL 0x1C +#define STM32H7_ADC_SQR1 0x30 +#define STM32H7_ADC_DR 0x40 +#define STM32H7_ADC_DIFSEL 0xC0 + +/* STM32H7_ADC_ISR - bit fields */ +#define STM32MP1_VREGREADY BIT(12) +#define STM32H7_EOC BIT(2) +#define STM32H7_ADRDY BIT(0) + +/* STM32H7_ADC_CR - bit fields */ +#define STM32H7_DEEPPWD BIT(29) +#define STM32H7_ADVREGEN BIT(28) +#define STM32H7_BOOST BIT(8) +#define STM32H7_ADSTART BIT(2) +#define STM32H7_ADDIS BIT(1) +#define STM32H7_ADEN BIT(0) + +/* STM32H7_ADC_CFGR bit fields */ +#define STM32H7_EXTEN GENMASK(11, 10) +#define STM32H7_DMNGT GENMASK(1, 0) + +/* STM32H7_ADC_SQR1 - bit fields */ +#define STM32H7_SQ1_SHIFT 6 + +/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */ +#define STM32H7_BOOST_CLKRATE 20000000UL + +#define STM32_ADC_CH_MAX 20 /* max number of channels */ +#define STM32_ADC_TIMEOUT_US 100000 + +struct stm32_adc_cfg { + unsigned int max_channels; + unsigned int num_bits; + bool has_vregready; +}; + +struct stm32_adc { + void __iomem *regs; + int active_channel; + const struct stm32_adc_cfg *cfg; +}; + +static int stm32_adc_stop(struct udevice *dev) +{ + struct stm32_adc *adc = dev_get_priv(dev); + + setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADDIS); + clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST); + /* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */ + setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD); + adc->active_channel = -1; + + return 0; +} + +static int stm32_adc_start_channel(struct udevice *dev, int channel) +{ + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); + struct stm32_adc_common *common = dev_get_priv(dev_get_parent(dev)); + struct stm32_adc *adc = dev_get_priv(dev); + int ret; + u32 val; + + /* Exit deep power down, then enable ADC voltage regulator */ + clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD); + setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADVREGEN); + if (common->rate > STM32H7_BOOST_CLKRATE) + setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST); + + /* Wait for startup time */ + if (!adc->cfg->has_vregready) { + udelay(20); + } else { + ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val, + val & STM32MP1_VREGREADY, + STM32_ADC_TIMEOUT_US); + if (ret < 0) { + stm32_adc_stop(dev); + dev_err(dev, "Failed to enable vreg: %d\n", ret); + return ret; + } + } + + /* Only use single ended channels */ + writel(0, adc->regs + STM32H7_ADC_DIFSEL); + + /* Enable ADC, Poll for ADRDY to be set (after adc startup time) */ + setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADEN); + ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val, + val & STM32H7_ADRDY, STM32_ADC_TIMEOUT_US); + if (ret < 0) { + stm32_adc_stop(dev); + dev_err(dev, "Failed to enable ADC: %d\n", ret); + return ret; + } + + /* Preselect channels */ + writel(uc_pdata->channel_mask, adc->regs + STM32H7_ADC_PCSEL); + + /* Set sampling time to max value by default */ + writel(0xffffffff, adc->regs + STM32H7_ADC_SMPR1); + writel(0xffffffff, adc->regs + STM32H7_ADC_SMPR2); + + /* Program regular sequence: chan in SQ1 & len = 0 for one channel */ + writel(channel << STM32H7_SQ1_SHIFT, adc->regs + STM32H7_ADC_SQR1); + + /* Trigger detection disabled (conversion can be launched in SW) */ + clrbits_le32(adc->regs + STM32H7_ADC_CFGR, STM32H7_EXTEN | + STM32H7_DMNGT); + adc->active_channel = channel; + + return 0; +} + +static int stm32_adc_channel_data(struct udevice *dev, int channel, + unsigned int *data) +{ + struct stm32_adc *adc = dev_get_priv(dev); + int ret; + u32 val; + + if (channel != adc->active_channel) { + dev_err(dev, "Requested channel is not active!\n"); + return -EINVAL; + } + + setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADSTART); + ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val, + val & STM32H7_EOC, STM32_ADC_TIMEOUT_US); + if (ret < 0) { + dev_err(dev, "conversion timed out: %d\n", ret); + return ret; + } + + *data = readl(adc->regs + STM32H7_ADC_DR); + + return 0; +} + +static int stm32_adc_chan_of_init(struct udevice *dev) +{ + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); + struct stm32_adc *adc = dev_get_priv(dev); + u32 chans[STM32_ADC_CH_MAX]; + int i, num_channels, ret; + + /* Retrieve single ended channels listed in device tree */ + num_channels = dev_read_size(dev, "st,adc-channels"); + if (num_channels < 0) { + dev_err(dev, "can't get st,adc-channels: %d\n", num_channels); + return num_channels; + } + num_channels /= sizeof(u32); + + if (num_channels > adc->cfg->max_channels) { + dev_err(dev, "too many st,adc-channels: %d\n", num_channels); + return -EINVAL; + } + + ret = dev_read_u32_array(dev, "st,adc-channels", chans, num_channels); + if (ret < 0) { + dev_err(dev, "can't read st,adc-channels: %d\n", ret); + return ret; + } + + for (i = 0; i < num_channels; i++) { + if (chans[i] >= adc->cfg->max_channels) { + dev_err(dev, "bad channel %u\n", chans[i]); + return -EINVAL; + } + uc_pdata->channel_mask |= 1 << chans[i]; + } + + uc_pdata->data_mask = (1 << adc->cfg->num_bits) - 1; + uc_pdata->data_format = ADC_DATA_FORMAT_BIN; + uc_pdata->data_timeout_us = 100000; + + return 0; +} + +static int stm32_adc_probe(struct udevice *dev) +{ + struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev); + struct stm32_adc_common *common = dev_get_priv(dev_get_parent(dev)); + struct stm32_adc *adc = dev_get_priv(dev); + int offset; + + offset = dev_read_u32_default(dev, "reg", -ENODATA); + if (offset < 0) { + dev_err(dev, "Can't read reg property\n"); + return offset; + } + adc->regs = common->base + offset; + adc->cfg = (const struct stm32_adc_cfg *)dev_get_driver_data(dev); + + /* VDD supplied by common vref pin */ + uc_pdata->vdd_supply = common->vref; + uc_pdata->vdd_microvolts = common->vref_uv; + uc_pdata->vss_microvolts = 0; + + return stm32_adc_chan_of_init(dev); +} + +static const struct adc_ops stm32_adc_ops = { + .start_channel = stm32_adc_start_channel, + .channel_data = stm32_adc_channel_data, + .stop = stm32_adc_stop, +}; + +static const struct stm32_adc_cfg stm32h7_adc_cfg = { + .num_bits = 16, + .max_channels = STM32_ADC_CH_MAX, +}; + +static const struct stm32_adc_cfg stm32mp1_adc_cfg = { + .num_bits = 16, + .max_channels = STM32_ADC_CH_MAX, + .has_vregready = true, +}; + +static const struct udevice_id stm32_adc_ids[] = { + { .compatible = "st,stm32h7-adc", + .data = (ulong)&stm32h7_adc_cfg }, + { .compatible = "st,stm32mp1-adc", + .data = (ulong)&stm32mp1_adc_cfg }, + {} +}; + +U_BOOT_DRIVER(stm32_adc) = { + .name = "stm32-adc", + .id = UCLASS_ADC, + .of_match = stm32_adc_ids, + .probe = stm32_adc_probe, + .ops = &stm32_adc_ops, + .priv_auto_alloc_size = sizeof(struct stm32_adc), +}; diff --git a/drivers/bootcount/bootcount.c b/drivers/bootcount/bootcount.c index a3162c97edd..646c563f8a4 100644 --- a/drivers/bootcount/bootcount.c +++ b/drivers/bootcount/bootcount.c @@ -11,16 +11,23 @@ __weak void bootcount_store(ulong a) { void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR; + uintptr_t flush_start = rounddown(CONFIG_SYS_BOOTCOUNT_ADDR, + CONFIG_SYS_CACHELINE_SIZE); + uintptr_t flush_end; #if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD) raw_bootcount_store(reg, (BOOTCOUNT_MAGIC & 0xffff0000) | a); + + flush_end = roundup(CONFIG_SYS_BOOTCOUNT_ADDR + 4, + CONFIG_SYS_CACHELINE_SIZE); #else raw_bootcount_store(reg, a); raw_bootcount_store(reg + 4, BOOTCOUNT_MAGIC); + + flush_end = roundup(CONFIG_SYS_BOOTCOUNT_ADDR + 8, + CONFIG_SYS_CACHELINE_SIZE); #endif /* defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD */ - flush_dcache_range(CONFIG_SYS_BOOTCOUNT_ADDR, - CONFIG_SYS_BOOTCOUNT_ADDR + - CONFIG_SYS_CACHELINE_SIZE); + flush_dcache_range(flush_start, flush_end); } __weak ulong bootcount_load(void) diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 419d4515acc..2b15978e141 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -154,6 +154,10 @@ static int clk_set_default_parents(struct udevice *dev) for (index = 0; index < num_parents; index++) { ret = clk_get_by_indexed_prop(dev, "assigned-clock-parents", index, &parent_clk); + /* If -ENOENT, this is a no-op entry */ + if (ret == -ENOENT) + continue; + if (ret) { debug("%s: could not get parent clock %d for %s\n", __func__, index, dev_read_name(dev)); @@ -210,6 +214,10 @@ static int clk_set_default_rates(struct udevice *dev) goto fail; for (index = 0; index < num_rates; index++) { + /* If 0 is passed, this is a no-op */ + if (!rates[index]) + continue; + ret = clk_get_by_indexed_prop(dev, "assigned-clocks", index, &clk); if (ret) { diff --git a/drivers/clk/clk_sandbox_test.c b/drivers/clk/clk_sandbox_test.c index 8cd4abb84f5..e8465dbfad1 100644 --- a/drivers/clk/clk_sandbox_test.c +++ b/drivers/clk/clk_sandbox_test.c @@ -116,6 +116,19 @@ int sandbox_clk_test_release_bulk(struct udevice *dev) return clk_release_bulk(&sbct->bulk); } +int sandbox_clk_test_valid(struct udevice *dev) +{ + struct sandbox_clk_test *sbct = dev_get_priv(dev); + int i; + + for (i = 0; i < SANDBOX_CLK_TEST_ID_COUNT; i++) { + if (!clk_valid(&sbct->clks[i])) + return -EINVAL; + } + + return 0; +} + static const struct udevice_id sandbox_clk_test_ids[] = { { .compatible = "sandbox,clk-test" }, { } diff --git a/drivers/gpio/hsdk-creg-gpio.c b/drivers/gpio/hsdk-creg-gpio.c index 084a2da6522..800027f18ef 100644 --- a/drivers/gpio/hsdk-creg-gpio.c +++ b/drivers/gpio/hsdk-creg-gpio.c @@ -16,25 +16,24 @@ #include <errno.h> #include <linux/printk.h> -#define HSDK_CREG_MAX_GPIO 8 - -#define GPIO_ACTIVATE 0x2 -#define GPIO_DEACTIVATE 0x3 -#define GPIO_PIN_MASK 0x3 -#define BIT_PER_GPIO 2 +#define DRV_NAME "gpio_creg" struct hsdk_creg_gpio { - uint32_t *regs; + u32 *regs; + u8 shift; + u8 activate; + u8 deactivate; + u8 bit_per_gpio; }; static int hsdk_creg_gpio_set_value(struct udevice *dev, unsigned oft, int val) { struct hsdk_creg_gpio *hcg = dev_get_priv(dev); - uint32_t reg = readl(hcg->regs); - uint32_t cmd = val ? GPIO_DEACTIVATE : GPIO_ACTIVATE; + u8 reg_shift = oft * hcg->bit_per_gpio + hcg->shift; + u32 reg = readl(hcg->regs); - reg &= ~(GPIO_PIN_MASK << (oft * BIT_PER_GPIO)); - reg |= (cmd << (oft * BIT_PER_GPIO)); + reg &= ~(GENMASK(hcg->bit_per_gpio - 1, 0) << reg_shift); + reg |= ((val ? hcg->deactivate : hcg->activate) << reg_shift); writel(reg, hcg->regs); @@ -51,7 +50,9 @@ static int hsdk_creg_gpio_direction_output(struct udevice *dev, unsigned oft, static int hsdk_creg_gpio_direction_input(struct udevice *dev, unsigned oft) { - pr_err("hsdk-creg-gpio can't be used as input!\n"); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + pr_err("%s can't be used as input!\n", uc_priv->bank_name); return -ENOTSUPP; } @@ -59,10 +60,11 @@ static int hsdk_creg_gpio_direction_input(struct udevice *dev, unsigned oft) static int hsdk_creg_gpio_get_value(struct udevice *dev, unsigned int oft) { struct hsdk_creg_gpio *hcg = dev_get_priv(dev); - uint32_t val = readl(hcg->regs); + u32 val = readl(hcg->regs); - val = (val >> (oft * BIT_PER_GPIO)) & GPIO_PIN_MASK; - return (val == GPIO_DEACTIVATE) ? 1 : 0; + val >>= oft * hcg->bit_per_gpio + hcg->shift; + val &= GENMASK(hcg->bit_per_gpio - 1, 0); + return (val == hcg->deactivate) ? 1 : 0; } static const struct dm_gpio_ops hsdk_creg_gpio_ops = { @@ -76,17 +78,74 @@ static int hsdk_creg_gpio_probe(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct hsdk_creg_gpio *hcg = dev_get_priv(dev); + u32 shift, bit_per_gpio, activate, deactivate, gpio_count; + const u8 *defaults; - hcg->regs = (uint32_t *)devfdt_get_addr_ptr(dev); - - uc_priv->gpio_count = dev_read_u32_default(dev, "gpio-count", 1); - if (uc_priv->gpio_count > HSDK_CREG_MAX_GPIO) - uc_priv->gpio_count = HSDK_CREG_MAX_GPIO; + hcg->regs = (u32 *)devfdt_get_addr_ptr(dev); + gpio_count = dev_read_u32_default(dev, "gpio-count", 1); + shift = dev_read_u32_default(dev, "gpio-first-shift", 0); + bit_per_gpio = dev_read_u32_default(dev, "gpio-bit-per-line", 1); + activate = dev_read_u32_default(dev, "gpio-activate-val", 1); + deactivate = dev_read_u32_default(dev, "gpio-deactivate-val", 0); + defaults = dev_read_u8_array_ptr(dev, "gpio-default-val", gpio_count); uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name"); if (!uc_priv->bank_name) uc_priv->bank_name = dev_read_name(dev); + if (!bit_per_gpio) { + pr_err("%s: 'gpio-bit-per-line' can't be 0\n", + uc_priv->bank_name); + + return -EINVAL; + } + + if (!gpio_count) { + pr_err("%s: 'gpio-count' can't be 0\n", + uc_priv->bank_name); + + return -EINVAL; + } + + if ((gpio_count * bit_per_gpio + shift) > 32) { + pr_err("%s: u32 io register overflow: try to use %u bits\n", + uc_priv->bank_name, gpio_count * bit_per_gpio + shift); + + return -EINVAL; + } + + if (GENMASK(31, bit_per_gpio) & activate) { + pr_err("%s: 'gpio-activate-val' can't be more than %lu\n", + uc_priv->bank_name, GENMASK(bit_per_gpio - 1, 0)); + + return -EINVAL; + } + + if (GENMASK(31, bit_per_gpio) & deactivate) { + pr_err("%s: 'gpio-deactivate-val' can't be more than %lu\n", + uc_priv->bank_name, GENMASK(bit_per_gpio - 1, 0)); + + return -EINVAL; + } + + if (activate == deactivate) { + pr_err("%s: 'gpio-deactivate-val' and 'gpio-activate-val' can't be equal\n", + uc_priv->bank_name); + + return -EINVAL; + } + + hcg->shift = (u8)shift; + hcg->bit_per_gpio = (u8)bit_per_gpio; + hcg->activate = (u8)activate; + hcg->deactivate = (u8)deactivate; + uc_priv->gpio_count = gpio_count; + + /* Setup default GPIO value if we have "gpio-default-val" array */ + if (defaults) + for (u8 i = 0; i < gpio_count; i++) + hsdk_creg_gpio_set_value(dev, i, defaults[i]); + pr_debug("%s GPIO [0x%p] controller with %d gpios probed\n", uc_priv->bank_name, hcg->regs, uc_priv->gpio_count); @@ -94,12 +153,12 @@ static int hsdk_creg_gpio_probe(struct udevice *dev) } static const struct udevice_id hsdk_creg_gpio_ids[] = { - { .compatible = "snps,hsdk-creg-gpio" }, + { .compatible = "snps,creg-gpio" }, { } }; U_BOOT_DRIVER(gpio_hsdk_creg) = { - .name = "gpio_hsdk_creg", + .name = DRV_NAME, .id = UCLASS_GPIO, .ops = &hsdk_creg_gpio_ops, .probe = hsdk_creg_gpio_probe, diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index e267cd782e8..f71d79ecd6b 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -357,13 +357,13 @@ static const struct mmc_ops arm_pl180_mmci_ops = { .set_ios = host_set_ios, .init = mmc_host_reset, }; -#endif /* * mmc_host_init - initialize the mmc controller. * Set initial clock and power for mmc slot. * Initialize mmc struct and register with mmc framework. */ + int arm_pl180_mmci_init(struct pl180_mmc_host *host, struct mmc **mmc) { u32 sdi_u32; @@ -377,9 +377,8 @@ int arm_pl180_mmci_init(struct pl180_mmc_host *host, struct mmc **mmc) writel(sdi_u32, &host->base->mask0); host->cfg.name = host->name; -#ifndef CONFIG_DM_MMC host->cfg.ops = &arm_pl180_mmci_ops; -#endif + /* TODO remove the duplicates */ host->cfg.host_caps = host->caps; host->cfg.voltages = host->voltages; @@ -393,20 +392,34 @@ int arm_pl180_mmci_init(struct pl180_mmc_host *host, struct mmc **mmc) *mmc = mmc_create(&host->cfg, host); if (!*mmc) return -1; - debug("registered mmc interface number is:%d\n", (*mmc)->block_dev.devnum); return 0; } +#endif #ifdef CONFIG_DM_MMC +static void arm_pl180_mmc_init(struct pl180_mmc_host *host) +{ + u32 sdi_u32; + + writel(host->pwr_init, &host->base->power); + writel(host->clkdiv_init, &host->base->clock); + udelay(CLK_CHANGE_DELAY); + + /* Disable mmc interrupts */ + sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; + writel(sdi_u32, &host->base->mask0); +} + static int arm_pl180_mmc_probe(struct udevice *dev) { struct arm_pl180_mmc_plat *pdata = dev_get_platdata(dev); struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct mmc *mmc = &pdata->mmc; - struct pl180_mmc_host *host = mmc->priv; + struct pl180_mmc_host *host = dev->priv; + struct mmc_config *cfg = &pdata->cfg; struct clk clk; u32 bus_width; int ret; @@ -417,31 +430,33 @@ static int arm_pl180_mmc_probe(struct udevice *dev) ret = clk_enable(&clk); if (ret) { + clk_free(&clk); dev_err(dev, "failed to enable clock\n"); return ret; } - strcpy(host->name, "MMC"); host->pwr_init = INIT_PWR; host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V1 | SDI_CLKCR_CLKEN | SDI_CLKCR_HWFC_EN; - host->voltages = VOLTAGE_WINDOW_SD; - host->caps = 0; host->clock_in = clk_get_rate(&clk); - host->clock_min = host->clock_in / (2 * (SDI_CLKCR_CLKDIV_INIT_V1 + 1)); - host->clock_max = dev_read_u32_default(dev, "max-frequency", - MMC_CLOCK_MAX); host->version2 = dev_get_driver_data(dev); + cfg->name = dev->name; + cfg->voltages = VOLTAGE_WINDOW_SD; + cfg->host_caps = 0; + cfg->f_min = host->clock_in / (2 * (SDI_CLKCR_CLKDIV_INIT_V1 + 1)); + cfg->f_max = dev_read_u32_default(dev, "max-frequency", MMC_CLOCK_MAX); + cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + gpio_request_by_name(dev, "cd-gpios", 0, &host->cd_gpio, GPIOD_IS_IN); bus_width = dev_read_u32_default(dev, "bus-width", 1); switch (bus_width) { case 8: - host->caps |= MMC_MODE_8BIT; + cfg->host_caps |= MMC_MODE_8BIT; /* Hosts capable of 8-bit transfers can also do 4 bits */ case 4: - host->caps |= MMC_MODE_4BIT; + cfg->host_caps |= MMC_MODE_4BIT; break; case 1: break; @@ -449,19 +464,21 @@ static int arm_pl180_mmc_probe(struct udevice *dev) dev_err(dev, "Invalid bus-width value %u\n", bus_width); } - ret = arm_pl180_mmci_init(host, &mmc); - if (ret) { - dev_err(dev, "arm_pl180_mmci init failed\n"); - return ret; - } - + arm_pl180_mmc_init(host); + mmc->priv = host; mmc->dev = dev; - dev->priv = host; upriv->mmc = mmc; return 0; } +int arm_pl180_mmc_bind(struct udevice *dev) +{ + struct arm_pl180_mmc_plat *plat = dev_get_platdata(dev); + + return mmc_bind(dev, &plat->mmc, &plat->cfg); +} + static int dm_host_request(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) { @@ -479,16 +496,11 @@ static int dm_host_set_ios(struct udevice *dev) static int dm_mmc_getcd(struct udevice *dev) { - struct arm_pl180_mmc_plat *pdata = dev_get_platdata(dev); - struct mmc *mmc = &pdata->mmc; - struct pl180_mmc_host *host = mmc->priv; + struct pl180_mmc_host *host = dev->priv; int value = 1; - if (dm_gpio_is_valid(&host->cd_gpio)) { + if (dm_gpio_is_valid(&host->cd_gpio)) value = dm_gpio_get_value(&host->cd_gpio); - if (host->cd_inverted) - return !value; - } return value; } @@ -501,12 +513,10 @@ static const struct dm_mmc_ops arm_pl180_dm_mmc_ops = { static int arm_pl180_mmc_ofdata_to_platdata(struct udevice *dev) { - struct arm_pl180_mmc_plat *pdata = dev_get_platdata(dev); - struct mmc *mmc = &pdata->mmc; - struct pl180_mmc_host *host = mmc->priv; + struct pl180_mmc_host *host = dev->priv; fdt_addr_t addr; - addr = devfdt_get_addr(dev); + addr = dev_read_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; @@ -527,6 +537,7 @@ U_BOOT_DRIVER(arm_pl180_mmc) = { .ops = &arm_pl180_dm_mmc_ops, .probe = arm_pl180_mmc_probe, .ofdata_to_platdata = arm_pl180_mmc_ofdata_to_platdata, + .bind = arm_pl180_mmc_bind, .priv_auto_alloc_size = sizeof(struct pl180_mmc_host), .platdata_auto_alloc_size = sizeof(struct arm_pl180_mmc_plat), }; diff --git a/drivers/mmc/arm_pl180_mmci.h b/drivers/mmc/arm_pl180_mmci.h index 6b98db6cd97..36487be288b 100644 --- a/drivers/mmc/arm_pl180_mmci.h +++ b/drivers/mmc/arm_pl180_mmci.h @@ -192,7 +192,6 @@ struct pl180_mmc_host { struct mmc_config cfg; #ifdef CONFIG_DM_MMC struct gpio_desc cd_gpio; - bool cd_inverted; #endif }; |
