Merge patch series "drivers: Driver support for ADI SC5xx SoCs"

Greg Malysa <malysagreg@gmail.com> says:

This series adds all of the supported peripheral drivers for the sc5xx
series of SoCs from Analog Devices and other drivers that are used by
the evaluation kits, such as a GPIO expander used by the EZLITE carrier
boards. This series passes gitlab CI tests.

Link: https://lore.kernel.org/r/20250226173150.13198-1-malysagreg@gmail.com
This commit is contained in:
Tom Rini 2025-03-12 10:25:13 -06:00
commit 81ef65099e
36 changed files with 2954 additions and 0 deletions

View file

@ -631,11 +631,24 @@ F: arch/arm/mach-sc5xx/
F: board/adi/
F: doc/device-tree-bindings/arm/adi/adi,sc5xx.yaml
F: doc/device-tree-bindings/clock/adi,sc5xx-clocks.yaml
F: doc/device-tree-bindings/pinctrl/adi,adsp-pinctrl.yaml
F: doc/device-tree-bindings/timer/adi,sc5xx-gptimer.yaml
F: drivers/clk/adi/
F: drivers/dma/adi_dma.c
F: drivers/gpio/adp5588_gpio.c
F: drivers/gpio/gpio-adi-adsp.c
F: drivers/i2c/adi_i2c.c
F: drivers/mmc/adi_sdhci.c
F: drivers/net/dwc_eth_qos_adi.c
F: drivers/pinctrl/pinctrl-adi-adsp.c
F: drivers/remoteproc/adi_sc5xx_rproc.c
F: drivers/serial/serial_adi_uart4.c
F: drivers/spi/adi_spi3.c
F: drivers/timer/adi_sc5xx_timer.c
F: drivers/usb/musb-new/sc5xx.c
F: drivers/watchdog/adi_wdt.c
F: include/configs/sc5*
F: include/dt-bindings/pinctrl/adi-adsp.h
F: include/env/adi/
ARM SNAPDRAGON

View file

@ -0,0 +1,73 @@
# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pinctrl/adi,adsp-pinctrl.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Pinctrl Driver for Analog Devices SC5xx Processors
maintainers:
- Vasileios Bimpikas <vasileios.bimpikas@analog.com>
- Utsav Agarwal <utsav.agarwal@analog.com>
- Arturs Artamonovs <arturs.artamonovs@analog.com>
description: |
This driver provides an interface for performing pin configuration
Analog Devices SoCs using the ADSP PORT hardware for pin
configuration according to the HRM. Currently this is only the
SC5xx series.
properties:
compatible:
const: adi,adsp-pinctrl
reg:
maxItems: 1
adi,npins:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Total number of pins available to this SoC's pin controller,
found in the HRM.
patternProperties:
'_pins$':
type: object
properties:
adi,pins:
$ref: /schemas/types.yaml#/definitions/uint32-array
description:
For n pins, 2n values must be provided as a sequence of pin
name as identified with the ADI_ADSP_PIN() macro and a pin
function constant, both defined in
include/dt-bindings/pinctrl/adi-adsp.h.
required:
- adi,pins
additionalProperties: false
required:
- compatible
- reg
- adi,npins
additionalProperties: false
examples:
- |
#include <dt-bindings/pinctrl/adi-adsp.h>
soc {
pinctrl0: pinctrl@0x31004000 {
compatible = "adi,adsp-pinctrl";
reg = <0x31004000 0x500>;
adi,npins = <135>;
uart0_default: uart0_pins {
adi,pins = <ADI_ADSP_PIN('A', 6) ADI_ADSP_PINFUNC_ALT1>,
<ADI_ADSP_PIN('A', 7) ADI_ADSP_PINFUNC_ALT1>;
};
};
};

View file

@ -76,6 +76,13 @@ config XILINX_DPDMA
this file is used as placeholder for driver. The main reason is
to record compatible string and calling power domain driver.
config ADI_DMA
bool "ADI DMA driver"
depends on DMA && DMA_CHANNELS
help
Enable DMA support for Analog Devices SOCs, such as the SC5xx.
Currently this is a minimalistic driver tested against OSPI use only.
if APBH_DMA
config APBH_DMA_BURST
bool "Enable DMA BURST"

View file

@ -13,5 +13,6 @@ obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o
obj-$(CONFIG_TI_EDMA3) += ti-edma3.o
obj-$(CONFIG_DMA_LPC32XX) += lpc32xx_dma.o
obj-$(CONFIG_XILINX_DPDMA) += xilinx_dpdma.o
obj-$(CONFIG_ADI_DMA) += adi_dma.o
obj-y += ti/

253
drivers/dma/adi_dma.c Normal file
View file

@ -0,0 +1,253 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Analog Devices DMA controller driver
*
* (C) Copyright 2024 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
* Contact: Greg Malysa <greg.malysa@timesys.com>
* Contact: Ian Roberts <ian.roberts@timesys.com>
*
*/
#include <dm.h>
#include <dma.h>
#include <dma-uclass.h>
#include <dm/device_compat.h>
#include <linux/errno.h>
#include <linux/io.h>
#define HAS_MDMA BIT(0)
#define REG_ADDRSTART 0x04
#define REG_CFG 0x08
#define REG_XCNT 0x0C
#define REG_XMOD 0x10
#define REG_STAT 0x30
#define BITP_DMA_CFG_MSIZE 8
#define BITP_DMA_CFG_PSIZE 4
#define BITM_DMA_CFG_WNR 0x00000002
#define BITM_DMA_CFG_EN 0x00000001
#define ENUM_DMA_CFG_XCNT_INT 0x00100000
#define BITP_DMA_STAT_PBWID 12
#define BITP_DMA_STAT_ERRC 4
#define BITM_DMA_STAT_PBWID 0x00003000
#define BITM_DMA_STAT_ERRC 0x00000070
#define BITM_DMA_STAT_PIRQ 0x00000004
#define BITM_DMA_STAT_IRQERR 0x00000002
#define BITM_DMA_STAT_IRQDONE 0x00000001
#define DMA_MDMA_SRC_DEFAULT_CONFIG(psize, msize) \
(BITM_DMA_CFG_EN | ((psize) << BITP_DMA_CFG_PSIZE) | ((msize) << BITP_DMA_CFG_MSIZE))
#define DMA_MDMA_DST_DEFAULT_CONFIG(psize, msize) \
(BITM_DMA_CFG_EN | BITM_DMA_CFG_WNR | ENUM_DMA_CFG_XCNT_INT | \
((psize) << BITP_DMA_CFG_PSIZE) | ((msize) << BITP_DMA_CFG_MSIZE))
struct adi_dma_channel {
int id;
struct adi_dma *dma;
void __iomem *iosrc;
void __iomem *iodest;
};
struct adi_dma {
struct udevice *dev;
struct adi_dma_channel channels[1];
void __iomem *ioaddr;
unsigned long hw_cfg;
};
static const struct udevice_id dma_dt_ids[] = {
{ .compatible = "adi,mdma-controller", .data = HAS_MDMA },
{ }
};
static u8 adi_dma_get_msize(u32 n_bytecount, u32 n_address)
{
/* Calculate MSIZE, PSIZE, XCNT and XMOD */
u8 n_msize = 0;
u32 n_value = n_bytecount | n_address;
u32 n_mask = 0x1;
for (n_msize = 0; n_msize < 5; n_msize++, n_mask <<= 1) {
if ((n_value & n_mask) == n_mask)
break;
}
return n_msize;
}
static int adi_dma_get_ch_error(void __iomem *ch)
{
u32 cause = (ioread32(ch + REG_STAT) & BITM_DMA_STAT_ERRC) >>
BITP_DMA_STAT_ERRC;
switch (cause) {
case 0:
return -EINVAL;
case 1:
return -EBUSY;
case 2:
return -EFAULT;
case 3:
fallthrough;
case 5:
fallthrough;
case 6:
fallthrough;
default:
return -EIO;
}
}
static int adi_mdma_transfer(struct udevice *dev, int direction,
dma_addr_t dst, dma_addr_t src, size_t len)
{
struct adi_dma *priv = dev_get_priv(dev);
void __iomem *chsrc = priv->channels[0].iosrc;
void __iomem *chdst = priv->channels[0].iodest;
int result = 0;
u32 reg;
u32 bytecount = len;
u8 n_srcmsize;
u8 n_dstmsize;
u8 n_srcpsize;
u8 n_dstpsize;
u8 n_psize;
u32 srcconfig;
u32 dstconfig;
u8 srcpsizemax = (ioread32(chsrc + REG_STAT) & BITM_DMA_STAT_PBWID) >>
BITP_DMA_STAT_PBWID;
u8 dstpsizemax = (ioread32(chdst + REG_STAT) & BITM_DMA_STAT_PBWID) >>
BITP_DMA_STAT_PBWID;
const u32 CLRSTAT = (BITM_DMA_STAT_IRQDONE | BITM_DMA_STAT_IRQERR |
BITM_DMA_STAT_PIRQ);
if (len == 0)
return -EINVAL;
/* Clear DMA status */
iowrite32(CLRSTAT, chsrc + REG_STAT);
iowrite32(CLRSTAT, chdst + REG_STAT);
/* Calculate MSIZE, PSIZE, XCNT and XMOD */
n_srcmsize = adi_dma_get_msize(bytecount, src);
n_dstmsize = adi_dma_get_msize(bytecount, dst);
n_srcpsize = min(n_srcmsize, srcpsizemax);
n_dstpsize = min(n_dstmsize, dstpsizemax);
n_psize = min(n_srcpsize, n_dstpsize);
srcconfig = DMA_MDMA_SRC_DEFAULT_CONFIG(n_psize, n_srcmsize);
dstconfig = DMA_MDMA_DST_DEFAULT_CONFIG(n_psize, n_dstmsize);
/* Load the DMA descriptors */
iowrite32(src, chsrc + REG_ADDRSTART);
iowrite32(bytecount >> n_srcmsize, chsrc + REG_XCNT);
iowrite32(1 << n_srcmsize, chsrc + REG_XMOD);
iowrite32(dst, chdst + REG_ADDRSTART);
iowrite32(bytecount >> n_dstmsize, chdst + REG_XCNT);
iowrite32(1 << n_dstmsize, chdst + REG_XMOD);
iowrite32(dstconfig, chdst + REG_CFG);
iowrite32(srcconfig, chsrc + REG_CFG);
/* Wait for DMA to complete while checking for a DMA error */
do {
reg = ioread32(chsrc + REG_STAT);
if ((reg & BITM_DMA_STAT_IRQERR) == BITM_DMA_STAT_IRQERR) {
result = adi_dma_get_ch_error(chsrc);
break;
}
reg = ioread32(chdst + REG_STAT);
if ((reg & BITM_DMA_STAT_IRQERR) == BITM_DMA_STAT_IRQERR) {
result = adi_dma_get_ch_error(chdst);
break;
}
} while ((reg & BITM_DMA_STAT_IRQDONE) == 0);
clrbits_32(chsrc + REG_CFG, 1);
clrbits_32(chdst + REG_CFG, 1);
return result;
}
static int adi_dma_init_channel(struct adi_dma *dma,
struct adi_dma_channel *channel, ofnode node)
{
u32 offset;
if (ofnode_read_u32(node, "adi,id", &channel->id)) {
dev_err(dma->dev, "Missing adi,id for channel %s\n",
ofnode_get_name(node));
return -ENOENT;
}
if (ofnode_read_u32(node, "adi,src-offset", &offset)) {
dev_err(dma->dev, "Missing adi,src-offset for channel %s\n",
ofnode_get_name(node));
return -ENOENT;
}
channel->iosrc = dma->ioaddr + offset;
channel->dma = dma;
if (dma->hw_cfg & HAS_MDMA) {
if (ofnode_read_u32(node, "adi,dest-offset", &offset)) {
dev_err(dma->dev,
"Missing adi,dest-offset for channel %s\n",
ofnode_get_name(node));
return -ENOENT;
}
channel->iodest = dma->ioaddr + offset;
}
return 0;
}
static int adi_dma_probe(struct udevice *dev)
{
struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct adi_dma *priv = dev_get_priv(dev);
ofnode node, child;
priv->hw_cfg = dev_get_driver_data(dev);
if (priv->hw_cfg & HAS_MDMA)
uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM;
priv->ioaddr = dev_remap_addr(dev);
if (!priv->ioaddr)
return -EINVAL;
node = dev_read_first_subnode(dev);
if (!ofnode_valid(node)) {
dev_err(dev,
"Error: device tree DMA channel config missing!\n");
return -ENODEV;
}
node = dev_ofnode(dev);
ofnode_for_each_subnode(child, node) {
adi_dma_init_channel(priv, priv->channels, child);
break; //Only 1 channel supported for now
}
return 0;
}
static const struct dma_ops adi_dma_ops = {
.transfer = adi_mdma_transfer,
};
U_BOOT_DRIVER(adi_dma) = {
.name = "adi_dma",
.id = UCLASS_DMA,
.of_match = dma_dt_ids,
.ops = &adi_dma_ops,
.probe = adi_dma_probe,
.priv_auto = sizeof(struct adi_dma),
};

View file

@ -97,6 +97,15 @@ config SPL_DM_GPIO_LOOKUP_LABEL
different gpios on different hardware versions
for the same functionality in board code.
config ADI_GPIO
bool "ADI GPIO driver"
depends on DM_GPIO && ARCH_SC5XX
help
This driver supports GPIO banks on SC5xx processors. It
supports inputs and outputs but does not support pin
interrupt functionality (PINT) or other features in the
Linux version of the driver.
config ALTERA_PIO
bool "Altera PIO driver"
depends on DM_GPIO
@ -545,6 +554,14 @@ config DM_PCA953X
Now, max 24 bits chips and PCA953X compatible chips are
supported
config ADP5588_GPIO
bool "ADP5588 GPIO expander driver"
depends on DM_GPIO && DM_I2C
help
Say yes here to support GPIO functionality of ADI ADP5588 chips.
The ADP5588 is an 18-port I2C GPIO expander and keypad controller.
config SPL_DM_PCA953X
bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports in SPL"
depends on SPL_DM_GPIO

