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