1 /*
2 * Copyright (c) 2021 IP-Logix Inc.
3 * Copyright 2023 NXP
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT atmel_sam_mdio
9
10 #include <errno.h>
11 #include <zephyr/device.h>
12 #include <zephyr/init.h>
13 #include <zephyr/kernel.h>
14 #include <soc.h>
15 #include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
16 #include <zephyr/drivers/mdio.h>
17 #include <zephyr/drivers/pinctrl.h>
18 #include <zephyr/net/mdio.h>
19
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(mdio_sam, CONFIG_MDIO_LOG_LEVEL);
22
23 /* GMAC */
24 #ifdef CONFIG_SOC_FAMILY_SAM0
25 #define GMAC_MAN MAN.reg
26 #define GMAC_NSR NSR.reg
27 #define GMAC_NCR NCR.reg
28 #endif
29
30 struct mdio_sam_dev_data {
31 struct k_sem sem;
32 };
33
34 struct mdio_sam_dev_config {
35 Gmac * const regs;
36 const struct pinctrl_dev_config *pcfg;
37 #ifdef CONFIG_SOC_FAMILY_SAM
38 const struct atmel_sam_pmc_config clock_cfg;
39 #endif
40 };
41
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)42 static int mdio_transfer(const struct device *dev, uint8_t prtad, uint8_t regad,
43 enum mdio_opcode op, bool c45, uint16_t data_in,
44 uint16_t *data_out)
45 {
46 const struct mdio_sam_dev_config *const cfg = dev->config;
47 struct mdio_sam_dev_data *const data = dev->data;
48 int timeout = 50;
49
50 k_sem_take(&data->sem, K_FOREVER);
51
52 /* Write mdio transaction */
53 cfg->regs->GMAC_MAN = (c45 ? 0U : GMAC_MAN_CLTTO)
54 | GMAC_MAN_OP(op)
55 | GMAC_MAN_WTN(0x02)
56 | GMAC_MAN_PHYA(prtad)
57 | GMAC_MAN_REGA(regad)
58 | GMAC_MAN_DATA(data_in);
59
60 /* Wait until done */
61 while (!(cfg->regs->GMAC_NSR & GMAC_NSR_IDLE)) {
62 if (timeout-- == 0U) {
63 LOG_ERR("transfer timedout %s", dev->name);
64 k_sem_give(&data->sem);
65
66 return -ETIMEDOUT;
67 }
68
69 k_sleep(K_MSEC(5));
70 }
71
72 if (data_out) {
73 *data_out = cfg->regs->GMAC_MAN & GMAC_MAN_DATA_Msk;
74 }
75
76 k_sem_give(&data->sem);
77
78 return 0;
79 }
80
mdio_sam_read(const struct device * dev,uint8_t prtad,uint8_t regad,uint16_t * data)81 static int mdio_sam_read(const struct device *dev, uint8_t prtad, uint8_t regad,
82 uint16_t *data)
83 {
84 return mdio_transfer(dev, prtad, regad, MDIO_OP_C22_READ, false,
85 0, data);
86 }
87
mdio_sam_write(const struct device * dev,uint8_t prtad,uint8_t regad,uint16_t data)88 static int mdio_sam_write(const struct device *dev, uint8_t prtad,
89 uint8_t regad, uint16_t data)
90 {
91 return mdio_transfer(dev, prtad, regad, MDIO_OP_C22_WRITE, false,
92 data, NULL);
93 }
94
mdio_sam_read_c45(const struct device * dev,uint8_t prtad,uint8_t devad,uint16_t regad,uint16_t * data)95 static int mdio_sam_read_c45(const struct device *dev, uint8_t prtad,
96 uint8_t devad, uint16_t regad, uint16_t *data)
97 {
98 int err;
99
100 err = mdio_transfer(dev, prtad, devad, MDIO_OP_C45_ADDRESS, true,
101 regad, NULL);
102 if (!err) {
103 err = mdio_transfer(dev, prtad, devad, MDIO_OP_C45_READ, true,
104 0, data);
105 }
106
107 return err;
108 }
109
mdio_sam_write_c45(const struct device * dev,uint8_t prtad,uint8_t devad,uint16_t regad,uint16_t data)110 static int mdio_sam_write_c45(const struct device *dev, uint8_t prtad,
111 uint8_t devad, uint16_t regad, uint16_t data)
112 {
113 int err;
114
115 err = mdio_transfer(dev, prtad, devad, MDIO_OP_C45_ADDRESS, true,
116 regad, NULL);
117 if (!err) {
118 err = mdio_transfer(dev, prtad, devad, MDIO_OP_C45_WRITE, true,
119 data, NULL);
120 }
121
122 return err;
123 }
124
mdio_sam_bus_enable(const struct device * dev)125 static void mdio_sam_bus_enable(const struct device *dev)
126 {
127 const struct mdio_sam_dev_config *const cfg = dev->config;
128
129 cfg->regs->GMAC_NCR |= GMAC_NCR_MPE;
130 }
131
mdio_sam_bus_disable(const struct device * dev)132 static void mdio_sam_bus_disable(const struct device *dev)
133 {
134 const struct mdio_sam_dev_config *const cfg = dev->config;
135
136 cfg->regs->GMAC_NCR &= ~GMAC_NCR_MPE;
137 }
138
mdio_sam_initialize(const struct device * dev)139 static int mdio_sam_initialize(const struct device *dev)
140 {
141 const struct mdio_sam_dev_config *const cfg = dev->config;
142 struct mdio_sam_dev_data *const data = dev->data;
143 int retval;
144
145 k_sem_init(&data->sem, 1, 1);
146
147 #ifdef CONFIG_SOC_FAMILY_SAM
148 /* Enable GMAC module's clock */
149 (void) clock_control_on(SAM_DT_PMC_CONTROLLER, (clock_control_subsys_t) &cfg->clock_cfg);
150 #else
151 /* Enable MCLK clock on GMAC */
152 MCLK->AHBMASK.reg |= MCLK_AHBMASK_GMAC;
153 *MCLK_GMAC |= MCLK_GMAC_MASK;
154 #endif
155
156 retval = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
157
158 return retval;
159 }
160
161 static const struct mdio_driver_api mdio_sam_driver_api = {
162 .read = mdio_sam_read,
163 .write = mdio_sam_write,
164 .read_c45 = mdio_sam_read_c45,
165 .write_c45 = mdio_sam_write_c45,
166 .bus_enable = mdio_sam_bus_enable,
167 .bus_disable = mdio_sam_bus_disable,
168 };
169
170 #define MDIO_SAM_CLOCK(n) \
171 COND_CODE_1(CONFIG_SOC_FAMILY_SAM, \
172 (.clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(n),), () \
173 )
174
175 #define MDIO_SAM_CONFIG(n) \
176 static const struct mdio_sam_dev_config mdio_sam_dev_config_##n = { \
177 .regs = (Gmac *)DT_INST_REG_ADDR(n), \
178 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
179 MDIO_SAM_CLOCK(n) \
180 };
181
182 #define MDIO_SAM_DEVICE(n) \
183 PINCTRL_DT_INST_DEFINE(n); \
184 MDIO_SAM_CONFIG(n); \
185 static struct mdio_sam_dev_data mdio_sam_dev_data##n; \
186 DEVICE_DT_INST_DEFINE(n, \
187 &mdio_sam_initialize, \
188 NULL, \
189 &mdio_sam_dev_data##n, \
190 &mdio_sam_dev_config_##n, POST_KERNEL, \
191 CONFIG_MDIO_INIT_PRIORITY, \
192 &mdio_sam_driver_api);
193
194 DT_INST_FOREACH_STATUS_OKAY(MDIO_SAM_DEVICE)
195