View file

@ -12,6 +12,7 @@ obj-$(CONFIG_$(PHASE_)DM_GPIO) += gpio-uclass.o
obj-$(CONFIG_$(XPL_)DM_PCA953X) += pca953x_gpio.o
obj-$(CONFIG_ADI_GPIO) += gpio-adi-adsp.o
obj-$(CONFIG_ASPEED_GPIO) += gpio-aspeed.o
obj-$(CONFIG_ASPEED_G7_GPIO) += gpio-aspeed-g7.o
obj-$(CONFIG_ASPEED_SGPIO) += gpio-aspeed-sgpio.o
@ -74,6 +75,7 @@ obj-$(CONFIG_NOMADIK_GPIO) += nmk_gpio.o
obj-$(CONFIG_MAX7320_GPIO) += max7320_gpio.o
obj-$(CONFIG_$(XPL_)MAX77663_GPIO) += max77663_gpio.o
obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o
obj-$(CONFIG_ADP5588_GPIO) += adp5588_gpio.o
obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o
obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o
obj-$(CONFIG_FTGPIO010) += ftgpio010.o

208
drivers/gpio/adp5588_gpio.c Normal file
View file

@ -0,0 +1,208 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* GPIO Chip driver for Analog Devices
* ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller
*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
* Contact: Greg Malysa <greg.malysa@timesys.com>
*
* Based on Michael Hennerich's Linux driver:
* Michael Hennerich <michael.hennerich@analog.com>
*
*/
#include <dm.h>
#include <i2c.h>
#include <asm-generic/gpio.h>
#define ADP5588_MAXGPIO 18
#define ADP5588_BANK(offs) ((offs) >> 3)
#define ADP5588_BIT(offs) (1u << ((offs) & 0x7))
#define DEV_ID 0x00 /* Device ID */
#define GPIO_DAT_STAT1 0x14 /* GPIO Data Status, Read twice to clear */
#define GPIO_DAT_STAT2 0x15 /* GPIO Data Status, Read twice to clear */
#define GPIO_DAT_STAT3 0x16 /* GPIO Data Status, Read twice to clear */
#define GPIO_DAT_OUT1 0x17 /* GPIO DATA OUT */
#define GPIO_DAT_OUT2 0x18 /* GPIO DATA OUT */
#define GPIO_DAT_OUT3 0x19 /* GPIO DATA OUT */
#define GPIO_INT_EN1 0x1A /* GPIO Interrupt Enable */
#define GPIO_INT_EN2 0x1B /* GPIO Interrupt Enable */
#define GPIO_INT_EN3 0x1C /* GPIO Interrupt Enable */
#define KP_GPIO1 0x1D /* Keypad or GPIO Selection */
#define KP_GPIO2 0x1E /* Keypad or GPIO Selection */
#define KP_GPIO3 0x1F /* Keypad or GPIO Selection */
#define GPIO_DIR1 0x23 /* GPIO Data Direction */
#define GPIO_DIR2 0x24 /* GPIO Data Direction */
#define GPIO_DIR3 0x25 /* GPIO Data Direction */
#define GPIO_PULL1 0x2C /* GPIO Pull Disable */
#define GPIO_PULL2 0x2D /* GPIO Pull Disable */
#define GPIO_PULL3 0x2E /* GPIO Pull Disable */
#define ID_MASK 0x0F
struct adp5588_gpio {
u8 dat_out[3];
u8 dir[3];
};
static int adp5588_gpio_read(struct udevice *dev, u8 reg)
{
int ret;
u8 val;
ret = dm_i2c_read(dev, reg, &val, 1);
if (ret < 0) {
pr_err("%s: read error\n", __func__);
return ret;
}
return val;
}
static int adp5588_gpio_write(struct udevice *dev, u8 reg, u8 val)
{
int ret;
ret = dm_i2c_write(dev, reg, &val, 1);
if (ret < 0) {
pr_err("%s: write error\n", __func__);
return ret;
}
return 0;
}
static int adp5588_get_value(struct udevice *dev, u32 offset)
{
struct adp5588_gpio *plat = dev_get_plat(dev);
unsigned int bank = ADP5588_BANK(offset);
unsigned int bit = ADP5588_BIT(offset);
int val;
if (plat->dir[bank] & bit)
val = plat->dat_out[bank];
else
val = adp5588_gpio_read(dev, GPIO_DAT_STAT1 + bank);
return !!(val & bit);
}
static int adp5588_set_value(struct udevice *dev, u32 offset,
int32_t value)
{
unsigned int bank, bit;
int ret;
struct adp5588_gpio *plat = dev_get_plat(dev);
bank = ADP5588_BANK(offset);
bit = ADP5588_BIT(offset);
if (value)
plat->dat_out[bank] |= bit;
else
plat->dat_out[bank] &= ~bit;
ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank,
plat->dat_out[bank]);
return ret;
}
static int adp5588_direction_input(struct udevice *dev, u32 offset)
{
int ret;
unsigned int bank;
struct adp5588_gpio *plat = dev_get_plat(dev);
bank = ADP5588_BANK(offset);
plat->dir[bank] &= ~ADP5588_BIT(offset);
ret = adp5588_gpio_write(dev, GPIO_DIR1 + bank, plat->dir[bank]);
return ret;
}
static int adp5588_direction_output(struct udevice *dev,
u32 offset, int value)
{
int ret;
unsigned int bank, bit;
struct adp5588_gpio *plat = dev_get_plat(dev);
bank = ADP5588_BANK(offset);
bit = ADP5588_BIT(offset);
plat->dir[bank] |= bit;
if (value)
plat->dat_out[bank] |= bit;
else
plat->dat_out[bank] &= ~bit;
ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank,
plat->dat_out[bank]);
ret |= adp5588_gpio_write(dev, GPIO_DIR1 + bank,
plat->dir[bank]);
return ret;
}
static int adp5588_ofdata_platdata(struct udevice *dev)
{
struct adp5588_gpio *plat = dev_get_plat(dev);
struct gpio_dev_priv *priv = dev_get_uclass_priv(dev);
int node = dev_of_offset(dev);
int ret, i, revid;
priv->gpio_count = ADP5588_MAXGPIO;
priv->bank_name = fdt_get_name(gd->fdt_blob, node, NULL);
ret = adp5588_gpio_read(dev, DEV_ID);
if (ret < 0)
return ret;
revid = ret & ID_MASK;
printf("ADP5588 Detected: Rev %x, Rev ID %x\n", ret, revid);
for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
plat->dat_out[i] = adp5588_gpio_read(dev, GPIO_DAT_OUT1 + i);
plat->dir[i] = adp5588_gpio_read(dev, GPIO_DIR1 + i);
ret |= adp5588_gpio_write(dev, KP_GPIO1 + i, 0);
ret |= adp5588_gpio_write(dev, GPIO_PULL1 + i, 0);
ret |= adp5588_gpio_write(dev, GPIO_INT_EN1 + i, 0);
if (ret) {
pr_err("%s: Initialization error\n", __func__);
return ret;
}
}
return 0;
}
static const struct dm_gpio_ops adp5588_ops = {
.direction_input = adp5588_direction_input,
.direction_output = adp5588_direction_output,
.get_value = adp5588_get_value,
.set_value = adp5588_set_value,
};
static const struct udevice_id adp5588_of_match_list[] = {
{ .compatible = "adi,adp5588"},
{ /* sentinel */ }
};
U_BOOT_DRIVER(gpio_adp5588) = {
.name = "gpio_adp5588",
.id = UCLASS_GPIO,
.ops = &adp5588_ops,
.of_match = adp5588_of_match_list,
.of_to_plat = adp5588_ofdata_platdata,
.plat_auto = sizeof(struct adp5588_gpio),
.flags = DM_FLAG_PRE_RELOC,
};

View file

@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Author: Greg Malysa <greg.malysa@timesys.com>
* Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
*/
#include <dm.h>
#include <asm-generic/gpio.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/io.h>
#define ADSP_PORT_MMIO_SIZE 0x80
#define ADSP_PORT_PIN_SIZE 16
#define ADSP_PORT_REG_FER 0x00
#define ADSP_PORT_REG_FER_SET 0x04
#define ADSP_PORT_REG_FER_CLEAR 0x08
#define ADSP_PORT_REG_DATA 0x0c
#define ADSP_PORT_REG_DATA_SET 0x10
#define ADSP_PORT_REG_DATA_CLEAR 0x14
#define ADSP_PORT_REG_DIR 0x18
#define ADSP_PORT_REG_DIR_SET 0x1c
#define ADSP_PORT_REG_DIR_CLEAR 0x20
#define ADSP_PORT_REG_INEN 0x24
#define ADSP_PORT_REG_INEN_SET 0x28
#define ADSP_PORT_REG_INEN_CLEAR 0x2c
#define ADSP_PORT_REG_PORT_MUX 0x30
#define ADSP_PORT_REG_DATA_TGL 0x34
#define ADSP_PORT_REG_POLAR 0x38
#define ADSP_PORT_REG_POLAR_SET 0x3c
#define ADSP_PORT_REG_POLAR_CLEAR 0x40
#define ADSP_PORT_REG_LOCK 0x44
#define ADSP_PORT_REG_TRIG_TGL 0x48
struct adsp_gpio_priv {
void __iomem *base;
int ngpio;
};
static u32 get_port(unsigned int pin)
{
return pin / ADSP_PORT_PIN_SIZE;
}
static u32 get_offset(unsigned int pin)
{
return pin % ADSP_PORT_PIN_SIZE;
}
static int adsp_gpio_input(struct udevice *udev, unsigned int pin)
{
struct adsp_gpio_priv *priv = dev_get_priv(udev);
u32 port, offset;
void __iomem *portbase;
if (pin < priv->ngpio) {
port = get_port(pin);
offset = get_offset(pin);
portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
iowrite16(BIT(offset), portbase + ADSP_PORT_REG_FER_CLEAR);
iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DIR_CLEAR);
iowrite16(BIT(offset), portbase + ADSP_PORT_REG_INEN_SET);
return 0;
}
return -EINVAL;
}
static int adsp_gpio_output(struct udevice *udev, unsigned int pin, int value)
{
struct adsp_gpio_priv *priv = dev_get_priv(udev);
u32 port, offset;
void __iomem *portbase;
if (pin < priv->ngpio) {
port = get_port(pin);
offset = get_offset(pin);
portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
iowrite16(BIT(offset), portbase + ADSP_PORT_REG_FER_CLEAR);
if (value)
iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_SET);
else
iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_CLEAR);
iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DIR_SET);
iowrite16(BIT(offset), portbase + ADSP_PORT_REG_INEN_CLEAR);
return 0;
}
return -EINVAL;
}
static int adsp_gpio_get_value(struct udevice *udev, unsigned int pin)
{
struct adsp_gpio_priv *priv = dev_get_priv(udev);
u32 port, offset;
u16 val;
void __iomem *portbase;
if (pin < priv->ngpio) {
port = get_port(pin);
offset = get_offset(pin);
portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
val = ioread16(portbase + ADSP_PORT_REG_DATA);
return !!(val & BIT(offset));
}
return 0;
}
static int adsp_gpio_set_value(struct udevice *udev, unsigned int pin, int value)
{
struct adsp_gpio_priv *priv = dev_get_priv(udev);
u32 port, offset;
void __iomem *portbase;
if (pin < priv->ngpio) {
port = get_port(pin);
offset = get_offset(pin);
portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
if (value)
iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_SET);
else
iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_CLEAR);
}
return 0;
}
static const struct dm_gpio_ops adsp_gpio_ops = {
.direction_input = adsp_gpio_input,
.direction_output = adsp_gpio_output,
.get_value = adsp_gpio_get_value,
.set_value = adsp_gpio_set_value,
};
static int adsp_gpio_probe(struct udevice *udev)
{
struct adsp_gpio_priv *priv = dev_get_priv(udev);
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
uc_priv->bank_name = "adsp gpio";
uc_priv->gpio_count = dev_read_u32_default(udev, "adi,ngpios", 0);
if (!uc_priv->gpio_count) {
dev_err(udev, "Missing adi,ngpios property!\n");
return -ENOENT;
}
priv->base = dev_read_addr_ptr(udev);
priv->ngpio = uc_priv->gpio_count;
return 0;
}
static const struct udevice_id adsp_gpio_match[] = {
{ .compatible = "adi,adsp-gpio" },
{ },
};
U_BOOT_DRIVER(adi_adsp_gpio) = {
.name = "adi_adsp_gpio",
.id = UCLASS_GPIO,
.ops = &adsp_gpio_ops,
.probe = adsp_gpio_probe,
.priv_auto = sizeof(struct adsp_gpio_priv),
.of_match = adsp_gpio_match,
.flags = DM_FLAG_PRE_RELOC,
};

View file

@ -154,6 +154,13 @@ config SPL_DM_I2C_GPIO
bindings are supported.
Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt
config SYS_I2C_ADI
bool "ADI I2C driver"
depends on DM_I2C && ARCH_SC5XX
help
Add support for the ADI (Analog Devices) I2C driver as used
in SC57X, SC58X, SC59X, SC59X_64.
config SYS_I2C_AT91
bool "Atmel I2C driver"
depends on DM_I2C && ARCH_AT91

View file

