mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-15 17:34:43 +00:00
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:
commit
81ef65099e
36 changed files with 2954 additions and 0 deletions
13
MAINTAINERS
13
MAINTAINERS
|
@ -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
|
||||
|
|
73
doc/device-tree-bindings/pinctrl/adi,adsp-pinctrl.yaml
Normal file
73
doc/device-tree-bindings/pinctrl/adi,adsp-pinctrl.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
|
@ -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"
|
||||
|
|
|
@ -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
253
drivers/dma/adi_dma.c
Normal 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),
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
208
drivers/gpio/adp5588_gpio.c
Normal 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,
|
||||
};
|
179
drivers/gpio/gpio-adi-adsp.c
Normal file
179
drivers/gpio/gpio-adi-adsp.c
Normal 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,
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
386
drivers/i2c/adi_i2c.c
Normal 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,
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
148
drivers/mmc/adi_sdhci.c
Normal 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),
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
103
drivers/net/dwc_eth_qos_adi.c
Normal file
103
drivers/net/dwc_eth_qos_adi.c
Normal 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,
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
161
drivers/pinctrl/pinctrl-adi-adsp.c
Normal file
161
drivers/pinctrl/pinctrl-adi-adsp.c
Normal 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,
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
277
drivers/remoteproc/adi_sc5xx_rproc.c
Normal file
277
drivers/remoteproc/adi_sc5xx_rproc.c
Normal 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,
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
679
drivers/spi/adi_spi3.c
Normal 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(®s->status) & SPI_STAT_RFE))
|
||||
ioread32(®s->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),
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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) \
|
||||
|
|
202
drivers/usb/musb-new/sc5xx.c
Normal file
202
drivers/usb/musb-new/sc5xx.c
Normal 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),
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
143
drivers/watchdog/adi_wdt.c
Normal 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,
|
||||
};
|
21
include/dt-bindings/pinctrl/adi-adsp.h
Normal file
21
include/dt-bindings/pinctrl/adi-adsp.h
Normal 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
|
Loading…
Add table
Reference in a new issue