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