@ -11,6 +11,7 @@ obj-$(CONFIG_$(XPL_)I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o
obj-$(CONFIG_$(XPL_)I2C_CROS_EC_LDO) += cros_ec_ldo.o
obj-$(CONFIG_$(XPL_)SYS_I2C_LEGACY) += i2c_core.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o
obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o
obj-$(CONFIG_SYS_I2C_AST2600) += ast2600_i2c.o
obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o

386
drivers/i2c/adi_i2c.c Normal file
View file

@ -0,0 +1,386 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Converted to driver model by Nathan Barrett-Morrison
*
* Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
* Contact: Greg Malysa <greg.malysa@timesys.com>
*/
#include <clk.h>
#include <dm.h>
#include <i2c.h>
#include <mapmem.h>
#include <linux/io.h>
#define CLKLOW(x) ((x) & 0xFF) // Periods Clock Is Held Low
#define CLKHI(y) (((y) & 0xFF) << 0x8) // Periods Clock Is High
#define PRESCALE 0x007F // SCLKs Per Internal Time Reference (10MHz)
#define TWI_ENA 0x0080 // TWI Enable
#define SCCB 0x0200 // SCCB Compatibility Enable
#define SEN 0x0001 // Slave Enable
#define SADD_LEN 0x0002 // Slave Address Length
#define STDVAL 0x0004 // Slave Transmit Data Valid
#define TSC_NAK 0x0008 // NAK Generated At Conclusion Of Transfer
#define GEN 0x0010 // General Call Adrress Matching Enabled
#define SDIR 0x0001 // Slave Transfer Direction
#define GCALL 0x0002 // General Call Indicator
#define MEN 0x0001 // Master Mode Enable
#define MADD_LEN 0x0002 // Master Address Length
#define MDIR 0x0004 // Master Transmit Direction (RX/TX*)
#define FAST 0x0008 // Use Fast Mode Timing Specs
#define STOP 0x0010 // Issue Stop Condition
#define RSTART 0x0020 // Repeat Start or Stop* At End Of Transfer
#define DCNT 0x3FC0 // Data Bytes To Transfer
#define SDAOVR 0x4000 // Serial Data Override
#define SCLOVR 0x8000 // Serial Clock Override
#define MPROG 0x0001 // Master Transfer In Progress
#define LOSTARB 0x0002 // Lost Arbitration Indicator (Xfer Aborted)
#define ANAK 0x0004 // Address Not Acknowledged
#define DNAK 0x0008 // Data Not Acknowledged
#define BUFRDERR 0x0010 // Buffer Read Error
#define BUFWRERR 0x0020 // Buffer Write Error
#define SDASEN 0x0040 // Serial Data Sense
#define SCLSEN 0x0080 // Serial Clock Sense
#define BUSBUSY 0x0100 // Bus Busy Indicator
#define SINIT 0x0001 // Slave Transfer Initiated
#define SCOMP 0x0002 // Slave Transfer Complete
#define SERR 0x0004 // Slave Transfer Error
#define SOVF 0x0008 // Slave Overflow
#define MCOMP 0x0010 // Master Transfer Complete
#define MERR 0x0020 // Master Transfer Error
#define XMTSERV 0x0040 // Transmit FIFO Service
#define RCVSERV 0x0080 // Receive FIFO Service
#define XMTFLUSH 0x0001 // Transmit Buffer Flush
#define RCVFLUSH 0x0002 // Receive Buffer Flush
#define XMTINTLEN 0x0004 // Transmit Buffer Interrupt Length
#define RCVINTLEN 0x0008 // Receive Buffer Interrupt Length
#define XMTSTAT 0x0003 // Transmit FIFO Status
#define XMT_EMPTY 0x0000 // Transmit FIFO Empty
#define XMT_HALF 0x0001 // Transmit FIFO Has 1 Byte To Write
#define XMT_FULL 0x0003 // Transmit FIFO Full (2 Bytes To Write)
#define RCVSTAT 0x000C // Receive FIFO Status
#define RCV_EMPTY 0x0000 // Receive FIFO Empty
#define RCV_HALF 0x0004 // Receive FIFO Has 1 Byte To Read
#define RCV_FULL 0x000C // Receive FIFO Full (2 Bytes To Read)
/* Every register is 32bit aligned, but only 16bits in size */
#define ureg(name) u16 name; u16 __pad_##name
struct twi_regs {
ureg(clkdiv);
ureg(control);
ureg(slave_ctl);
ureg(slave_stat);
ureg(slave_addr);
ureg(master_ctl);
ureg(master_stat);
ureg(master_addr);
ureg(int_stat);
ureg(int_mask);
ureg(fifo_ctl);
ureg(fifo_stat);
u8 __pad[0x50];
ureg(xmt_data8);
ureg(xmt_data16);
ureg(rcv_data8);
ureg(rcv_data16);
};
#undef ureg
/*
* The way speed is changed into duty often results in integer truncation
* with 50% duty, so we'll force rounding up to the next duty by adding 1
* to the max. In practice this will get us a speed of something like
* 385 KHz. The other limit is easy to handle as it is only 8 bits.
*/
#define I2C_SPEED_MAX 400000
#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed))
#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1)
#define I2C_DUTY_MIN 0xff /* 8 bit limited */
#define I2C_M_COMBO 0x4
#define I2C_M_STOP 0x2
#define I2C_M_READ 0x1
/*
* All transfers are described by this data structure
*/
struct adi_i2c_msg {
u8 flags;
u32 len; /* msg length */
u8 *buf; /* pointer to msg data */
u32 olen; /* addr length */
u8 *obuf; /* addr buffer */
};
struct adi_i2c_dev {
struct twi_regs __iomem *base;
u32 i2c_clk;
uint speed;
};
/* Allow msec timeout per ~byte transfer */
#define I2C_TIMEOUT 10
/**
* wait_for_completion - manage the actual i2c transfer
* @msg: the i2c msg
*/
static int wait_for_completion(struct twi_regs *twi, struct adi_i2c_msg *msg)
{
u16 int_stat;
ulong timebase = get_timer(0);
do {
int_stat = ioread16(&twi->int_stat);
if (int_stat & XMTSERV) {
iowrite16(XMTSERV, &twi->int_stat);
if (msg->olen) {
iowrite16(*(msg->obuf++), &twi->xmt_data8);
--msg->olen;
} else if (!(msg->flags & I2C_M_COMBO) && msg->len) {
iowrite16(*(msg->buf++), &twi->xmt_data8);
--msg->len;
} else {
if (msg->flags & I2C_M_COMBO)
setbits_16(&twi->master_ctl, RSTART | MDIR);
else
setbits_16(&twi->master_ctl, STOP);
}
}
if (int_stat & RCVSERV) {
iowrite16(RCVSERV, &twi->int_stat);
if (msg->len) {
*(msg->buf++) = ioread16(&twi->rcv_data8);
--msg->len;
} else if (msg->flags & I2C_M_STOP) {
setbits_16(&twi->master_ctl, STOP);
}
}
if (int_stat & MERR) {
pr_err("%s: master transmit terror: %d\n", __func__,
ioread16(&twi->master_stat));
iowrite16(MERR, &twi->int_stat);
return -EIO;
}
if (int_stat & MCOMP) {
iowrite16(MCOMP, &twi->int_stat);
if (msg->flags & I2C_M_COMBO && msg->len) {
u16 mlen = min(msg->len, 0xffu) << 6;
clrsetbits_16(&twi->master_ctl, RSTART, mlen | MEN | MDIR);
} else {
break;
}
}
/* If we were able to do something, reset timeout */
if (int_stat)
timebase = get_timer(0);
} while (get_timer(timebase) < I2C_TIMEOUT);
return 0;
}
static int i2c_transfer(struct twi_regs *twi, u8 chip, u8 *offset,
int olen, u8 *buffer, int len, u8 flags)
{
int ret;
u16 ctl;
struct adi_i2c_msg msg = {
.flags = flags | (len >= 0xff ? I2C_M_STOP : 0),
.buf = buffer,
.len = len,
.obuf = offset,
.olen = olen,
};
/* wait for things to settle */
while (ioread16(&twi->master_stat) & BUSBUSY)
if (!IS_ENABLED(CONFIG_SPL_BUILD) && ctrlc())
return -EINTR;
/* Set Transmit device address */
iowrite16(chip, &twi->master_addr);
/* Clear the FIFO before starting things */
iowrite16(XMTFLUSH | RCVFLUSH, &twi->fifo_ctl);
iowrite16(0, &twi->fifo_ctl);
/* Prime the pump */
if (msg.olen) {
len = (msg.flags & I2C_M_COMBO) ? msg.olen : msg.olen + len;
iowrite16(*(msg.obuf++), &twi->xmt_data8);
--msg.olen;
} else if (!(msg.flags & I2C_M_READ) && msg.len) {
iowrite16(*(msg.buf++), &twi->xmt_data8);
--msg.len;
}
/* clear int stat */
iowrite16(-1, &twi->master_stat);
iowrite16(-1, &twi->int_stat);
iowrite16(0, &twi->int_mask);
/* Master enable */
ctl = ioread16(&twi->master_ctl);
ctl = (ctl & FAST) | (min(len, 0xff) << 6) | MEN |
((msg.flags & I2C_M_READ) ? MDIR : 0);
iowrite16(ctl, &twi->master_ctl);
/* Process the rest */
ret = wait_for_completion(twi, &msg);
clrbits_16(&twi->master_ctl, MEN);
clrbits_16(&twi->control, TWI_ENA);
setbits_16(&twi->control, TWI_ENA);
return ret;
}
static int adi_i2c_read(struct twi_regs *twi, u8 chip,
u8 *offset, int olen, u8 *buffer, int len)
{
return i2c_transfer(twi, chip, offset, olen, buffer,
len, olen ? I2C_M_COMBO : I2C_M_READ);
}
static int adi_i2c_write(struct twi_regs *twi, u8 chip,
u8 *offset, int olen, u8 *buffer, int len)
{
return i2c_transfer(twi, chip, offset, olen, buffer, len, 0);
}
static int adi_i2c_set_bus_speed(struct udevice *bus, uint speed)
{
struct adi_i2c_dev *dev = dev_get_priv(bus);
struct twi_regs *twi = dev->base;
u16 clkdiv = I2C_SPEED_TO_DUTY(speed);
/* Set TWI interface clock */
if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN)
return -1;
clkdiv = (clkdiv << 8) | (clkdiv & 0xff);
iowrite16(clkdiv, &twi->clkdiv);
/* Don't turn it on */
iowrite16(speed > 100000 ? FAST : 0, &twi->master_ctl);
return 0;
}
static int adi_i2c_of_to_plat(struct udevice *bus)
{
struct adi_i2c_dev *dev = dev_get_priv(bus);
struct clk clock;
u32 ret;
dev->base = map_sysmem(dev_read_addr(bus), sizeof(struct twi_regs));
if (!dev->base)
return -ENOMEM;
dev->speed = dev_read_u32_default(bus, "clock-frequency",
I2C_SPEED_FAST_RATE);
ret = clk_get_by_name(bus, "i2c", &clock);
if (ret < 0)
printf("%s: Can't get I2C clk: %d\n", __func__, ret);
else
dev->i2c_clk = clk_get_rate(&clock);
return 0;
}
static int adi_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
u32 chip_flags)
{
struct adi_i2c_dev *dev = dev_get_priv(bus);
u8 byte;
return adi_i2c_read(dev->base, chip_addr, NULL, 0, &byte, 1);
}
static int adi_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
{
struct adi_i2c_dev *dev = dev_get_priv(bus);
struct i2c_msg *dmsg, *omsg, dummy;
memset(&dummy, 0, sizeof(struct i2c_msg));
/*
* We expect either two messages (one with an offset and one with the
* actual data) or one message (just data)
*/
if (nmsgs > 2 || nmsgs == 0) {
debug("%s: Only one or two messages are supported.", __func__);
return -EINVAL;
}
omsg = nmsgs == 1 ? &dummy : msg;
dmsg = nmsgs == 1 ? msg : msg + 1;
if (dmsg->flags & I2C_M_RD)
return adi_i2c_read(dev->base, dmsg->addr, omsg->buf, omsg->len,
dmsg->buf, dmsg->len);
else
return adi_i2c_write(dev->base, dmsg->addr, omsg->buf, omsg->len,
dmsg->buf, dmsg->len);
}
int adi_i2c_probe(struct udevice *bus)
{
struct adi_i2c_dev *dev = dev_get_priv(bus);
struct twi_regs *twi = dev->base;
u16 prescale = ((dev->i2c_clk / 1000 / 1000 + 5) / 10) & 0x7F;
/* Set TWI internal clock as 10MHz */
iowrite16(prescale, &twi->control);
/* Set TWI interface clock as specified */
adi_i2c_set_bus_speed(bus, dev->speed);
/* Enable it */
iowrite16(TWI_ENA | prescale, &twi->control);
return 0;
}
static const struct dm_i2c_ops adi_i2c_ops = {
.xfer = adi_i2c_xfer,
.probe_chip = adi_i2c_probe_chip,
.set_bus_speed = adi_i2c_set_bus_speed,
};
static const struct udevice_id adi_i2c_ids[] = {
{ .compatible = "adi-i2c", },
{ /* sentinel */ }
};
U_BOOT_DRIVER(i2c_adi) = {
.name = "i2c_adi",
.id = UCLASS_I2C,
.of_match = adi_i2c_ids,
.probe = adi_i2c_probe,
.of_to_plat = adi_i2c_of_to_plat,
.priv_auto = sizeof(struct adi_i2c_dev),
.ops = &adi_i2c_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View file

@ -292,6 +292,15 @@ config MMC_DW_ROCKCHIP
SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well
as removeable SD and micro-SD cards.
config MMC_SDHCI_ADI
bool "ADI SD/MMC controller support"
depends on ARCH_SC5XX
depends on DM_MMC && OF_CONTROL
depends on MMC_SDHCI && MMC_SDHCI_ADMA
help
This enables support for the SD/MMC controller included in some Analog
Devices SC5XX Socs.
config MMC_DW_SOCFPGA
bool "SOCFPGA specific extensions for Synopsys DW Memory Card Interface"
depends on ARCH_SOCFPGA

View file

@ -70,6 +70,7 @@ obj-$(CONFIG_MMC_SDHCI_MV) += mv_sdhci.o
obj-$(CONFIG_MMC_SDHCI_NPCM) += npcm_sdhci.o
obj-$(CONFIG_MMC_SDHCI_PIC32) += pic32_sdhci.o
obj-$(CONFIG_MMC_SDHCI_ROCKCHIP) += rockchip_sdhci.o
obj-$(CONFIG_MMC_SDHCI_ADI) += adi_sdhci.o
obj-$(CONFIG_MMC_SDHCI_S5P) += s5p_sdhci.o
obj-$(CONFIG_MMC_SDHCI_SNPS) += snps_sdhci.o
obj-$(CONFIG_MMC_SDHCI_STI) += sti_sdhci.o

148
drivers/mmc/adi_sdhci.c Normal file
View file

@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
* Contact: Greg Malysa <greg.malysa@timesys.com>
*
* Based on Rockchip's sdhci.c file
*/
#include <clk.h>
#include <dm.h>
#include <malloc.h>
#include <sdhci.h>
#include <asm/cache.h>
/* 400KHz is max freq for card ID etc. Use that as min */
#define EMMC_MIN_FREQ 400000
/* Check if an operation crossed a boundary of size ADMA_BOUNDARY_ALIGN */
#define ADMA_BOUNDARY_ALGN SZ_128M
#define BOUNDARY_OK(addr, len) \
(((addr) | (ADMA_BOUNDARY_ALGN - 1)) == (((addr) + (len) - 1) | \
(ADMA_BOUNDARY_ALGN - 1)))
/* We split a descriptor for every crossing of the ADMA alignment boundary,
* so we need an additional descriptor for every expected crossing.
* As I understand it, the max expected transaction size is:
* CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN
*
* With the way the SDHCI-ADMA driver is implemented, if ADMA_MAX_LEN was a
* clean power of two, we'd only ever need +1 descriptor as the first
* descriptor that got split would then bring the remaining DMA
* destination addresses into alignment. Unfortunately, it's currently
* hardcoded to a non-power-of-two value.
*
* If that ever becomes parameterized, ADMA max length can be set to
* 0x10000, and set this to 1.
*/
#define ADMA_POTENTIAL_CROSSINGS \
DIV_ROUND_UP((CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN), \
ADMA_BOUNDARY_ALGN)
/* +1 descriptor for each crossing.
*/
#define ADMA_TABLE_EXTRA_SZ (ADMA_POTENTIAL_CROSSINGS * ADMA_DESC_LEN)
struct adi_sdhc_plat {
struct mmc_config cfg;
struct mmc mmc;
};
void adi_dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len, bool end)
{
int tmplen, offset;
if (likely(!len || BOUNDARY_OK(addr, len))) {
sdhci_adma_write_desc(host, desc, addr, len, end);
return;
}
offset = addr & (ADMA_BOUNDARY_ALGN - 1);
tmplen = ADMA_BOUNDARY_ALGN - offset;
sdhci_adma_write_desc(host, desc, addr, tmplen, false);
addr += tmplen;
len -= tmplen;
sdhci_adma_write_desc(host, desc, addr, len, end);
}
struct sdhci_ops adi_dwcmshc_sdhci_ops = {
.adma_write_desc = adi_dwcmshc_adma_write_desc,
};
static int adi_dwcmshc_sdhci_probe(struct udevice *dev)
{
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct adi_sdhc_plat *plat = dev_get_plat(dev);
struct sdhci_host *host = dev_get_priv(dev);
int max_frequency, ret;
struct clk clk;
max_frequency = dev_read_u32_default(dev, "max-frequency", 0);
ret = clk_get_by_index(dev, 0, &clk);
host->quirks = 0;
host->max_clk = max_frequency;
/*
* The sdhci-driver only supports 4bit and 8bit, as sdhci_setup_cfg
* doesn't allow us to clear MMC_MODE_4BIT. Consequently, we don't
* check for other bus-width values.
*/
if (host->bus_width == 8)
host->host_caps |= MMC_MODE_8BIT;
host->mmc = &plat->mmc;
host->mmc->priv = host;
host->mmc->dev = dev;
upriv->mmc = host->mmc;
host->ops = &adi_dwcmshc_sdhci_ops;
host->adma_desc_table = memalign(ARCH_DMA_MINALIGN,
ADMA_TABLE_SZ + ADMA_TABLE_EXTRA_SZ);
host->adma_addr = virt_to_phys(host->adma_desc_table);
ret = sdhci_setup_cfg(&plat->cfg, host, 0, EMMC_MIN_FREQ);
if (ret)
return ret;
return sdhci_probe(dev);
}
static int adi_dwcmshc_sdhci_of_to_plat(struct udevice *dev)
{
struct sdhci_host *host = dev_get_priv(dev);
host->name = dev->name;
host->ioaddr = dev_read_addr_ptr(dev);
host->bus_width = dev_read_u32_default(dev, "bus-width", 4);
return 0;
}
static int adi_sdhci_bind(struct udevice *dev)
{
struct adi_sdhc_plat *plat = dev_get_plat(dev);
return sdhci_bind(dev, &plat->mmc, &plat->cfg);
}
static const struct udevice_id adi_dwcmshc_sdhci_ids[] = {
{ .compatible = "adi,dwc-sdhci" },
{ }
};
U_BOOT_DRIVER(adi_dwcmshc_sdhci_drv) = {
.name = "adi_sdhci",
.id = UCLASS_MMC,
.of_match = adi_dwcmshc_sdhci_ids,
.of_to_plat = adi_dwcmshc_sdhci_of_to_plat,
.ops = &sdhci_ops,
.bind = adi_sdhci_bind,
.probe = adi_dwcmshc_sdhci_probe,
.priv_auto = sizeof(struct sdhci_host),
.plat_auto = sizeof(struct adi_sdhc_plat),
};

