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/mmc/arm_pl180_mmci.c | 73 | ||||
-rw-r--r-- | drivers/mmc/arm_pl180_mmci.h | 1 | ||||
-rw-r--r-- | drivers/mmc/sunxi_mmc.c | 13 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/sunxi_nand.c | 2 | ||||
-rw-r--r-- | drivers/phy/allwinner/phy-sun4i-usb.c | 10 | ||||
-rw-r--r-- | drivers/video/sunxi/sunxi_de2.c | 3 |
16 files changed, 645 insertions, 64 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/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 }; diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 7fa1ae8b162..39f15eb4236 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -70,10 +70,12 @@ static int mmc_resource_init(int sdc_no) priv->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE; priv->mclkreg = &ccm->sd2_clk_cfg; break; +#ifdef SUNXI_MMC3_BASE case 3: priv->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE; priv->mclkreg = &ccm->sd3_clk_cfg; break; +#endif default: printf("Wrong mmc number %d\n", sdc_no); return -1; @@ -116,6 +118,9 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz) #ifdef CONFIG_MACH_SUN9I pll = CCM_MMC_CTRL_PLL_PERIPH0; pll_hz = clock_get_pll4_periph0(); +#elif defined(CONFIG_MACH_SUN50I_H6) + pll = CCM_MMC_CTRL_PLL6X2; + pll_hz = clock_get_pll6() * 2; #else pll = CCM_MMC_CTRL_PLL6; pll_hz = clock_get_pll6(); @@ -494,7 +499,7 @@ struct mmc *sunxi_mmc_init(int sdc_no) cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; cfg->host_caps = MMC_MODE_4BIT; -#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN8I) +#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN8I) || defined(CONFIG_MACH_SUN50I_H6) if (sdc_no == 2) cfg->host_caps = MMC_MODE_8BIT; #endif @@ -509,6 +514,7 @@ struct mmc *sunxi_mmc_init(int sdc_no) /* config ahb clock */ debug("init mmc %d clock and io\n", sdc_no); +#if !defined(CONFIG_MACH_SUN50I_H6) setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no)); #ifdef CONFIG_SUNXI_GEN_SUN6I @@ -520,6 +526,11 @@ struct mmc *sunxi_mmc_init(int sdc_no) writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET, SUNXI_MMC_COMMON_BASE + 4 * sdc_no); #endif +#else /* CONFIG_MACH_SUN50I_H6 */ + setbits_le32(&ccm->sd_gate_reset, 1 << sdc_no); + /* unassert reset */ + setbits_le32(&ccm->sd_gate_reset, 1 << (RESET_SHIFT + sdc_no)); +#endif ret = mmc_set_mod_clk(priv, 24000000); if (ret) return NULL; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 64e4621aaaf..9094f857c1e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3041,7 +3041,7 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; + return -ENOTSUPP; #endif chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); @@ -3070,7 +3070,7 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; + return -ENOTSUPP; #endif chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1); diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index bb87aca6987..3ccb168d137 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1369,7 +1369,7 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip) ONFI_FEATURE_ADDR_TIMING_MODE, feature); chip->nand.select_chip(mtd, -1); - if (ret) + if (ret && ret != -ENOTSUPP) return ret; } } diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 2b3cf48025c..a7d7e3f044a 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -117,9 +117,6 @@ struct sun4i_usb_phy_info { .gpio_vbus = CONFIG_USB3_VBUS_PIN, .gpio_vbus_det = NULL, .gpio_id_det = NULL, -#ifdef CONFIG_MACH_SUN6I - .rst_mask = (CCM_USB_CTRL_PHY3_RST | CCM_USB_CTRL_PHY3_CLK), -#endif }, }; @@ -300,8 +297,7 @@ static int sun4i_usb_phy_init(struct phy *phy) data->cfg->disc_thresh, PHY_DISCON_TH_LEN); } - if (usb_phy->id != 0) - sun4i_usb_phy_passby(phy, true); + sun4i_usb_phy_passby(phy, true); sun4i_usb_phy0_reroute(data, true); @@ -461,10 +457,10 @@ static int sun4i_usb_phy_probe(struct udevice *dev) phy->id = i; phy->rst_mask = info->rst_mask; + if ((data->cfg->type == sun8i_h3_phy) && (phy->id == 3)) + phy->rst_mask = (BIT(3) | BIT(11)); }; - setbits_le32(&data->ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); - debug("Allwinner Sun4I USB PHY driver loaded\n"); return 0; } diff --git a/drivers/video/sunxi/sunxi_de2.c b/drivers/video/sunxi/sunxi_de2.c index 4ed035d556a..8333ddc44c0 100644 --- a/drivers/video/sunxi/sunxi_de2.c +++ b/drivers/video/sunxi/sunxi_de2.c @@ -347,6 +347,9 @@ int sunxi_simplefb_setup(void *blob) if (ret) { debug("DE2 not present\n"); return 0; + } else if (!device_active(de2)) { + debug("DE2 present but not probed\n"); + return 0; } ret = uclass_find_device_by_name(UCLASS_DISPLAY, |