195 lines
4.7 KiB
C
195 lines
4.7 KiB
C
/*
|
|
* Copyright (c) 2021 IP-Logix Inc.
|
|
* Copyright 2023 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT atmel_sam_mdio
|
|
|
|
#include <errno.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <soc.h>
|
|
#include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
|
|
#include <zephyr/drivers/mdio.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/net/mdio.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(mdio_sam, CONFIG_MDIO_LOG_LEVEL);
|
|
|
|
/* GMAC */
|
|
#ifdef CONFIG_SOC_FAMILY_ATMEL_SAM0
|
|
#define GMAC_MAN MAN.reg
|
|
#define GMAC_NSR NSR.reg
|
|
#define GMAC_NCR NCR.reg
|
|
#endif
|
|
|
|
struct mdio_sam_dev_data {
|
|
struct k_sem sem;
|
|
};
|
|
|
|
struct mdio_sam_dev_config {
|
|
Gmac * const regs;
|
|
const struct pinctrl_dev_config *pcfg;
|
|
#ifdef CONFIG_SOC_FAMILY_ATMEL_SAM
|
|
const struct atmel_sam_pmc_config clock_cfg;
|
|
#endif
|
|
};
|
|
|
|
static int mdio_transfer(const struct device *dev, uint8_t prtad, uint8_t regad,
|
|
enum mdio_opcode op, bool c45, uint16_t data_in,
|
|
uint16_t *data_out)
|
|
{
|
|
const struct mdio_sam_dev_config *const cfg = dev->config;
|
|
struct mdio_sam_dev_data *const data = dev->data;
|
|
int timeout = 50;
|
|
|
|
k_sem_take(&data->sem, K_FOREVER);
|
|
|
|
/* Write mdio transaction */
|
|
cfg->regs->GMAC_MAN = (c45 ? 0U : GMAC_MAN_CLTTO)
|
|
| GMAC_MAN_OP(op)
|
|
| GMAC_MAN_WTN(0x02)
|
|
| GMAC_MAN_PHYA(prtad)
|
|
| GMAC_MAN_REGA(regad)
|
|
| GMAC_MAN_DATA(data_in);
|
|
|
|
/* Wait until done */
|
|
while (!(cfg->regs->GMAC_NSR & GMAC_NSR_IDLE)) {
|
|
if (timeout-- == 0U) {
|
|
LOG_ERR("transfer timedout %s", dev->name);
|
|
k_sem_give(&data->sem);
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
k_sleep(K_MSEC(5));
|
|
}
|
|
|
|
if (data_out) {
|
|
*data_out = cfg->regs->GMAC_MAN & GMAC_MAN_DATA_Msk;
|
|
}
|
|
|
|
k_sem_give(&data->sem);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mdio_sam_read(const struct device *dev, uint8_t prtad, uint8_t regad,
|
|
uint16_t *data)
|
|
{
|
|
return mdio_transfer(dev, prtad, regad, MDIO_OP_C22_READ, false,
|
|
0, data);
|
|
}
|
|
|
|
static int mdio_sam_write(const struct device *dev, uint8_t prtad,
|
|
uint8_t regad, uint16_t data)
|
|
{
|
|
return mdio_transfer(dev, prtad, regad, MDIO_OP_C22_WRITE, false,
|
|
data, NULL);
|
|
}
|
|
|
|
static int mdio_sam_read_c45(const struct device *dev, uint8_t prtad,
|
|
uint8_t devad, uint16_t regad, uint16_t *data)
|
|
{
|
|
int err;
|
|
|
|
err = mdio_transfer(dev, prtad, devad, MDIO_OP_C45_ADDRESS, true,
|
|
regad, NULL);
|
|
if (!err) {
|
|
err = mdio_transfer(dev, prtad, devad, MDIO_OP_C45_READ, true,
|
|
0, data);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int mdio_sam_write_c45(const struct device *dev, uint8_t prtad,
|
|
uint8_t devad, uint16_t regad, uint16_t data)
|
|
{
|
|
int err;
|
|
|
|
err = mdio_transfer(dev, prtad, devad, MDIO_OP_C45_ADDRESS, true,
|
|
regad, NULL);
|
|
if (!err) {
|
|
err = mdio_transfer(dev, prtad, devad, MDIO_OP_C45_WRITE, true,
|
|
data, NULL);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void mdio_sam_bus_enable(const struct device *dev)
|
|
{
|
|
const struct mdio_sam_dev_config *const cfg = dev->config;
|
|
|
|
cfg->regs->GMAC_NCR |= GMAC_NCR_MPE;
|
|
}
|
|
|
|
static void mdio_sam_bus_disable(const struct device *dev)
|
|
{
|
|
const struct mdio_sam_dev_config *const cfg = dev->config;
|
|
|
|
cfg->regs->GMAC_NCR &= ~GMAC_NCR_MPE;
|
|
}
|
|
|
|
static int mdio_sam_initialize(const struct device *dev)
|
|
{
|
|
const struct mdio_sam_dev_config *const cfg = dev->config;
|
|
struct mdio_sam_dev_data *const data = dev->data;
|
|
int retval;
|
|
|
|
k_sem_init(&data->sem, 1, 1);
|
|
|
|
#ifdef CONFIG_SOC_FAMILY_ATMEL_SAM
|
|
/* Enable GMAC module's clock */
|
|
(void) clock_control_on(SAM_DT_PMC_CONTROLLER, (clock_control_subsys_t) &cfg->clock_cfg);
|
|
#else
|
|
/* Enable MCLK clock on GMAC */
|
|
MCLK->AHBMASK.reg |= MCLK_AHBMASK_GMAC;
|
|
*MCLK_GMAC |= MCLK_GMAC_MASK;
|
|
#endif
|
|
|
|
retval = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static const struct mdio_driver_api mdio_sam_driver_api = {
|
|
.read = mdio_sam_read,
|
|
.write = mdio_sam_write,
|
|
.read_c45 = mdio_sam_read_c45,
|
|
.write_c45 = mdio_sam_write_c45,
|
|
.bus_enable = mdio_sam_bus_enable,
|
|
.bus_disable = mdio_sam_bus_disable,
|
|
};
|
|
|
|
#define MDIO_SAM_CLOCK(n) \
|
|
COND_CODE_1(CONFIG_SOC_FAMILY_ATMEL_SAM, \
|
|
(.clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(n),), () \
|
|
)
|
|
|
|
#define MDIO_SAM_CONFIG(n) \
|
|
static const struct mdio_sam_dev_config mdio_sam_dev_config_##n = { \
|
|
.regs = (Gmac *)DT_INST_REG_ADDR(n), \
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
|
MDIO_SAM_CLOCK(n) \
|
|
};
|
|
|
|
#define MDIO_SAM_DEVICE(n) \
|
|
PINCTRL_DT_INST_DEFINE(n); \
|
|
MDIO_SAM_CONFIG(n); \
|
|
static struct mdio_sam_dev_data mdio_sam_dev_data##n; \
|
|
DEVICE_DT_INST_DEFINE(n, \
|
|
&mdio_sam_initialize, \
|
|
NULL, \
|
|
&mdio_sam_dev_data##n, \
|
|
&mdio_sam_dev_config_##n, POST_KERNEL, \
|
|
CONFIG_MDIO_INIT_PRIORITY, \
|
|
&mdio_sam_driver_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(MDIO_SAM_DEVICE)
|