View file

@ -237,6 +237,13 @@ config DWC_ETH_QOS
Of Service) IP block. The IP supports many options for bus type,
clocking/reset structure, and feature list.
config DWC_ETH_QOS_ADI
bool "Synopsys DWC Ethernet QOS device support for ADI SC59x-64 parts"
depends on DWC_ETH_QOS
help
The Synopsis Designware Ethernet QoS IP block with the specific
configuration used in the ADI ADSP-SC59X 64 bit SoCs
config DWC_ETH_QOS_IMX
bool "Synopsys DWC Ethernet QOS device support for IMX"
depends on DWC_ETH_QOS

View file

@ -19,6 +19,7 @@ obj-$(CONFIG_DM_ETH_PHY) += eth-phy-uclass.o
obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o
obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o
obj-$(CONFIG_DWC_ETH_QOS_ADI) += dwc_eth_qos_adi.o
obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o
obj-$(CONFIG_DWC_ETH_QOS_INTEL) += dwc_eth_qos_intel.o
obj-$(CONFIG_DWC_ETH_QOS_ROCKCHIP) += dwc_eth_qos_rockchip.o

View file

@ -1631,6 +1631,12 @@ static const struct udevice_id eqos_ids[] = {
.compatible = "starfive,jh7110-dwmac",
.data = (ulong)&eqos_jh7110_config
},
#endif
#if IS_ENABLED(CONFIG_DWC_ETH_QOS_ADI)
{
.compatible = "adi,sc59x-dwmac-eqos",
.data = (ulong)&eqos_adi_config
},
#endif
{ }
};

View file

@ -87,6 +87,7 @@ struct eqos_mac_regs {
#define EQOS_MAC_MDIO_ADDRESS_CR_MASK GENMASK(11, 8)
#define EQOS_MAC_MDIO_ADDRESS_CR_100_150 1
#define EQOS_MAC_MDIO_ADDRESS_CR_20_35 2
#define EQOS_MAC_MDIO_ADDRESS_CR_150_250 4
#define EQOS_MAC_MDIO_ADDRESS_CR_250_300 5
#define EQOS_MAC_MDIO_ADDRESS_SKAP BIT(4)
#define EQOS_MAC_MDIO_ADDRESS_GOC_MASK GENMASK(3, 2)
@ -301,3 +302,4 @@ extern struct eqos_config eqos_qcom_config;
extern struct eqos_config eqos_stm32mp13_config;
extern struct eqos_config eqos_stm32mp15_config;
extern struct eqos_config eqos_jh7110_config;
extern struct eqos_config eqos_adi_config;

View file

@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0
/**
* (C) Copyright 2024 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Author: Greg Malysa <greg.malysa@timesys.com>
* Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
*/
#include <clk.h>
#include <dm.h>
#include <net.h>
#include <phy.h>
#include <reset.h>
#include <linux/io.h>
#include <asm/arch-adi/sc5xx/sc5xx.h>
#include "dwc_eth_qos.h"
static int eqos_start_resets_adi(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
/*
* Settings need to latch with the DMA reset below. Currently only
* rgmii is supported but other phy interfaces may be supported in
* the future
*/
sc5xx_enable_rgmii();
setbits_32(&eqos->dma_regs->mode, EQOS_DMA_MODE_SWR);
return 0;
}
static int eqos_probe_resources_adi(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
phy_interface_t interface;
int ret;
ret = eqos_get_base_addr_dt(dev);
if (ret) {
pr_err("eqos_get_base_addr_dt failed: %d\n", ret);
return ret;
}
interface = eqos->config->interface(dev);
if (interface == PHY_INTERFACE_MODE_NA) {
pr_err("Invalid PHY interface\n");
return -EINVAL;
}
return 0;
}
/**
* rgmii tx clock rate is set to 125 MHz regardless of phy mode, and
* by default the internal clock is always connected to 125 MHz. According
* to the HRM it is invalid for this clock to have any other speed, so
* the hardware won't work anyway if this is wrong.
*/
static ulong eqos_get_tick_clk_rate_adi(struct udevice *dev)
{
return 125 * 1000000;
}
static int eqos_get_enetaddr_adi(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_plat(dev);
return eth_env_get_enetaddr("ethaddr", pdata->enetaddr);
}
static struct eqos_ops eqos_adi_ops = {
.eqos_inval_desc = eqos_inval_desc_generic,
.eqos_flush_desc = eqos_flush_desc_generic,
.eqos_inval_buffer = eqos_inval_buffer_generic,
.eqos_flush_buffer = eqos_flush_buffer_generic,
.eqos_probe_resources = eqos_probe_resources_adi,
.eqos_remove_resources = eqos_null_ops,
.eqos_start_resets = eqos_start_resets_adi,
.eqos_stop_resets = eqos_null_ops,
.eqos_start_clks = eqos_null_ops,
.eqos_stop_clks = eqos_null_ops,
.eqos_calibrate_pads = eqos_null_ops,
.eqos_disable_calibration = eqos_null_ops,
.eqos_set_tx_clk_speed = eqos_null_ops,
.eqos_get_enetaddr = eqos_get_enetaddr_adi,
.eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_adi,
};
struct eqos_config __maybe_unused eqos_adi_config = {
.reg_access_always_ok = true,
.mdio_wait = 20,
.swr_wait = 50,
.config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
.config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_150_250,
.axi_bus_width = EQOS_AXI_WIDTH_32,
.interface = dev_read_phy_mode,
.ops = &eqos_adi_ops,
};

View file

@ -178,6 +178,14 @@ config PINCTRL_APPLE
both the GPIO definitions and pin control functions for each
available multiplex function.
config PINCTRL_ADI
bool "ADI pinctrl driver"
depends on DM && ARCH_SC5XX
help
This driver enables pinctrl support on SC5xx processors. This
driver covers only the pin configuration functionality, and
GPIO functionality is contained in the separate GPIO driver.
config PINCTRL_AR933X
bool "QCA/Athores ar933x pin control driver"
depends on DM && SOC_AR933X

View file

@ -3,6 +3,7 @@
obj-y += pinctrl-uclass.o
obj-$(CONFIG_$(XPL_)PINCTRL_GENERIC) += pinctrl-generic.o
obj-$(CONFIG_PINCTRL_ADI) += pinctrl-adi-adsp.o
obj-$(CONFIG_PINCTRL_APPLE) += pinctrl-apple.o
obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o
obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o

View file

@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Author: Greg Malysa <greg.malysa@timesys.com>
* Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
*
* dm pinctrl implementation for ADI ADSP SoCs
*
*/
#include <dm.h>
#include <dm/pinctrl.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/io.h>
#define ADSP_PORT_MMIO_SIZE 0x80
#define ADSP_PORT_PIN_SIZE 16
#define ADSP_PORT_PORT_MUX_BITS 2
#define ADSP_PORT_PORT_MUX_MASK 0x03
#define ADSP_PINCTRL_FUNCTION_COUNT 4
#define ADSP_PORT_REG_FER 0x00
#define ADSP_PORT_REG_FER_SET 0x04
#define ADSP_PORT_REG_FER_CLEAR 0x08
#define ADSP_PORT_REG_DATA 0x0c
#define ADSP_PORT_REG_DATA_SET 0x10
#define ADSP_PORT_REG_DATA_CLEAR 0x14
#define ADSP_PORT_REG_DIR 0x18
#define ADSP_PORT_REG_DIR_SET 0x1c
#define ADSP_PORT_REG_DIR_CLEAR 0x20
#define ADSP_PORT_REG_INEN 0x24
#define ADSP_PORT_REG_INEN_SET 0x28
#define ADSP_PORT_REG_INEN_CLEAR 0x2c
#define ADSP_PORT_REG_PORT_MUX 0x30
#define ADSP_PORT_REG_DATA_TGL 0x34
#define ADSP_PORT_REG_POLAR 0x38
#define ADSP_PORT_REG_POLAR_SET 0x3c
#define ADSP_PORT_REG_POLAR_CLEAR 0x40
#define ADSP_PORT_REG_LOCK 0x44
#define ADSP_PORT_REG_TRIG_TGL 0x48
struct adsp_pinctrl_priv {
void __iomem *base;
int npins;
char pinbuf[16];
};
static u32 get_port(unsigned int pin)
{
return pin / ADSP_PORT_PIN_SIZE;
}
static u32 get_offset(unsigned int pin)
{
return pin % ADSP_PORT_PIN_SIZE;
}
static int adsp_pinctrl_pinmux_set(struct udevice *udev, unsigned int pin, unsigned int func)
{
struct adsp_pinctrl_priv *priv = dev_get_priv(udev);
void __iomem *portbase;
u32 port, offset;
u32 val;
if (pin >= priv->npins)
return -ENODEV;
if (func >= ADSP_PINCTRL_FUNCTION_COUNT)
return -EINVAL;
port = get_port(pin);
offset = get_offset(pin);
portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
val = ioread32(portbase + ADSP_PORT_REG_PORT_MUX);
val &= ~(ADSP_PORT_PORT_MUX_MASK << (ADSP_PORT_PORT_MUX_BITS * offset));
val |= func << (ADSP_PORT_PORT_MUX_BITS * offset);
iowrite32(val, portbase + ADSP_PORT_REG_PORT_MUX);
iowrite32(BIT(offset), portbase + ADSP_PORT_REG_FER_SET);
return 0;
}
static int adsp_pinctrl_set_state(struct udevice *udev, struct udevice *config)
{
const struct fdt_property *pinlist;
int length = 0;
int ret, i;
u32 pin, function;
pinlist = dev_read_prop(config, "adi,pins", &length);
if (!pinlist) {
dev_err(udev, "missing adi,pins property in pinctrl config node\n");
return -EINVAL;
}
if (length % (sizeof(uint32_t) * 2)) {
dev_err(udev, "adi,pins property must be a multiple of two uint32_ts\n");
return -EINVAL;
}
for (i = 0; i < length / sizeof(uint32_t); i += 2) {
ret = dev_read_u32_index(config, "adi,pins", i, &pin);
if (ret)
return ret;
ret = dev_read_u32_index(config, "adi,pins", i + 1, &function);
if (ret)
return ret;
ret = adsp_pinctrl_pinmux_set(udev, pin, function);
if (ret)
return ret;
}
return 0;
}
const struct pinctrl_ops adsp_pinctrl_ops = {
.set_state = adsp_pinctrl_set_state,
};
static int adsp_pinctrl_probe(struct udevice *udev)
{
struct adsp_pinctrl_priv *priv = dev_get_priv(udev);
priv->base = dev_read_addr_ptr(udev);
priv->npins = dev_read_u32_default(udev, "adi,npins", 0);
if (!priv->base) {
dev_err(udev, "Missing or invalid pinctrl base address\n");
return -ENOENT;
}
if (!priv->npins) {
dev_err(udev, "Missing adi,npins property!\n");
return -ENOENT;
}
return 0;
}
static const struct udevice_id adsp_pinctrl_match[] = {
{ .compatible = "adi,adsp-pinctrl" },
{ },
};
U_BOOT_DRIVER(adi_adsp_pinctrl) = {
.name = "adi_adsp_pinctrl",
.id = UCLASS_PINCTRL,
.of_match = adsp_pinctrl_match,
.probe = adsp_pinctrl_probe,
.priv_auto = sizeof(struct adsp_pinctrl_priv),
.ops = &adsp_pinctrl_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View file

@ -13,6 +13,7 @@ config REMOTEPROC
depends on DM
# Please keep the configuration alphabetically sorted.
config K3_SYSTEM_CONTROLLER
bool "Support for TI' K3 System Controller"
select REMOTEPROC
@ -22,6 +23,16 @@ config K3_SYSTEM_CONTROLLER
help
Say 'y' here to add support for TI' K3 System Controller.
config REMOTEPROC_ADI_SC5XX
bool "Support for ADI SC5xx SHARC cores"
select REMOTEPROC
depends on DM
depends on ARCH_SC5XX
depends on SYSCON
help
Say 'y' here to add support for loading code onto SHARC cores in
an ADSP-SC5xx SoC from Analog Devices
config REMOTEPROC_RENESAS_APMU
bool "Support for Renesas R-Car Gen4 APMU start of CR52 processor"
select REMOTEPROC

View file

@ -8,6 +8,7 @@ obj-$(CONFIG_$(XPL_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o
# Remote proc drivers - Please keep this list alphabetically sorted.
obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o
obj-$(CONFIG_REMOTEPROC_ADI_SC5XX) += adi_sc5xx_rproc.o
obj-$(CONFIG_REMOTEPROC_RENESAS_APMU) += renesas_apmu.o
obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o
obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o

View file

@ -0,0 +1,277 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
* Contact: Greg Malysa <greg.malysa@timesys.com>
*
* Analog Devices SC5xx remoteproc driver for loading code onto SHARC cores
*/
#include <dm.h>
#include <regmap.h>
#include <remoteproc.h>
#include <syscon.h>
#include <dm/device_compat.h>
#include <linux/delay.h>
#include <linux/io.h>
/* Register offsets */
#ifdef CONFIG_SC58X
#define ADI_RCU_REG_CTL 0x00
#define ADI_RCU_REG_STAT 0x04
#define ADI_RCU_REG_CRCTL 0x08
#define ADI_RCU_REG_CRSTAT 0x0c
#define ADI_RCU_REG_SIDIS 0x10
#define ADI_RCU_REG_SISTAT 0x14
#define ADI_RCU_REG_BCODE 0x1c
#define ADI_RCU_REG_SVECT0 0x20
#define ADI_RCU_REG_SVECT1 0x24
#define ADI_RCU_REG_SVECT2 0x28
#define ADI_RCU_REG_MSG 0x60
#define ADI_RCU_REG_MSG_SET 0x64
#define ADI_RCU_REG_MSG_CLR 0x68
#else
#define ADI_RCU_REG_CTL 0x00
#define ADI_RCU_REG_STAT 0x04
#define ADI_RCU_REG_CRCTL 0x08
#define ADI_RCU_REG_CRSTAT 0x0c
#define ADI_RCU_REG_SRRQSTAT 0x18
#define ADI_RCU_REG_SIDIS 0x1c
#define ADI_RCU_REG_SISTAT 0x20
#define ADI_RCU_REG_SVECT_LCK 0x24
#define ADI_RCU_REG_BCODE 0x28
#define ADI_RCU_REG_SVECT0 0x2c
#define ADI_RCU_REG_SVECT1 0x30
#define ADI_RCU_REG_SVECT2 0x34
#define ADI_RCU_REG_MSG 0x6c
#define ADI_RCU_REG_MSG_SET 0x70
#define ADI_RCU_REG_MSG_CLR 0x74
#endif /* CONFIG_SC58X */
/* Register bit definitions */
#define ADI_RCU_CTL_SYSRST BIT(0)
/* Bit values for the RCU0_MSG register */
#define RCU0_MSG_C0IDLE 0x00000100 /* Core 0 Idle */
#define RCU0_MSG_C1IDLE 0x00000200 /* Core 1 Idle */
#define RCU0_MSG_C2IDLE 0x00000400 /* Core 2 Idle */
#define RCU0_MSG_CRR0 0x00001000 /* Core 0 reset request */
#define RCU0_MSG_CRR1 0x00002000 /* Core 1 reset request */
#define RCU0_MSG_CRR2 0x00004000 /* Core 2 reset request */
#define RCU0_MSG_C1ACTIVATE 0x00080000 /* Core 1 Activated */
#define RCU0_MSG_C2ACTIVATE 0x00100000 /* Core 2 Activated */
struct sc5xx_rproc_data {
/* Address to load to svect when rebooting core */
u32 load_addr;
/* RCU parameters */
struct regmap *rcu;
u32 svect_offset;
u32 coreid;
};
struct block_code_flag {
u32 bcode:4, /* 0-3 */
bflag_save:1, /* 4 */
bflag_aux:1, /* 5 */
breserved:1, /* 6 */
bflag_forward:1, /* 7 */
bflag_fill:1, /* 8 */
bflag_quickboot:1, /* 9 */
bflag_callback:1, /* 10 */
bflag_init:1, /* 11 */
bflag_ignore:1, /* 12 */
bflag_indirect:1, /* 13 */
bflag_first:1, /* 14 */
bflag_final:1, /* 15 */
bhdrchk:8, /* 16-23 */
bhdrsign:8; /* 0xAD, 0xAC or 0xAB */
};
struct ldr_hdr {
struct block_code_flag bcode_flag;
u32 target_addr;
u32 byte_count;
u32 argument;
};
static int is_final(struct ldr_hdr *hdr)
{
return hdr->bcode_flag.bflag_final;
}
static int is_empty(struct ldr_hdr *hdr)
{
return hdr->bcode_flag.bflag_ignore || (hdr->byte_count == 0);
}
static int adi_valid_firmware(struct ldr_hdr *adi_ldr_hdr)
{
if (!adi_ldr_hdr->byte_count &&
(adi_ldr_hdr->bcode_flag.bhdrsign == 0xAD ||
adi_ldr_hdr->bcode_flag.bhdrsign == 0xAC ||
adi_ldr_hdr->bcode_flag.bhdrsign == 0xAB))
return 1;
return 0;
}
static int sharc_load(struct udevice *dev, ulong addr, ulong size)
{
struct sc5xx_rproc_data *priv = dev_get_priv(dev);
size_t offset;
u8 *buf = (u8 *)addr;
struct ldr_hdr *ldr = (struct ldr_hdr *)addr;
struct ldr_hdr *block_hdr;
struct ldr_hdr *next_hdr;
if (!adi_valid_firmware(ldr)) {
dev_err(dev, "Firmware at 0x%lx does not appear to be an LDR image\n", addr);
dev_err(dev, "Note: Signed firmware is not currently supported\n");
return -EINVAL;
}
do {
block_hdr = (struct ldr_hdr *)buf;
offset = sizeof(struct ldr_hdr) + (block_hdr->bcode_flag.bflag_fill ?
0 : block_hdr->byte_count);
next_hdr = (struct ldr_hdr *)(buf + offset);
if (block_hdr->bcode_flag.bflag_first)
priv->load_addr = (unsigned long)block_hdr->target_addr;
if (!is_empty(block_hdr)) {
if (block_hdr->bcode_flag.bflag_fill) {
memset_io((void *)(phys_addr_t)block_hdr->target_addr,
block_hdr->argument,
block_hdr->byte_count);
} else {
memcpy_toio((void *)(phys_addr_t)block_hdr->target_addr,
buf + sizeof(struct ldr_hdr),
block_hdr->byte_count);
}
}
if (is_final(block_hdr))
break;
buf += offset;
} while (1);
return 0;
}
static void sharc_reset(struct sc5xx_rproc_data *priv)
{
u32 coreid = priv->coreid;
u32 val;
/* First put core in reset.
* Clear CRSTAT bit for given coreid.
*/
regmap_write(priv->rcu, ADI_RCU_REG_CRSTAT, 1 << coreid);
/* Set SIDIS to disable the system interface */
regmap_read(priv->rcu, ADI_RCU_REG_SIDIS, &val);
regmap_write(priv->rcu, ADI_RCU_REG_SIDIS, val | (1 << (coreid - 1)));
/*
* Wait for access to coreX have been disabled and all the pending
* transactions have completed
*/
udelay(50);
/* Set CRCTL bit to put core in reset */
regmap_read(priv->rcu, ADI_RCU_REG_CRCTL, &val);
regmap_write(priv->rcu, ADI_RCU_REG_CRCTL, val | (1 << coreid));
/* Poll until Core is in reset */
while (!(regmap_read(priv->rcu, ADI_RCU_REG_CRSTAT, &val), val & (1 << coreid)))
;
/* Clear SIDIS to reenable the system interface */
regmap_read(priv->rcu, ADI_RCU_REG_SIDIS, &val);
regmap_write(priv->rcu, ADI_RCU_REG_SIDIS, val & ~(1 << (coreid - 1)));
udelay(50);
/* Take Core out of reset */
regmap_read(priv->rcu, ADI_RCU_REG_CRCTL, &val);
regmap_write(priv->rcu, ADI_RCU_REG_CRCTL, val & ~(1 << coreid));
/* Wait for done */
udelay(50);
}
static int sharc_start(struct udevice *dev)
{
struct sc5xx_rproc_data *priv = dev_get_priv(dev);
/* Write load address to appropriate SVECT for core */
regmap_write(priv->rcu, priv->svect_offset, priv->load_addr);
sharc_reset(priv);
/* Clear the IDLE bit when start the SHARC core */
regmap_write(priv->rcu, ADI_RCU_REG_MSG_CLR, RCU0_MSG_C0IDLE << priv->coreid);
/* Notify CCES */
regmap_write(priv->rcu, ADI_RCU_REG_MSG_SET, RCU0_MSG_C1ACTIVATE << (priv->coreid - 1));
return 0;
}
static const struct dm_rproc_ops sc5xx_ops = {
.load = sharc_load,
.start = sharc_start,
};
static int sc5xx_probe(struct udevice *dev)
{
struct sc5xx_rproc_data *priv = dev_get_priv(dev);
u32 coreid;
if (dev_read_u32(dev, "coreid", &coreid)) {
dev_err(dev, "Missing property coreid\n");
return -ENOENT;
}
priv->coreid = coreid;
switch (coreid) {
case 1:
priv->svect_offset = ADI_RCU_REG_SVECT1;
break;
case 2:
priv->svect_offset = ADI_RCU_REG_SVECT2;
break;
default:
dev_err(dev, "Invalid value %d for coreid, must be 1 or 2\n", coreid);
return -EINVAL;
}
priv->rcu = syscon_regmap_lookup_by_phandle(dev, "adi,rcu");
if (IS_ERR(priv->rcu))
return PTR_ERR(priv->rcu);
dev_err(dev, "sc5xx remoteproc core %d available\n", priv->coreid);
return 0;
}
static const struct udevice_id sc5xx_ids[] = {
{ .compatible = "adi,sc5xx-rproc" },
{ }
};
U_BOOT_DRIVER(adi_sc5xx_rproc) = {
.name = "adi_sc5xx_rproc",
.of_match = sc5xx_ids,
.id = UCLASS_REMOTEPROC,
.ops = &sc5xx_ops,
.probe = sc5xx_probe,
.priv_auto = sizeof(struct sc5xx_rproc_data),
.flags = 0,
};

View file

@ -52,6 +52,13 @@ config SPI_DIRMAP
if DM_SPI
config ADI_SPI3
bool "Enable ADI SPI Driver"
depends on ARCH_SC5XX
help
Enable the ADI (Analog Devices) SPI controller driver. This
driver enables the support for SC5XX spi controller.
config ALTERA_SPI
bool "Altera SPI driver"
help

View file

@ -19,6 +19,7 @@ obj-y += spi.o
obj-$(CONFIG_SPI_MEM) += spi-mem-nodm.o
endif
obj-$(CONFIG_ADI_SPI3) += adi_spi3.o
obj-$(CONFIG_ALTERA_SPI) += altera_spi.o
obj-$(CONFIG_APPLE_SPI) += apple_spi.o
obj-$(CONFIG_ATH79_SPI) += ath79_spi.o

679
drivers/spi/adi_spi3.c Normal file
View file

@ -0,0 +1,679 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Converted to driver model by Nathan Barrett-Morrison
*
* Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
* Contact: Greg Malysa <greg.malysa@timesys.com>
* Contact: Ian Roberts <ian.roberts@timesys.com>
* Contact: Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>
*
*/
#include <clk.h>
#include <dm.h>
#include <mapmem.h>
#include <spi.h>
#include <spi-mem.h>
#include <dm/device_compat.h>
#include <linux/io.h>
#define SPI_IDLE_VAL 0xff
#define MAX_CTRL_CS 7
/* SPI_CONTROL */
#define SPI_CTL_EN 0x00000001 /* Enable */
#define SPI_CTL_MSTR 0x00000002 /* Master/Slave */
#define SPI_CTL_PSSE 0x00000004 /* controls modf error in master mode */
#define SPI_CTL_ODM 0x00000008 /* Open Drain Mode */
#define SPI_CTL_CPHA 0x00000010 /* Clock Phase */
#define SPI_CTL_CPOL 0x00000020 /* Clock Polarity */
#define SPI_CTL_ASSEL 0x00000040 /* Slave Select Pin Control */
#define SPI_CTL_SELST 0x00000080 /* Slave Select Polarity in transfers */
#define SPI_CTL_EMISO 0x00000100 /*Enable MISO */
#define SPI_CTL_SIZE 0x00000600 /*Word Transfer Size */
#define SPI_CTL_SIZE08 0x00000000 /*SIZE: 8 bits */
#define SPI_CTL_SIZE16 0x00000200 /*SIZE: 16 bits */
#define SPI_CTL_SIZE32 0x00000400 /*SIZE: 32 bits */
#define SPI_CTL_LSBF 0x00001000 /*LSB First */
#define SPI_CTL_FCEN 0x00002000 /*Flow-Control Enable */
#define SPI_CTL_FCCH 0x00004000 /*Flow-Control Channel Selection */
#define SPI_CTL_FCPL 0x00008000 /*Flow-Control Polarity */
#define SPI_CTL_FCWM 0x00030000 /*Flow-Control Water-Mark */
#define SPI_CTL_FIFO0 0x00000000 /*FCWM: Tx empty or Rx Full */
#define SPI_CTL_FIFO1 0x00010000 /*FCWM: Tx empty or Rx full (>=75%) */
#define SPI_CTL_FIFO2 0x00020000 /*FCWM: Tx empty or Rx full (>=50%) */
#define SPI_CTL_FMODE 0x00040000 /*Fast-mode Enable */
#define SPI_CTL_MIOM 0x00300000 /*Multiple I/O Mode */
#define SPI_CTL_MIO_DIS 0x00000000 /*MIOM: Disable */
#define SPI_CTL_MIO_DUAL 0x00100000 /*MIOM: Enable DIOM (Dual I/O Mode) */
#define SPI_CTL_MIO_QUAD 0x00200000 /*MIOM: Enable QUAD (Quad SPI Mode) */
#define SPI_CTL_SOSI 0x00400000 /*Start on MOSI */
#define SPI_CTL_MMWEM 0x40000000 /*Start on MMWEM */
#define SPI_CTL_MMSE 0x80000000 /*Start on MMSE */
/* SPI_RX_CONTROL */
#define SPI_RXCTL_REN 0x00000001 /*Receive Channel Enable */
#define SPI_RXCTL_RTI 0x00000004 /*Receive Transfer Initiate */
#define SPI_RXCTL_RWCEN 0x00000008 /*Receive Word Counter Enable */
#define SPI_RXCTL_RDR 0x00000070 /*Receive Data Request */
#define SPI_RXCTL_RDR_DIS 0x00000000 /*RDR: Disabled */
#define SPI_RXCTL_RDR_NE 0x00000010 /*RDR: RFIFO not empty */
#define SPI_RXCTL_RDR_25 0x00000020 /*RDR: RFIFO 25% full */
#define SPI_RXCTL_RDR_50 0x00000030 /*RDR: RFIFO 50% full */
#define SPI_RXCTL_RDR_75 0x00000040 /*RDR: RFIFO 75% full */
#define SPI_RXCTL_RDR_FULL 0x00000050 /*RDR: RFIFO full */
#define SPI_RXCTL_RDO 0x00000100 /*Receive Data Over-Run */
#define SPI_RXCTL_RRWM 0x00003000 /*FIFO Regular Water-Mark */
#define SPI_RXCTL_RWM_0 0x00000000 /*RRWM: RFIFO Empty */
#define SPI_RXCTL_RWM_25 0x00001000 /*RRWM: RFIFO 25% full */
#define SPI_RXCTL_RWM_50 0x00002000 /*RRWM: RFIFO 50% full */
#define SPI_RXCTL_RWM_75 0x00003000 /*RRWM: RFIFO 75% full */
#define SPI_RXCTL_RUWM 0x00070000 /*FIFO Urgent Water-Mark */
#define SPI_RXCTL_UWM_DIS 0x00000000 /*RUWM: Disabled */
#define SPI_RXCTL_UWM_25 0x00010000 /*RUWM: RFIFO 25% full */
#define SPI_RXCTL_UWM_50 0x00020000 /*RUWM: RFIFO 50% full */
#define SPI_RXCTL_UWM_75 0x00030000 /*RUWM: RFIFO 75% full */
#define SPI_RXCTL_UWM_FULL 0x00040000 /*RUWM: RFIFO full */
/* SPI_TX_CONTROL */
#define SPI_TXCTL_TEN 0x00000001 /*Transmit Channel Enable */
#define SPI_TXCTL_TTI 0x00000004 /*Transmit Transfer Initiate */
#define SPI_TXCTL_TWCEN 0x00000008 /*Transmit Word Counter Enable */
#define SPI_TXCTL_TDR 0x00000070 /*Transmit Data Request */
#define SPI_TXCTL_TDR_DIS 0x00000000 /*TDR: Disabled */
#define SPI_TXCTL_TDR_NF 0x00000010 /*TDR: TFIFO not full */
#define SPI_TXCTL_TDR_25 0x00000020 /*TDR: TFIFO 25% empty */
#define SPI_TXCTL_TDR_50 0x00000030 /*TDR: TFIFO 50% empty */
#define SPI_TXCTL_TDR_75 0x00000040 /*TDR: TFIFO 75% empty */
#define SPI_TXCTL_TDR_EMPTY 0x00000050 /*TDR: TFIFO empty */
#define SPI_TXCTL_TDU 0x00000100 /*Transmit Data Under-Run */
#define SPI_TXCTL_TRWM 0x00003000 /*FIFO Regular Water-Mark */
#define SPI_TXCTL_RWM_FULL 0x00000000 /*TRWM: TFIFO full */
#define SPI_TXCTL_RWM_25 0x00001000 /*TRWM: TFIFO 25% empty */
#define SPI_TXCTL_RWM_50 0x00002000 /*TRWM: TFIFO 50% empty */
#define SPI_TXCTL_RWM_75 0x00003000 /*TRWM: TFIFO 75% empty */
#define SPI_TXCTL_TUWM 0x00070000 /*FIFO Urgent Water-Mark */
#define SPI_TXCTL_UWM_DIS 0x00000000 /*TUWM: Disabled */
#define SPI_TXCTL_UWM_25 0x00010000 /*TUWM: TFIFO 25% empty */
#define SPI_TXCTL_UWM_50 0x00020000 /*TUWM: TFIFO 50% empty */
#define SPI_TXCTL_UWM_75 0x00030000 /*TUWM: TFIFO 75% empty */
#define SPI_TXCTL_UWM_EMPTY 0x00040000 /*TUWM: TFIFO empty */
/* SPI_CLOCK */
#define SPI_CLK_BAUD 0x0000FFFF /*Baud Rate */
/* SPI_DELAY */
#define SPI_DLY_STOP 0x000000FF /*Transfer delay time */
#define SPI_DLY_LEADX 0x00000100 /*Extended (1 SCK) LEAD Control */
#define SPI_DLY_LAGX 0x00000200 /*Extended (1 SCK) LAG control */
/* SPI_SSEL */
#define SPI_SLVSEL_SSE1 0x00000002 /*SPISSEL1 Enable */
#define SPI_SLVSEL_SSE2 0x00000004 /*SPISSEL2 Enable */
#define SPI_SLVSEL_SSE3 0x00000008 /*SPISSEL3 Enable */
#define SPI_SLVSEL_SSE4 0x00000010 /*SPISSEL4 Enable */
#define SPI_SLVSEL_SSE5 0x00000020 /*SPISSEL5 Enable */
#define SPI_SLVSEL_SSE6 0x00000040 /*SPISSEL6 Enable */
#define SPI_SLVSEL_SSE7 0x00000080 /*SPISSEL7 Enable */
#define SPI_SLVSEL_SSEL1 0x00000200 /*SPISSEL1 Value */
#define SPI_SLVSEL_SSEL2 0x00000400 /*SPISSEL2 Value */
#define SPI_SLVSEL_SSEL3 0x00000800 /*SPISSEL3 Value */
#define SPI_SLVSEL_SSEL4 0x00001000 /*SPISSEL4 Value */
#define SPI_SLVSEL_SSEL5 0x00002000 /*SPISSEL5 Value */
#define SPI_SLVSEL_SSEL6 0x00004000 /*SPISSEL6 Value */
#define SPI_SLVSEL_SSEL7 0x00008000 /*SPISSEL7 Value */
/* SPI_RWC */
#define SPI_RWC_VALUE 0x0000FFFF /*Received Word-Count */
/* SPI_RWCR */
#define SPI_RWCR_VALUE 0x0000FFFF /*Received Word-Count Reload */
/* SPI_TWC */
#define SPI_TWC_VALUE 0x0000FFFF /*Transmitted Word-Count */
/* SPI_TWCR */
#define SPI_TWCR_VALUE 0x0000FFFF /*Transmitted Word-Count Reload */
/* SPI_IMASK */
#define SPI_IMSK_RUWM 0x00000002 /*Receive Water-Mark Interrupt Mask */
#define SPI_IMSK_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */
#define SPI_IMSK_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */
#define SPI_IMSK_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */
#define SPI_IMSK_TCM 0x00000040 /*Transmit Collision Interrupt Mask */
#define SPI_IMSK_MFM 0x00000080 /*Mode Fault Interrupt Mask */
#define SPI_IMSK_RSM 0x00000100 /*Receive Start Interrupt Mask */
#define SPI_IMSK_TSM 0x00000200 /*Transmit Start Interrupt Mask */
#define SPI_IMSK_RFM 0x00000400 /*Receive Finish Interrupt Mask */
#define SPI_IMSK_TFM 0x00000800 /*Transmit Finish Interrupt Mask */
/* SPI_IMASKCL */
#define SPI_IMSK_CLR_RUW 0x00000002 /*Receive Water-Mark Interrupt Mask */
#define SPI_IMSK_CLR_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */
#define SPI_IMSK_CLR_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */
#define SPI_IMSK_CLR_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */
#define SPI_IMSK_CLR_TCM 0x00000040 /*Transmit Collision Interrupt Mask */
#define SPI_IMSK_CLR_MFM 0x00000080 /*Mode Fault Interrupt Mask */
#define SPI_IMSK_CLR_RSM 0x00000100 /*Receive Start Interrupt Mask */
#define SPI_IMSK_CLR_TSM 0x00000200 /*Transmit Start Interrupt Mask */
#define SPI_IMSK_CLR_RFM 0x00000400 /*Receive Finish Interrupt Mask */
#define SPI_IMSK_CLR_TFM 0x00000800 /*Transmit Finish Interrupt Mask */
/* SPI_IMASKST */
#define SPI_IMSK_SET_RUWM 0x00000002 /*Receive Water-Mark Interrupt Mask */
#define SPI_IMSK_SET_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */
#define SPI_IMSK_SET_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */
#define SPI_IMSK_SET_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */
#define SPI_IMSK_SET_TCM 0x00000040 /*Transmit Collision Interrupt Mask */
#define SPI_IMSK_SET_MFM 0x00000080 /*Mode Fault Interrupt Mask */
#define SPI_IMSK_SET_RSM 0x00000100 /*Receive Start Interrupt Mask */
#define SPI_IMSK_SET_TSM 0x00000200 /*Transmit Start Interrupt Mask */
#define SPI_IMSK_SET_RFM 0x00000400 /*Receive Finish Interrupt Mask */
#define SPI_IMSK_SET_TFM 0x00000800 /*Transmit Finish Interrupt Mask */
/* SPI_STATUS */
#define SPI_STAT_SPIF 0x00000001 /*SPI Finished */
#define SPI_STAT_RUWM 0x00000002 /*Receive Water-Mark Breached */
#define SPI_STAT_TUWM 0x00000004 /*Transmit Water-Mark Breached */
#define SPI_STAT_ROE 0x00000010 /*Receive Over-Run Indication */
#define SPI_STAT_TUE 0x00000020 /*Transmit Under-Run Indication */
#define SPI_STAT_TCE 0x00000040 /*Transmit Collision Indication */
#define SPI_STAT_MODF 0x00000080 /*Mode Fault Indication */
#define SPI_STAT_RS 0x00000100 /*Receive Start Indication */
#define SPI_STAT_TS 0x00000200 /*Transmit Start Indication */
#define SPI_STAT_RF 0x00000400 /*Receive Finish Indication */
#define SPI_STAT_TF 0x00000800 /*Transmit Finish Indication */
#define SPI_STAT_RFS 0x00007000 /*SPI_RFIFO status */
#define SPI_STAT_RFIFO_EMPTY 0x00000000 /*RFS: RFIFO Empty */
#define SPI_STAT_RFIFO_25 0x00001000 /*RFS: RFIFO 25% Full */
#define SPI_STAT_RFIFO_50 0x00002000 /*RFS: RFIFO 50% Full */
#define SPI_STAT_RFIFO_75 0x00003000 /*RFS: RFIFO 75% Full */
#define SPI_STAT_RFIFO_FULL 0x00004000 /*RFS: RFIFO Full */
#define SPI_STAT_TFS 0x00070000 /*SPI_TFIFO status */
#define SPI_STAT_TFIFO_FULL 0x00000000 /*TFS: TFIFO full */
#define SPI_STAT_TFIFO_25 0x00010000 /*TFS: TFIFO 25% empty */
#define SPI_STAT_TFIFO_50 0x00020000 /*TFS: TFIFO 50% empty */
#define SPI_STAT_TFIFO_75 0x00030000 /*TFS: TFIFO 75% empty */
#define SPI_STAT_TFIFO_EMPTY 0x00040000 /*TFS: TFIFO empty */
#define SPI_STAT_FCS 0x00100000 /*Flow-Control Stall Indication */
#define SPI_STAT_RFE 0x00400000 /*SPI_RFIFO Empty */
#define SPI_STAT_TFF 0x00800000 /*SPI_TFIFO Full */
/* SPI_ILAT */
#define SPI_ILAT_RUWMI 0x00000002 /*Receive Water Mark Interrupt */
#define SPI_ILAT_TUWMI 0x00000004 /*Transmit Water Mark Interrupt */
#define SPI_ILAT_ROI 0x00000010 /*Receive Over-Run Indication */
#define SPI_ILAT_TUI 0x00000020 /*Transmit Under-Run Indication */
#define SPI_ILAT_TCI 0x00000040 /*Transmit Collision Indication */
#define SPI_ILAT_MFI 0x00000080 /*Mode Fault Indication */
#define SPI_ILAT_RSI 0x00000100 /*Receive Start Indication */
#define SPI_ILAT_TSI 0x00000200 /*Transmit Start Indication */
#define SPI_ILAT_RFI 0x00000400 /*Receive Finish Indication */
#define SPI_ILAT_TFI 0x00000800 /*Transmit Finish Indication */
/* SPI_ILATCL */
#define SPI_ILAT_CLR_RUWMI 0x00000002 /*Receive Water Mark Interrupt */
#define SPI_ILAT_CLR_TUWMI 0x00000004 /*Transmit Water Mark Interrupt */
#define SPI_ILAT_CLR_ROI 0x00000010 /*Receive Over-Run Indication */
#define SPI_ILAT_CLR_TUI 0x00000020 /*Transmit Under-Run Indication */
#define SPI_ILAT_CLR_TCI 0x00000040 /*Transmit Collision Indication */
#define SPI_ILAT_CLR_MFI 0x00000080 /*Mode Fault Indication */
#define SPI_ILAT_CLR_RSI 0x00000100 /*Receive Start Indication */
#define SPI_ILAT_CLR_TSI 0x00000200 /*Transmit Start Indication */
#define SPI_ILAT_CLR_RFI 0x00000400 /*Receive Finish Indication */
#define SPI_ILAT_CLR_TFI 0x00000800 /*Transmit Finish Indication */
/* SPI_MMRDH */
#define SPI_MMRDH_MERGE 0x04000000 /*Merge Enable */
#define SPI_MMRDH_DMY_SZ 0x00007000 /*Bytes of Dummy */
#define SPI_MMRDH_ADDR_PINS 0x00000800 /*Pins used for Address */
#define SPI_MMRDH_ADDR_SZ 0x00000700 /*Bytes of Read Address */
#define SPI_MMRDH_OPCODE 0x000000FF /*Read Opcode */
#define SPI_MMRDH_TRIDMY_OFF 24 /*Bytes of Dummy offset */
#define SPI_MMRDH_DMY_SZ_OFF 12 /*Bytes of Dummy offset */
#define SPI_MMRDH_ADDR_SZ_OFF 8 /*Bytes of Read Address offset */
#define BIT_SSEL_VAL(x) ((1 << 8) << (x)) /* Slave Select input value bit */
#define BIT_SSEL_EN(x) (1 << (x)) /* Slave Select enable bit*/
struct adi_spi_regs {
u32 revid;
u32 control;
u32 rx_control;
u32 tx_control;
u32 clock;
u32 delay;
u32 ssel;
u32 rwc;
u32 rwcr;
u32 twc;
u32 twcr;
u32 reserved0;
u32 emask;
u32 emaskcl;
u32 emaskst;
u32 reserved1;
u32 status;
u32 elat;
u32 elatcl;
u32 reserved2;
u32 rfifo;
u32 reserved3;
u32 tfifo;
u32 reserved4;
u32 mmrdh;
u32 mmtop;
};
struct adi_spi_platdata {
u32 max_hz;
u32 bus_num;
struct adi_spi_regs __iomem *regs;
};
struct adi_spi_priv {
u32 control;
u32 clock;
u32 bus_num;
u32 max_cs;
struct adi_spi_regs __iomem *regs;
};
/**
* By convention, this driver uses the same CS numbering that is used with the SSEL bit
* definitions (both here and in the TRM on which this is based), which are 1-indexed not
* 0-indexed. The valid CS range is therefore [1,max_cs], in contrast with other drivers
* where it is [0,max_cs-1].
*/
static int adi_spi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
{
struct adi_spi_priv *priv = dev_get_priv(bus);
if (cs == 0 || cs > priv->max_cs) {
dev_err(bus, "invalid chipselect %u\n", cs);
return -EINVAL;
}
return 0;
}
static int adi_spi_of_to_plat(struct udevice *bus)
{
struct adi_spi_platdata *plat = dev_get_plat(bus);
fdt_addr_t addr;
plat->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 500000);
plat->bus_num = dev_read_u32_default(bus, "bus-num", 0);
addr = dev_read_addr(bus);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
plat->regs = map_sysmem(addr, sizeof(*plat->regs));
return 0;
}
static int adi_spi_probe(struct udevice *bus)
{
struct adi_spi_platdata *plat = dev_get_plat(bus);
struct adi_spi_priv *priv = dev_get_priv(bus);
priv->bus_num = plat->bus_num;
priv->regs = plat->regs;
priv->max_cs = dev_read_u32_default(bus, "num-cs", MAX_CTRL_CS);
iowrite32(0x0, &plat->regs->control);
iowrite32(0x0, &plat->regs->rx_control);
iowrite32(0x0, &plat->regs->tx_control);
return 0;
}
static int adi_spi_remove(struct udevice *dev)
{
return -ENODEV;
}
static int adi_spi_claim_bus(struct udevice *dev)
{
struct adi_spi_priv *priv;
struct udevice *bus = dev->parent;
priv = dev_get_priv(bus);
debug("%s: control:%i clock:%i\n",
__func__, priv->control, priv->clock);
iowrite32(priv->control, &priv->regs->control);
iowrite32(priv->clock, &priv->regs->clock);
iowrite32(0x0, &priv->regs->delay);
return 0;
}
static int adi_spi_release_bus(struct udevice *dev)
{
struct adi_spi_priv *priv;
struct udevice *bus = dev->parent;
priv = dev_get_priv(bus);
debug("%s: control:%i clock:%i\n",
__func__, priv->control, priv->clock);
iowrite32(0x0, &priv->regs->rx_control);
iowrite32(0x0, &priv->regs->tx_control);
iowrite32(0x0, &priv->regs->control);
return 0;
}
void adi_spi_enable_ssel(struct adi_spi_priv *priv, int cs)
{
setbits_32(&priv->regs->ssel, BIT_SSEL_EN(cs));
}
void adi_spi_set_ssel(struct adi_spi_priv *priv, int cs, int high)
{
if (high)
setbits_32(&priv->regs->ssel, BIT_SSEL_VAL(cs));
else
clrbits_32(&priv->regs->ssel, BIT_SSEL_VAL(cs));
}
void adi_spi_cs_activate(struct adi_spi_priv *priv, struct dm_spi_slave_plat *slave_plat)
{
bool high = slave_plat->mode & SPI_CS_HIGH;
adi_spi_set_ssel(priv, slave_plat->cs[0], high);
adi_spi_enable_ssel(priv, slave_plat->cs[0]);
}
void adi_spi_cs_deactivate(struct adi_spi_priv *priv, struct dm_spi_slave_plat *slave_plat)
{
bool high = slave_plat->mode & SPI_CS_HIGH;
/* invert CS for matching SSEL to deactivate */
adi_spi_set_ssel(priv, slave_plat->cs[0], !high);
}
static void discard_rx_fifo_contents(struct adi_spi_regs *regs)
{
while (!(ioread32(&regs->status) & SPI_STAT_RFE))
ioread32(&regs->rfifo);
}
static int adi_spi_fifo_mio_xfer(struct adi_spi_priv *priv, const u8 *tx, u8 *rx,
uint bytes, uint32_t mio_mode)
{
u8 value;
/* switch current SPI transfer to mio SPI mode */
clrsetbits_32(&priv->regs->control, SPI_CTL_SOSI, mio_mode);
/*
* Data can only be transferred in one direction in multi-io SPI
* modes, trigger the transfer in respective direction.
*/
if (rx) {
iowrite32(0x0, &priv->regs->tx_control);
iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RTI, &priv->regs->rx_control);
while (bytes--) {
while (ioread32(&priv->regs->status) &
SPI_STAT_RFE)
if (ctrlc())
return -1;
value = ioread32(&priv->regs->rfifo);
*rx++ = value;
}
} else if (tx) {
iowrite32(0x0, &priv->regs->rx_control);
iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &priv->regs->tx_control);
while (bytes--) {
value = *tx++;
iowrite32(value, &priv->regs->tfifo);
while (ioread32(&priv->regs->status) &
SPI_STAT_TFF)
if (ctrlc())
return -1;
}
/* Wait till the tfifo is empty */
while ((ioread32(&priv->regs->status) & SPI_STAT_TFS) != SPI_STAT_TFIFO_EMPTY)
if (ctrlc())
return -1;
} else {
return -1;
}
return 0;
}
static int adi_spi_fifo_1x_xfer(struct adi_spi_priv *priv, const u8 *tx, u8 *rx,
uint bytes)
{
u8 value;
/*
* Set current SPI transfer in normal mode and trigger
* the bi-direction transfer by tx write operation.
*/
iowrite32(priv->control, &priv->regs->control);
iowrite32(SPI_RXCTL_REN, &priv->regs->rx_control);
iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &priv->regs->tx_control);
while (bytes--) {
value = (tx ? *tx++ : SPI_IDLE_VAL);
debug("%s: tx:%x ", __func__, value);
iowrite32(value, &priv->regs->tfifo);
while (ioread32(&priv->regs->status) & SPI_STAT_RFE)
if (ctrlc())
return -1;
value = ioread32(&priv->regs->rfifo);
if (rx)
*rx++ = value;
debug("rx:%x\n", value);
}
return 0;
}
static int adi_spi_fifo_xfer(struct adi_spi_priv *priv, int buswidth,
const u8 *tx, u8 *rx, uint bytes)
{
switch (buswidth) {
case 1:
return adi_spi_fifo_1x_xfer(priv, tx, rx, bytes);
case 2:
return adi_spi_fifo_mio_xfer(priv, tx, rx, bytes, SPI_CTL_MIO_DUAL);
case 4:
return adi_spi_fifo_mio_xfer(priv, tx, rx, bytes, SPI_CTL_MIO_QUAD);
default:
return -ENOTSUPP;
}
}
static int adi_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct adi_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
const u8 *tx = dout;
u8 *rx = din;
uint bytes = bitlen / 8;
int ret = 0;
debug("%s: bus_num:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
priv->bus_num, slave_plat->cs[0], bitlen, bytes, flags);
if (flags & SPI_XFER_BEGIN)
adi_spi_cs_activate(priv, slave_plat);
if (bitlen == 0)
goto done;
/* we can only do 8 bit transfers */
if (bitlen % 8) {
flags |= SPI_XFER_END;
goto done;
}
/* Discard invalid rx data and empty rfifo */
discard_rx_fifo_contents(priv->regs);
ret = adi_spi_fifo_1x_xfer(priv, tx, rx, bytes);
done:
if (flags & SPI_XFER_END)
adi_spi_cs_deactivate(priv, slave_plat);
return ret;
}
static int adi_spi_set_speed(struct udevice *bus, uint speed)
{
struct adi_spi_platdata *plat = dev_get_plat(bus);
struct adi_spi_priv *priv = dev_get_priv(bus);
int ret;
u32 clock, spi_base_clk;
struct clk spi_clk;
ret = clk_get_by_name(bus, "spi", &spi_clk);
if (ret < 0) {
dev_err(bus, "Can't get SPI clk: %d\n", ret);
return ret;
}
spi_base_clk = clk_get_rate(&spi_clk);
if (speed > plat->max_hz)
speed = plat->max_hz;
if (speed > spi_base_clk)
return -ENODEV;
clock = spi_base_clk / speed;
if (clock)
clock--;
priv->clock = clock;
debug("%s: priv->clock: %x, speed: %x, get_spi_clk(): %x\n",
__func__, clock, speed, spi_base_clk);
return 0;
}
static int adi_spi_set_mode(struct udevice *bus, uint mode)
{
struct adi_spi_priv *priv = dev_get_priv(bus);
u32 reg;
reg = SPI_CTL_EN | SPI_CTL_MSTR;
if (mode & SPI_CPHA)
reg |= SPI_CTL_CPHA;
if (mode & SPI_CPOL)
reg |= SPI_CTL_CPOL;
if (mode & SPI_LSB_FIRST)
reg |= SPI_CTL_LSBF;
reg &= ~SPI_CTL_ASSEL;
priv->control = reg;
debug("%s: control=%d, cs_pol=%d\n", __func__, reg, mode & SPI_CS_HIGH ? 1 : 0);
return 0;
}
/**
* U-boot's version of spi-mem does not support mixed bus-width
* commands nor anything more than 1x mode.
* Using a custom exec_op implementation, we can support it.
*/
static int adi_spi_mem_exec_op(struct spi_slave *slave,
const struct spi_mem_op *op)
{
int rv = 0;
struct udevice *bus = slave->dev->parent;
struct adi_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(slave->dev);
u8 tmpbuf[64];
int i;
if ((op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes) >
sizeof(tmpbuf))
return -ENOMEM;
for (i = 0; i < op->cmd.nbytes; i++)
tmpbuf[i] = op->cmd.opcode >>
(8 * (op->cmd.nbytes - i - 1));
for (i = 0; i < op->addr.nbytes; i++)
tmpbuf[i + op->cmd.nbytes] = op->addr.val >>
(8 * (op->addr.nbytes - i - 1));
memset(tmpbuf + op->addr.nbytes + op->cmd.nbytes, 0xff,
op->dummy.nbytes);
adi_spi_cs_activate(priv, slave_plat);
discard_rx_fifo_contents(priv->regs);
if (op->cmd.nbytes) {
rv = adi_spi_fifo_xfer(priv, op->cmd.buswidth,
tmpbuf, NULL, op->cmd.nbytes);
if (rv != 0)
goto cleanup;
}
if (op->addr.nbytes) {
rv = adi_spi_fifo_xfer(priv, op->addr.buswidth,
tmpbuf + op->cmd.nbytes, NULL,
op->addr.nbytes);
if (rv != 0)
goto cleanup;
}
if (op->dummy.nbytes) {
rv = adi_spi_fifo_xfer(priv, op->dummy.buswidth,
tmpbuf + op->cmd.nbytes +
op->addr.nbytes,
NULL, op->dummy.nbytes);
if (rv != 0)
goto cleanup;
}
if (op->data.dir == SPI_MEM_DATA_IN)
rv = adi_spi_fifo_xfer(priv, op->data.buswidth,
NULL, op->data.buf.in,
op->data.nbytes);
else if (op->data.dir == SPI_MEM_DATA_OUT)
rv = adi_spi_fifo_xfer(priv, op->data.buswidth,
op->data.buf.out, NULL,
op->data.nbytes);
cleanup:
adi_spi_cs_deactivate(priv, slave_plat);
return rv;
}
static const struct spi_controller_mem_ops adi_spi_mem_ops = {
.exec_op = adi_spi_mem_exec_op,
};
static const struct dm_spi_ops adi_spi_ops = {
.claim_bus = adi_spi_claim_bus,
.release_bus = adi_spi_release_bus,
.xfer = adi_spi_xfer,
.set_speed = adi_spi_set_speed,
.set_mode = adi_spi_set_mode,
.cs_info = adi_spi_cs_info,
.mem_ops = &adi_spi_mem_ops,
};
static const struct udevice_id adi_spi_ids[] = {
{ .compatible = "adi,spi3" },
{ }
};
U_BOOT_DRIVER(adi_spi3) = {
.name = "adi_spi3",
.id = UCLASS_SPI,
.of_match = adi_spi_ids,
.ops = &adi_spi_ops,
.of_to_plat = adi_spi_of_to_plat,
.probe = adi_spi_probe,
.remove = adi_spi_remove,
.plat_auto = sizeof(struct adi_spi_platdata),
.priv_auto = sizeof(struct adi_spi_priv),
.per_child_auto = sizeof(struct spi_slave),
};

View file

@ -22,6 +22,13 @@ config USB_MUSB_GADGET
Enables the MUSB USB dual-role controller in gadget mode.
if USB_MUSB_HOST || USB_MUSB_GADGET
config USB_MUSB_SC5XX
bool "Analog Devices MUSB support"
depends on (SC57X || SC58X)
help
Say y here to enable support for the USB controller on
ADI SC57X/SC58X processors.
config USB_MUSB_DA8XX
bool "Enable DA8xx MUSB Controller"
depends on ARCH_DAVINCI

View file

@ -14,6 +14,7 @@ obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o
obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
obj-$(CONFIG_USB_MUSB_TI) += ti-musb.o
obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
obj-$(CONFIG_USB_MUSB_SC5XX) += sc5xx.o
ccflags-y := $(call cc-option,-Wno-unused-variable) \
$(call cc-option,-Wno-unused-but-set-variable) \

View file

@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* ADI SC5XX MUSB "glue layer"
*
* Written and/or maintained by Timesys Corporation
*
* Loosely ported from Linux driver:
* Author: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
*
*/
#include <dm.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/usb/musb.h>
#include "linux-compat.h"
#include "musb_core.h"
#include "musb_uboot.h"
#define MUSB_SOFTRST 0x7f
#define MUSB_SOFTRST_NRST BIT(0)
#define MUSB_SOFTRST_NRSTX BIT(1)
#define REG_USB_VBUS_CTL 0x380
#define REG_USB_ID_CTL 0x382
#define REG_USB_PHY_CTL 0x394
#define REG_USB_PLL_OSC 0x398
#define REG_USB_UTMI_CTL 0x39c
/* controller data */
struct sc5xx_musb_data {
struct musb_host_data mdata;
struct device dev;
};
#define to_sc5xx_musb_data(d) \
container_of(d, struct sc5xx_musb_data, dev)
static void sc5xx_musb_disable(struct musb *musb)
{
/* no way to shut the controller */
}
static int sc5xx_musb_enable(struct musb *musb)
{
/* soft reset by NRSTx */
musb_writeb(musb->mregs, MUSB_SOFTRST, MUSB_SOFTRST_NRSTX);
/* set mode */
musb_platform_set_mode(musb, musb->board_mode);
return 0;
}
static irqreturn_t sc5xx_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
irqreturn_t ret = IRQ_NONE;
u8 devctl;
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
if (musb->int_usb & MUSB_INTR_VBUSERROR) {
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
devctl = musb_readw(musb->mregs, MUSB_DEVCTL);
devctl |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
}
if (musb->int_usb || musb->int_tx || musb->int_rx) {
musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
ret = musb_interrupt(musb);
}
if (musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb))
musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0);
return ret;
}
static int sc5xx_musb_set_mode(struct musb *musb, u8 mode)
{
struct device *dev = musb->controller;
struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(dev);
switch (mode) {
case MUSB_HOST:
musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x1);
break;
case MUSB_PERIPHERAL:
musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x3);
break;
case MUSB_OTG:
musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x0);
break;
default:
dev_err(dev, "unsupported mode %d\n", mode);
return -EINVAL;
}
return 0;
}
static int sc5xx_musb_init(struct musb *musb)
{
struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(musb->controller);
musb->isr = sc5xx_interrupt;
musb_writel(musb->mregs, REG_USB_PLL_OSC, 20 << 1);
musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0);
musb_writeb(musb->mregs, REG_USB_PHY_CTL, 0x80);
musb_writel(musb->mregs, REG_USB_UTMI_CTL,
0x40 | musb_readl(musb->mregs, REG_USB_UTMI_CTL));
return 0;
}
const struct musb_platform_ops sc5xx_musb_ops = {
.init = sc5xx_musb_init,
.set_mode = sc5xx_musb_set_mode,
.disable = sc5xx_musb_disable,
.enable = sc5xx_musb_enable,
};
static struct musb_hdrc_config sc5xx_musb_config = {
.multipoint = 1,
.dyn_fifo = 1,
.num_eps = 16,
.ram_bits = 12,
};
/* has one MUSB controller which can be host or gadget */
static struct musb_hdrc_platform_data sc5xx_musb_plat = {
.mode = MUSB_HOST,
.config = &sc5xx_musb_config,
.power = 100,
.platform_ops = &sc5xx_musb_ops,
};
static int musb_usb_probe(struct udevice *dev)
{
struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
struct sc5xx_musb_data *pdata = dev_get_priv(dev);
struct musb_host_data *mdata = &pdata->mdata;
void __iomem *mregs;
int ret;
priv->desc_before_addr = true;
mregs = dev_remap_addr(dev);
if (!mregs)
return -EINVAL;
/* init controller */
if (IS_ENABLED(CONFIG_USB_MUSB_HOST)) {
mdata->host = musb_init_controller(&sc5xx_musb_plat,
&pdata->dev, mregs);
if (!mdata->host)
return -EIO;
ret = musb_lowlevel_init(mdata);
} else {
sc5xx_musb_plat.mode = MUSB_PERIPHERAL;
mdata->host = musb_register(&sc5xx_musb_plat, &pdata->dev, mregs);
if (!mdata->host)
return -EIO;
}
return ret;
}
static int musb_usb_remove(struct udevice *dev)
{
struct sc5xx_musb_data *pdata = dev_get_priv(dev);
musb_stop(pdata->mdata.host);
return 0;
}
static const struct udevice_id sc5xx_musb_ids[] = {
{ .compatible = "adi,sc5xx-musb" },
{ }
};
U_BOOT_DRIVER(usb_musb) = {
.name = "sc5xx-musb",
.id = UCLASS_USB,
.of_match = sc5xx_musb_ids,
.probe = musb_usb_probe,
.remove = musb_usb_remove,
#ifdef CONFIG_USB_MUSB_HOST
.ops = &musb_usb_ops,
#endif
.plat_auto = sizeof(struct usb_plat),
.priv_auto = sizeof(struct sc5xx_musb_data),
};

View file

@ -95,6 +95,15 @@ config WDT_APPLE
The watchdog will perform a full SoC reset resulting in a
reboot of the entire system.
config WDT_ADI
bool "Analog Devices watchdog timer support"
select WDT
select SPL_WDT if SPL
depends on ARCH_SC5XX
help
Enable this to support Watchdog Timer on ADI SC57X, SC58X, SC59X,
and SC59X_64 processors
config WDT_ARMADA_37XX
bool "Marvell Armada 37xx watchdog timer support"
depends on WDT && ARMADA_3700

View file

@ -53,3 +53,4 @@ obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o
obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o
obj-$(CONFIG_WDT_ADI) += adi_wdt.o

143
drivers/watchdog/adi_wdt.c Normal file
View file

@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Converted to driver model by Nathan Barrett-Morrison
*
* Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
* Contact: Greg Malysa <greg.malysa@timesys.com>
*
* adi_wtd.c - driver for ADI on-chip watchdog
*
*/
#include <clk.h>
#include <dm.h>
#include <wdt.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/io.h>
#define WDOG_CTL 0x0
#define WDOG_CNT 0x4
#define WDOG_STAT 0x8
#define RCU_CTL 0x0
#define RCU_STAT 0x4
#define SEC_GCTL 0x0
#define SEC_FCTL 0x10
#define SEC_SCTL0 0x800
#define WDEN 0x0010
#define WDDIS 0x0AD0
struct adi_wdt_priv {
void __iomem *rcu_base;
void __iomem *sec_base;
void __iomem *wdt_base;
struct clk clock;
};
static int adi_wdt_reset(struct udevice *dev)
{
struct adi_wdt_priv *priv = dev_get_priv(dev);
iowrite32(0, priv->wdt_base + WDOG_STAT);
return 0;
}
static int adi_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct adi_wdt_priv *priv = dev_get_priv(dev);
/* Disable SYSCD_RESETb input and clear the RCU0 reset status */
iowrite32(0xf, priv->rcu_base + RCU_STAT);
iowrite32(0x0, priv->rcu_base + RCU_CTL);
/* reset the SEC controller */
iowrite32(0x2, priv->sec_base + SEC_GCTL);
iowrite32(0x2, priv->sec_base + SEC_FCTL);
udelay(50);
/* enable SEC fault event */
iowrite32(0x1, priv->sec_base + SEC_GCTL);
/* ANOMALY 36100004 Spurious External Fault event occurs when FCTL
* is re-programmed when currently active fault is not cleared
*/
iowrite32(0xc0, priv->sec_base + SEC_FCTL);
iowrite32(0xc1, priv->sec_base + SEC_FCTL);
/* enable SEC fault source for watchdog0 */
setbits_32(priv->sec_base + SEC_SCTL0 + (3*8), 0x6);
/* Enable SYSCD_RESETb input */
iowrite32(0x100, priv->rcu_base + RCU_CTL);
/* enable watchdog0 */
iowrite32(WDDIS, priv->wdt_base + WDOG_CTL);
iowrite32(timeout_ms / 1000 *
(clk_get_rate(&priv->clock) / (IS_ENABLED(CONFIG_SC58X) ? 2 : 1)),
priv->wdt_base + WDOG_CNT);
iowrite32(0, priv->wdt_base + WDOG_STAT);
iowrite32(WDEN, priv->wdt_base + WDOG_CTL);
return 0;
}
static int adi_wdt_probe(struct udevice *dev)
{
struct adi_wdt_priv *priv = dev_get_priv(dev);
int ret;
struct resource res;
ret = dev_read_resource_byname(dev, "rcu", &res);
if (ret)
return ret;
priv->rcu_base = devm_ioremap(dev, res.start, resource_size(&res));
ret = dev_read_resource_byname(dev, "sec", &res);
if (ret)
return ret;
priv->sec_base = devm_ioremap(dev, res.start, resource_size(&res));
ret = dev_read_resource_byname(dev, "wdt", &res);
if (ret)
return ret;
priv->wdt_base = devm_ioremap(dev, res.start, resource_size(&res));
ret = clk_get_by_name(dev, "sclk0", &priv->clock);
if (ret < 0) {
printf("Can't get WDT clk: %d\n", ret);
return ret;
}
return 0;
}
static const struct wdt_ops adi_wdt_ops = {
.start = adi_wdt_start,
.reset = adi_wdt_reset,
};
static const struct udevice_id adi_wdt_ids[] = {
{ .compatible = "adi,wdt" },
{}
};
U_BOOT_DRIVER(adi_wdt) = {
.name = "adi_wdt",
.id = UCLASS_WDT,
.of_match = adi_wdt_ids,
.probe = adi_wdt_probe,
.ops = &adi_wdt_ops,
.priv_auto = sizeof(struct adi_wdt_priv),
.flags = DM_FLAG_PRE_RELOC,
};

View file

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
* Contact: Greg Malysa <greg.malysa@timesys.com>
*
*/
#ifndef DT_BINDINGS_PINCTRL_ADI_ADSP
#define DT_BINDINGS_PINCTRL_ADI_ADSP
#define ADI_ADSP_PIN(port, pin) (16 * ((port) - 'A') + (pin))
#define ADI_ADSP_PINFUNC_ALT0 0
#define ADI_ADSP_PINFUNC_ALT1 1
#define ADI_ADSP_PINFUNC_ALT2 2
#define ADI_ADSP_PINFUNC_ALT3 3
#endif