706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130
  1. From a38126870488398932e017dd9d76174b4aadbbbb Mon Sep 17 00:00:00 2001
  2. From: Robert Marko <[email protected]>
  3. Date: Sat, 10 Sep 2022 15:46:09 +0200
  4. Subject: [PATCH] net: dsa: qca8k: add IPQ4019 built-in switch support
  5. Qualcomm IPQ40xx SoC-s have a variant of QCA8337N switch built-in.
  6. It shares most of the stuff with its external counterpart, however it is
  7. modified for the SoC.
  8. Namely, it doesn't have second CPU port (Port 6), so it has 6 ports
  9. instead of 7.
  10. It also has no built-in PHY-s but rather requires external PSGMII based
  11. companion PHY-s (QCA8072 and QCA8075) for which it first needs to carry
  12. out calibration before using them.
  13. PSGMII has a SoC built-in PHY that is used to connect to the PHY-s which
  14. unfortunately requires some magic values as the datasheet doesnt document
  15. the bits that are being set or the register at all.
  16. Since its built-in it is MMIO like other peripherals and doesn't have its
  17. own MDIO bus but depends on the SoC provided one.
  18. CPU connection is at Port 0 and it uses some kind of a internal connection
  19. and no traditional RGMII/SGMII.
  20. It also doesn't use in-band tagging like other qca8k switches so a out of
  21. band based tagger is used.
  22. Signed-off-by: Robert Marko <[email protected]>
  23. ---
  24. drivers/net/dsa/qca/Kconfig | 8 +
  25. drivers/net/dsa/qca/Makefile | 1 +
  26. drivers/net/dsa/qca/qca8k-common.c | 6 +-
  27. drivers/net/dsa/qca/qca8k-ipq4019.c | 948 ++++++++++++++++++++++++++++
  28. drivers/net/dsa/qca/qca8k.h | 56 ++
  29. 5 files changed, 1016 insertions(+), 3 deletions(-)
  30. create mode 100644 drivers/net/dsa/qca/qca8k-ipq4019.c
  31. --- a/drivers/net/dsa/qca/Kconfig
  32. +++ b/drivers/net/dsa/qca/Kconfig
  33. @@ -24,3 +24,11 @@ config NET_DSA_QCA8K_LEDS_SUPPORT
  34. help
  35. This enabled support for LEDs present on the Qualcomm Atheros
  36. QCA8K Ethernet switch chips.
  37. +
  38. +config NET_DSA_QCA8K_IPQ4019
  39. + tristate "Qualcomm Atheros IPQ4019 Ethernet switch support"
  40. + select NET_DSA_TAG_OOB
  41. + select REGMAP_MMIO
  42. + help
  43. + This enables support for the switch built-into Qualcomm Atheros
  44. + IPQ4019 SoCs.
  45. --- a/drivers/net/dsa/qca/Makefile
  46. +++ b/drivers/net/dsa/qca/Makefile
  47. @@ -5,3 +5,4 @@ qca8k-y += qca8k-common.o qca8k-8xxx.
  48. ifdef CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT
  49. qca8k-y += qca8k-leds.o
  50. endif
  51. +obj-$(CONFIG_NET_DSA_QCA8K_IPQ4019) += qca8k-ipq4019.o qca8k-common.o
  52. --- a/drivers/net/dsa/qca/qca8k-common.c
  53. +++ b/drivers/net/dsa/qca/qca8k-common.c
  54. @@ -412,7 +412,7 @@ static int qca8k_vlan_del(struct qca8k_p
  55. /* Check if we're the last member to be removed */
  56. del = true;
  57. - for (i = 0; i < QCA8K_NUM_PORTS; i++) {
  58. + for (i = 0; i < priv->ds->num_ports; i++) {
  59. mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
  60. if ((reg & mask) != mask) {
  61. @@ -653,7 +653,7 @@ int qca8k_port_bridge_join(struct dsa_sw
  62. cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
  63. port_mask = BIT(cpu_port);
  64. - for (i = 0; i < QCA8K_NUM_PORTS; i++) {
  65. + for (i = 0; i < ds->num_ports; i++) {
  66. if (dsa_is_cpu_port(ds, i))
  67. continue;
  68. if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
  69. @@ -685,7 +685,7 @@ void qca8k_port_bridge_leave(struct dsa_
  70. cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
  71. - for (i = 0; i < QCA8K_NUM_PORTS; i++) {
  72. + for (i = 0; i < ds->num_ports; i++) {
  73. if (dsa_is_cpu_port(ds, i))
  74. continue;
  75. if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
  76. --- /dev/null
  77. +++ b/drivers/net/dsa/qca/qca8k-ipq4019.c
  78. @@ -0,0 +1,946 @@
  79. +// SPDX-License-Identifier: GPL-2.0
  80. +/*
  81. + * Copyright (C) 2009 Felix Fietkau <[email protected]>
  82. + * Copyright (C) 2011-2012, 2020-2021 Gabor Juhos <[email protected]>
  83. + * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved.
  84. + * Copyright (c) 2016 John Crispin <[email protected]>
  85. + * Copyright (c) 2022 Robert Marko <[email protected]>
  86. + */
  87. +
  88. +#include <linux/module.h>
  89. +#include <linux/phy.h>
  90. +#include <linux/netdevice.h>
  91. +#include <linux/bitfield.h>
  92. +#include <linux/regmap.h>
  93. +#include <net/dsa.h>
  94. +#include <linux/of_net.h>
  95. +#include <linux/of_mdio.h>
  96. +#include <linux/of_platform.h>
  97. +#include <linux/mdio.h>
  98. +#include <linux/phylink.h>
  99. +
  100. +#include "qca8k.h"
  101. +
  102. +static struct regmap_config qca8k_ipq4019_regmap_config = {
  103. + .reg_bits = 32,
  104. + .val_bits = 32,
  105. + .reg_stride = 4,
  106. + .max_register = 0x16ac, /* end MIB - Port6 range */
  107. + .rd_table = &qca8k_readable_table,
  108. +};
  109. +
  110. +static struct regmap_config qca8k_ipq4019_psgmii_phy_regmap_config = {
  111. + .name = "psgmii-phy",
  112. + .reg_bits = 32,
  113. + .val_bits = 32,
  114. + .reg_stride = 4,
  115. + .max_register = 0x7fc,
  116. +};
  117. +
  118. +static enum dsa_tag_protocol
  119. +qca8k_ipq4019_get_tag_protocol(struct dsa_switch *ds, int port,
  120. + enum dsa_tag_protocol mp)
  121. +{
  122. + return DSA_TAG_PROTO_OOB;
  123. +}
  124. +
  125. +static struct phylink_pcs *
  126. +qca8k_ipq4019_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
  127. + phy_interface_t interface)
  128. +{
  129. + struct qca8k_priv *priv = ds->priv;
  130. + struct phylink_pcs *pcs = NULL;
  131. +
  132. + switch (interface) {
  133. + case PHY_INTERFACE_MODE_PSGMII:
  134. + switch (port) {
  135. + case 0:
  136. + pcs = &priv->pcs_port_0.pcs;
  137. + break;
  138. + }
  139. + break;
  140. + default:
  141. + break;
  142. + }
  143. +
  144. + return pcs;
  145. +}
  146. +
  147. +static int qca8k_ipq4019_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
  148. + phy_interface_t interface,
  149. + const unsigned long *advertising,
  150. + bool permit_pause_to_mac)
  151. +{
  152. + return 0;
  153. +}
  154. +
  155. +static void qca8k_ipq4019_pcs_an_restart(struct phylink_pcs *pcs)
  156. +{
  157. +}
  158. +
  159. +static struct qca8k_pcs *pcs_to_qca8k_pcs(struct phylink_pcs *pcs)
  160. +{
  161. + return container_of(pcs, struct qca8k_pcs, pcs);
  162. +}
  163. +
  164. +static void qca8k_ipq4019_pcs_get_state(struct phylink_pcs *pcs,
  165. + struct phylink_link_state *state)
  166. +{
  167. + struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv;
  168. + int port = pcs_to_qca8k_pcs(pcs)->port;
  169. + u32 reg;
  170. + int ret;
  171. +
  172. + ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), &reg);
  173. + if (ret < 0) {
  174. + state->link = false;
  175. + return;
  176. + }
  177. +
  178. + state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);
  179. + state->an_complete = state->link;
  180. + state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL :
  181. + DUPLEX_HALF;
  182. +
  183. + switch (reg & QCA8K_PORT_STATUS_SPEED) {
  184. + case QCA8K_PORT_STATUS_SPEED_10:
  185. + state->speed = SPEED_10;
  186. + break;
  187. + case QCA8K_PORT_STATUS_SPEED_100:
  188. + state->speed = SPEED_100;
  189. + break;
  190. + case QCA8K_PORT_STATUS_SPEED_1000:
  191. + state->speed = SPEED_1000;
  192. + break;
  193. + default:
  194. + state->speed = SPEED_UNKNOWN;
  195. + break;
  196. + }
  197. +
  198. + if (reg & QCA8K_PORT_STATUS_RXFLOW)
  199. + state->pause |= MLO_PAUSE_RX;
  200. + if (reg & QCA8K_PORT_STATUS_TXFLOW)
  201. + state->pause |= MLO_PAUSE_TX;
  202. +}
  203. +
  204. +static const struct phylink_pcs_ops qca8k_pcs_ops = {
  205. + .pcs_get_state = qca8k_ipq4019_pcs_get_state,
  206. + .pcs_config = qca8k_ipq4019_pcs_config,
  207. + .pcs_an_restart = qca8k_ipq4019_pcs_an_restart,
  208. +};
  209. +
  210. +static void qca8k_ipq4019_setup_pcs(struct qca8k_priv *priv,
  211. + struct qca8k_pcs *qpcs,
  212. + int port)
  213. +{
  214. + qpcs->pcs.ops = &qca8k_pcs_ops;
  215. +
  216. + /* We don't have interrupts for link changes, so we need to poll */
  217. + qpcs->pcs.poll = true;
  218. + qpcs->priv = priv;
  219. + qpcs->port = port;
  220. +}
  221. +
  222. +static void qca8k_ipq4019_phylink_get_caps(struct dsa_switch *ds, int port,
  223. + struct phylink_config *config)
  224. +{
  225. + switch (port) {
  226. + case 0: /* CPU port */
  227. + __set_bit(PHY_INTERFACE_MODE_INTERNAL,
  228. + config->supported_interfaces);
  229. + break;
  230. +
  231. + case 1:
  232. + case 2:
  233. + case 3:
  234. + __set_bit(PHY_INTERFACE_MODE_PSGMII,
  235. + config->supported_interfaces);
  236. + break;
  237. + case 4:
  238. + case 5:
  239. + phy_interface_set_rgmii(config->supported_interfaces);
  240. + __set_bit(PHY_INTERFACE_MODE_PSGMII,
  241. + config->supported_interfaces);
  242. + break;
  243. + }
  244. +
  245. + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
  246. + MAC_10 | MAC_100 | MAC_1000FD;
  247. +}
  248. +
  249. +static void
  250. +qca8k_phylink_ipq4019_mac_link_down(struct dsa_switch *ds, int port,
  251. + unsigned int mode,
  252. + phy_interface_t interface)
  253. +{
  254. + struct qca8k_priv *priv = ds->priv;
  255. +
  256. + qca8k_port_set_status(priv, port, 0);
  257. +}
  258. +
  259. +static void
  260. +qca8k_phylink_ipq4019_mac_link_up(struct dsa_switch *ds, int port,
  261. + unsigned int mode, phy_interface_t interface,
  262. + struct phy_device *phydev, int speed,
  263. + int duplex, bool tx_pause, bool rx_pause)
  264. +{
  265. + struct qca8k_priv *priv = ds->priv;
  266. + u32 reg;
  267. +
  268. + if (phylink_autoneg_inband(mode)) {
  269. + reg = QCA8K_PORT_STATUS_LINK_AUTO;
  270. + } else {
  271. + switch (speed) {
  272. + case SPEED_10:
  273. + reg = QCA8K_PORT_STATUS_SPEED_10;
  274. + break;
  275. + case SPEED_100:
  276. + reg = QCA8K_PORT_STATUS_SPEED_100;
  277. + break;
  278. + case SPEED_1000:
  279. + reg = QCA8K_PORT_STATUS_SPEED_1000;
  280. + break;
  281. + default:
  282. + reg = QCA8K_PORT_STATUS_LINK_AUTO;
  283. + break;
  284. + }
  285. +
  286. + if (duplex == DUPLEX_FULL)
  287. + reg |= QCA8K_PORT_STATUS_DUPLEX;
  288. +
  289. + if (rx_pause || dsa_is_cpu_port(ds, port))
  290. + reg |= QCA8K_PORT_STATUS_RXFLOW;
  291. +
  292. + if (tx_pause || dsa_is_cpu_port(ds, port))
  293. + reg |= QCA8K_PORT_STATUS_TXFLOW;
  294. + }
  295. +
  296. + reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
  297. +
  298. + qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg);
  299. +}
  300. +
  301. +static int psgmii_vco_calibrate(struct qca8k_priv *priv)
  302. +{
  303. + int val, ret;
  304. +
  305. + if (!priv->psgmii_ethphy) {
  306. + dev_err(priv->dev, "PSGMII eth PHY missing, calibration failed!\n");
  307. + return -ENODEV;
  308. + }
  309. +
  310. + /* Fix PSGMII RX 20bit */
  311. + ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x5b);
  312. + /* Reset PHY PSGMII */
  313. + ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x1b);
  314. + /* Release PHY PSGMII reset */
  315. + ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x5b);
  316. +
  317. + /* Poll for VCO PLL calibration finish - Malibu(QCA8075) */
  318. + ret = phy_read_mmd_poll_timeout(priv->psgmii_ethphy,
  319. + MDIO_MMD_PMAPMD,
  320. + 0x28, val,
  321. + (val & BIT(0)),
  322. + 10000, 1000000,
  323. + false);
  324. + if (ret) {
  325. + dev_err(priv->dev, "QCA807x PSGMII VCO calibration PLL not ready\n");
  326. + return ret;
  327. + }
  328. + mdelay(50);
  329. +
  330. + /* Freeze PSGMII RX CDR */
  331. + ret = phy_write(priv->psgmii_ethphy, MII_RESV2, 0x2230);
  332. +
  333. + /* Start PSGMIIPHY VCO PLL calibration */
  334. + ret = regmap_set_bits(priv->psgmii,
  335. + PSGMIIPHY_VCO_CALIBRATION_CONTROL_REGISTER_1,
  336. + PSGMIIPHY_REG_PLL_VCO_CALIB_RESTART);
  337. +
  338. + /* Poll for PSGMIIPHY PLL calibration finish - Dakota(IPQ40xx) */
  339. + ret = regmap_read_poll_timeout(priv->psgmii,
  340. + PSGMIIPHY_VCO_CALIBRATION_CONTROL_REGISTER_2,
  341. + val, val & PSGMIIPHY_REG_PLL_VCO_CALIB_READY,
  342. + 10000, 1000000);
  343. + if (ret) {
  344. + dev_err(priv->dev, "IPQ PSGMIIPHY VCO calibration PLL not ready\n");
  345. + return ret;
  346. + }
  347. + mdelay(50);
  348. +
  349. + /* Release PSGMII RX CDR */
  350. + ret = phy_write(priv->psgmii_ethphy, MII_RESV2, 0x3230);
  351. + /* Release PSGMII RX 20bit */
  352. + ret = phy_write(priv->psgmii_ethphy, MII_BMCR, 0x5f);
  353. + mdelay(200);
  354. +
  355. + return ret;
  356. +}
  357. +
  358. +static void
  359. +qca8k_switch_port_loopback_on_off(struct qca8k_priv *priv, int port, int on)
  360. +{
  361. + u32 val = QCA8K_PORT_LOOKUP_LOOPBACK_EN;
  362. +
  363. + if (on == 0)
  364. + val = 0;
  365. +
  366. + qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
  367. + QCA8K_PORT_LOOKUP_LOOPBACK_EN, val);
  368. +}
  369. +
  370. +static int
  371. +qca8k_wait_for_phy_link_state(struct phy_device *phy, int need_status)
  372. +{
  373. + int a;
  374. + u16 status;
  375. +
  376. + for (a = 0; a < 100; a++) {
  377. + status = phy_read(phy, MII_QCA8075_SSTATUS);
  378. + status &= QCA8075_PHY_SPEC_STATUS_LINK;
  379. + status = !!status;
  380. + if (status == need_status)
  381. + return 0;
  382. + mdelay(8);
  383. + }
  384. +
  385. + return -1;
  386. +}
  387. +
  388. +static void
  389. +qca8k_phy_loopback_on_off(struct qca8k_priv *priv, struct phy_device *phy,
  390. + int sw_port, int on)
  391. +{
  392. + if (on) {
  393. + phy_write(phy, MII_BMCR, BMCR_ANENABLE | BMCR_RESET);
  394. + phy_modify(phy, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN);
  395. + qca8k_wait_for_phy_link_state(phy, 0);
  396. + qca8k_write(priv, QCA8K_REG_PORT_STATUS(sw_port), 0);
  397. + phy_write(phy, MII_BMCR,
  398. + BMCR_SPEED1000 |
  399. + BMCR_FULLDPLX |
  400. + BMCR_LOOPBACK);
  401. + qca8k_wait_for_phy_link_state(phy, 1);
  402. + qca8k_write(priv, QCA8K_REG_PORT_STATUS(sw_port),
  403. + QCA8K_PORT_STATUS_SPEED_1000 |
  404. + QCA8K_PORT_STATUS_TXMAC |
  405. + QCA8K_PORT_STATUS_RXMAC |
  406. + QCA8K_PORT_STATUS_DUPLEX);
  407. + qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(sw_port),
  408. + QCA8K_PORT_LOOKUP_STATE_FORWARD,
  409. + QCA8K_PORT_LOOKUP_STATE_FORWARD);
  410. + } else { /* off */
  411. + qca8k_write(priv, QCA8K_REG_PORT_STATUS(sw_port), 0);
  412. + qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(sw_port),
  413. + QCA8K_PORT_LOOKUP_STATE_DISABLED,
  414. + QCA8K_PORT_LOOKUP_STATE_DISABLED);
  415. + phy_write(phy, MII_BMCR, BMCR_SPEED1000 | BMCR_ANENABLE | BMCR_RESET);
  416. + /* turn off the power of the phys - so that unused
  417. + ports do not raise links */
  418. + phy_modify(phy, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN);
  419. + }
  420. +}
  421. +
  422. +static void
  423. +qca8k_phy_pkt_gen_prep(struct qca8k_priv *priv, struct phy_device *phy,
  424. + int pkts_num, int on)
  425. +{
  426. + if (on) {
  427. + /* enable CRC checker and packets counters */
  428. + phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_CRC_AND_PKTS_COUNT, 0);
  429. + phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_CRC_AND_PKTS_COUNT,
  430. + QCA8075_MMD7_CNT_FRAME_CHK_EN | QCA8075_MMD7_CNT_SELFCLR);
  431. + qca8k_wait_for_phy_link_state(phy, 1);
  432. + /* packet number */
  433. + phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_PKT_NUMB, pkts_num);
  434. + /* pkt size - 1504 bytes + 20 bytes */
  435. + phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_PKT_SIZE, 1504);
  436. + } else { /* off */
  437. + /* packet number */
  438. + phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_PKT_NUMB, 0);
  439. + /* disable CRC checker and packet counter */
  440. + phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_CRC_AND_PKTS_COUNT, 0);
  441. + /* disable traffic gen */
  442. + phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_CTRL, 0);
  443. + }
  444. +}
  445. +
  446. +static void
  447. +qca8k_wait_for_phy_pkt_gen_fin(struct qca8k_priv *priv, struct phy_device *phy)
  448. +{
  449. + int val;
  450. + /* wait for all traffic end: 4096(pkt num)*1524(size)*8ns(125MHz)=49938us */
  451. + phy_read_mmd_poll_timeout(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_CTRL,
  452. + val, !(val & QCA8075_MMD7_PKT_GEN_INPROGR),
  453. + 50000, 1000000, true);
  454. +}
  455. +
  456. +static void
  457. +qca8k_start_phy_pkt_gen(struct phy_device *phy)
  458. +{
  459. + /* start traffic gen */
  460. + phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_PKT_GEN_CTRL,
  461. + QCA8075_MMD7_PKT_GEN_START | QCA8075_MMD7_PKT_GEN_INPROGR);
  462. +}
  463. +
  464. +static int
  465. +qca8k_start_all_phys_pkt_gens(struct qca8k_priv *priv)
  466. +{
  467. + struct phy_device *phy;
  468. + phy = phy_device_create(priv->bus, QCA8075_MDIO_BRDCST_PHY_ADDR,
  469. + 0, 0, NULL);
  470. + if (!phy) {
  471. + dev_err(priv->dev, "unable to create mdio broadcast PHY(0x%x)\n",
  472. + QCA8075_MDIO_BRDCST_PHY_ADDR);
  473. + return -ENODEV;
  474. + }
  475. +
  476. + qca8k_start_phy_pkt_gen(phy);
  477. +
  478. + phy_device_free(phy);
  479. + return 0;
  480. +}
  481. +
  482. +static int
  483. +qca8k_get_phy_pkt_gen_test_result(struct phy_device *phy, int pkts_num)
  484. +{
  485. + u32 tx_ok, tx_error;
  486. + u32 rx_ok, rx_error;
  487. + u32 tx_ok_high16;
  488. + u32 rx_ok_high16;
  489. + u32 tx_all_ok, rx_all_ok;
  490. +
  491. + /* check counters */
  492. + tx_ok = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_EG_FRAME_RECV_CNT_LO);
  493. + tx_ok_high16 = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_EG_FRAME_RECV_CNT_HI);
  494. + tx_error = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_EG_FRAME_ERR_CNT);
  495. + rx_ok = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_IG_FRAME_RECV_CNT_LO);
  496. + rx_ok_high16 = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_IG_FRAME_RECV_CNT_HI);
  497. + rx_error = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_IG_FRAME_ERR_CNT);
  498. + tx_all_ok = tx_ok + (tx_ok_high16 << 16);
  499. + rx_all_ok = rx_ok + (rx_ok_high16 << 16);
  500. +
  501. + if (tx_all_ok < pkts_num)
  502. + return -1;
  503. + if(rx_all_ok < pkts_num)
  504. + return -2;
  505. + if(tx_error)
  506. + return -3;
  507. + if(rx_error)
  508. + return -4;
  509. + return 0; /* test is ok */
  510. +}
  511. +
  512. +static
  513. +void qca8k_phy_broadcast_write_on_off(struct qca8k_priv *priv,
  514. + struct phy_device *phy, int on)
  515. +{
  516. + u32 val;
  517. +
  518. + val = phy_read_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_MDIO_BRDCST_WRITE);
  519. +
  520. + if (on == 0)
  521. + val &= ~QCA8075_MMD7_MDIO_BRDCST_WRITE_EN;
  522. + else
  523. + val |= QCA8075_MMD7_MDIO_BRDCST_WRITE_EN;
  524. +
  525. + phy_write_mmd(phy, MDIO_MMD_AN, QCA8075_MMD7_MDIO_BRDCST_WRITE, val);
  526. +}
  527. +
  528. +static int
  529. +qca8k_test_dsa_port_for_errors(struct qca8k_priv *priv, struct phy_device *phy,
  530. + int port, int test_phase)
  531. +{
  532. + int res = 0;
  533. + const int test_pkts_num = QCA8075_PKT_GEN_PKTS_COUNT;
  534. +
  535. + if (test_phase == 1) { /* start test preps */
  536. + qca8k_phy_loopback_on_off(priv, phy, port, 1);
  537. + qca8k_switch_port_loopback_on_off(priv, port, 1);
  538. + qca8k_phy_broadcast_write_on_off(priv, phy, 1);
  539. + qca8k_phy_pkt_gen_prep(priv, phy, test_pkts_num, 1);
  540. + } else if (test_phase == 2) {
  541. + /* wait for test results, collect it and cleanup */
  542. + qca8k_wait_for_phy_pkt_gen_fin(priv, phy);
  543. + res = qca8k_get_phy_pkt_gen_test_result(phy, test_pkts_num);
  544. + qca8k_phy_pkt_gen_prep(priv, phy, test_pkts_num, 0);
  545. + qca8k_phy_broadcast_write_on_off(priv, phy, 0);
  546. + qca8k_switch_port_loopback_on_off(priv, port, 0);
  547. + qca8k_phy_loopback_on_off(priv, phy, port, 0);
  548. + }
  549. +
  550. + return res;
  551. +}
  552. +
  553. +static int
  554. +qca8k_do_dsa_sw_ports_self_test(struct qca8k_priv *priv, int parallel_test)
  555. +{
  556. + struct device_node *dn = priv->dev->of_node;
  557. + struct device_node *ports, *port;
  558. + struct device_node *phy_dn;
  559. + struct phy_device *phy;
  560. + int reg, err = 0, test_phase;
  561. + u32 tests_result = 0;
  562. +
  563. + ports = of_get_child_by_name(dn, "ports");
  564. + if (!ports) {
  565. + dev_err(priv->dev, "no ports child node found\n");
  566. + return -EINVAL;
  567. + }
  568. +
  569. + for (test_phase = 1; test_phase <= 2; test_phase++) {
  570. + if (parallel_test && test_phase == 2) {
  571. + err = qca8k_start_all_phys_pkt_gens(priv);
  572. + if (err)
  573. + goto error;
  574. + }
  575. + for_each_available_child_of_node(ports, port) {
  576. + err = of_property_read_u32(port, "reg", &reg);
  577. + if (err)
  578. + goto error;
  579. + if (reg >= QCA8K_NUM_PORTS) {
  580. + err = -EINVAL;
  581. + goto error;
  582. + }
  583. + phy_dn = of_parse_phandle(port, "phy-handle", 0);
  584. + if (phy_dn) {
  585. + phy = of_phy_find_device(phy_dn);
  586. + of_node_put(phy_dn);
  587. + if (phy) {
  588. + int result;
  589. + result = qca8k_test_dsa_port_for_errors(priv,
  590. + phy, reg, test_phase);
  591. + if (!parallel_test && test_phase == 1)
  592. + qca8k_start_phy_pkt_gen(phy);
  593. + put_device(&phy->mdio.dev);
  594. + if (test_phase == 2) {
  595. + tests_result <<= 1;
  596. + if (result)
  597. + tests_result |= 1;
  598. + }
  599. + }
  600. + }
  601. + }
  602. + }
  603. +
  604. +end:
  605. + of_node_put(ports);
  606. + qca8k_fdb_flush(priv);
  607. + return tests_result;
  608. +error:
  609. + tests_result |= 0xf000;
  610. + goto end;
  611. +}
  612. +
  613. +static int
  614. +psgmii_vco_calibrate_and_test(struct dsa_switch *ds)
  615. +{
  616. + int ret, a, test_result;
  617. + struct qca8k_priv *priv = ds->priv;
  618. +
  619. + for (a = 0; a <= QCA8K_PSGMII_CALB_NUM; a++) {
  620. + ret = psgmii_vco_calibrate(priv);
  621. + if (ret)
  622. + return ret;
  623. + /* first we run serial test */
  624. + test_result = qca8k_do_dsa_sw_ports_self_test(priv, 0);
  625. + /* and if it is ok then we run the test in parallel */
  626. + if (!test_result)
  627. + test_result = qca8k_do_dsa_sw_ports_self_test(priv, 1);
  628. + if (!test_result) {
  629. + if (a > 0) {
  630. + dev_warn(priv->dev, "PSGMII work was stabilized after %d "
  631. + "calibration retries !\n", a);
  632. + }
  633. + return 0;
  634. + } else {
  635. + schedule();
  636. + if (a > 0 && a % 10 == 0) {
  637. + dev_err(priv->dev, "PSGMII work is unstable !!! "
  638. + "Let's try to wait a bit ... %d\n", a);
  639. + set_current_state(TASK_INTERRUPTIBLE);
  640. + schedule_timeout(msecs_to_jiffies(a * 100));
  641. + }
  642. + }
  643. + }
  644. +
  645. + panic("PSGMII work is unstable !!! "
  646. + "Repeated recalibration attempts did not help(0x%x) !\n",
  647. + test_result);
  648. +
  649. + return -EFAULT;
  650. +}
  651. +
  652. +static int
  653. +ipq4019_psgmii_configure(struct dsa_switch *ds)
  654. +{
  655. + struct qca8k_priv *priv = ds->priv;
  656. + int ret;
  657. +
  658. + if (!priv->psgmii_calibrated) {
  659. + dev_info(ds->dev, "PSGMII calibration!\n");
  660. + ret = psgmii_vco_calibrate_and_test(ds);
  661. +
  662. + ret = regmap_clear_bits(priv->psgmii, PSGMIIPHY_MODE_CONTROL,
  663. + PSGMIIPHY_MODE_ATHR_CSCO_MODE_25M);
  664. + ret = regmap_write(priv->psgmii, PSGMIIPHY_TX_CONTROL,
  665. + PSGMIIPHY_TX_CONTROL_MAGIC_VALUE);
  666. +
  667. + priv->psgmii_calibrated = true;
  668. +
  669. + return ret;
  670. + }
  671. +
  672. + return 0;
  673. +}
  674. +
  675. +static void
  676. +qca8k_phylink_ipq4019_mac_config(struct dsa_switch *ds, int port,
  677. + unsigned int mode,
  678. + const struct phylink_link_state *state)
  679. +{
  680. + struct qca8k_priv *priv = ds->priv;
  681. +
  682. + switch (port) {
  683. + case 0:
  684. + /* CPU port, no configuration needed */
  685. + return;
  686. + case 1:
  687. + case 2:
  688. + case 3:
  689. + if (state->interface == PHY_INTERFACE_MODE_PSGMII)
  690. + if (ipq4019_psgmii_configure(ds))
  691. + dev_err(ds->dev, "PSGMII configuration failed!\n");
  692. + return;
  693. + case 4:
  694. + case 5:
  695. + if (state->interface == PHY_INTERFACE_MODE_RGMII ||
  696. + state->interface == PHY_INTERFACE_MODE_RGMII_ID ||
  697. + state->interface == PHY_INTERFACE_MODE_RGMII_RXID ||
  698. + state->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
  699. + regmap_set_bits(priv->regmap,
  700. + QCA8K_IPQ4019_REG_RGMII_CTRL,
  701. + QCA8K_IPQ4019_RGMII_CTRL_CLK);
  702. + }
  703. +
  704. + if (state->interface == PHY_INTERFACE_MODE_PSGMII)
  705. + if (ipq4019_psgmii_configure(ds))
  706. + dev_err(ds->dev, "PSGMII configuration failed!\n");
  707. + return;
  708. + default:
  709. + dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
  710. + return;
  711. + }
  712. +}
  713. +
  714. +static int
  715. +qca8k_ipq4019_setup_port(struct dsa_switch *ds, int port)
  716. +{
  717. + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
  718. + int ret;
  719. +
  720. + /* CPU port gets connected to all user ports of the switch */
  721. + if (dsa_is_cpu_port(ds, port)) {
  722. + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
  723. + QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
  724. + if (ret)
  725. + return ret;
  726. +
  727. + /* Disable CPU ARP Auto-learning by default */
  728. + ret = regmap_clear_bits(priv->regmap,
  729. + QCA8K_PORT_LOOKUP_CTRL(port),
  730. + QCA8K_PORT_LOOKUP_LEARN);
  731. + if (ret)
  732. + return ret;
  733. + }
  734. +
  735. + /* Individual user ports get connected to CPU port only */
  736. + if (dsa_is_user_port(ds, port)) {
  737. + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
  738. + QCA8K_PORT_LOOKUP_MEMBER,
  739. + BIT(QCA8K_IPQ4019_CPU_PORT));
  740. + if (ret)
  741. + return ret;
  742. +
  743. + /* Enable ARP Auto-learning by default */
  744. + ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
  745. + QCA8K_PORT_LOOKUP_LEARN);
  746. + if (ret)
  747. + return ret;
  748. +
  749. + /* For port based vlans to work we need to set the
  750. + * default egress vid
  751. + */
  752. + ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
  753. + QCA8K_EGREES_VLAN_PORT_MASK(port),
  754. + QCA8K_EGREES_VLAN_PORT(port, QCA8K_PORT_VID_DEF));
  755. + if (ret)
  756. + return ret;
  757. +
  758. + ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
  759. + QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
  760. + QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
  761. + if (ret)
  762. + return ret;
  763. + }
  764. +
  765. + return 0;
  766. +}
  767. +
  768. +static int
  769. +qca8k_ipq4019_setup(struct dsa_switch *ds)
  770. +{
  771. + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
  772. + int ret, i;
  773. +
  774. + /* Make sure that port 0 is the cpu port */
  775. + if (!dsa_is_cpu_port(ds, QCA8K_IPQ4019_CPU_PORT)) {
  776. + dev_err(priv->dev, "port %d is not the CPU port",
  777. + QCA8K_IPQ4019_CPU_PORT);
  778. + return -EINVAL;
  779. + }
  780. +
  781. + qca8k_ipq4019_setup_pcs(priv, &priv->pcs_port_0, 0);
  782. +
  783. + /* Enable CPU Port */
  784. + ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
  785. + QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
  786. + if (ret) {
  787. + dev_err(priv->dev, "failed enabling CPU port");
  788. + return ret;
  789. + }
  790. +
  791. + /* Enable MIB counters */
  792. + ret = qca8k_mib_init(priv);
  793. + if (ret)
  794. + dev_warn(priv->dev, "MIB init failed");
  795. +
  796. + /* Disable forwarding by default on all ports */
  797. + for (i = 0; i < QCA8K_IPQ4019_NUM_PORTS; i++) {
  798. + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
  799. + QCA8K_PORT_LOOKUP_MEMBER, 0);
  800. + if (ret)
  801. + return ret;
  802. + }
  803. +
  804. + /* Enable QCA header mode on the CPU port */
  805. + ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_IPQ4019_CPU_PORT),
  806. + FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
  807. + FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
  808. + if (ret) {
  809. + dev_err(priv->dev, "failed enabling QCA header mode");
  810. + return ret;
  811. + }
  812. +
  813. + /* Disable MAC by default on all ports */
  814. + for (i = 0; i < QCA8K_IPQ4019_NUM_PORTS; i++) {
  815. + if (dsa_is_user_port(ds, i))
  816. + qca8k_port_set_status(priv, i, 0);
  817. + }
  818. +
  819. + /* Forward all unknown frames to CPU port for Linux processing */
  820. + ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
  821. + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(QCA8K_IPQ4019_CPU_PORT)) |
  822. + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(QCA8K_IPQ4019_CPU_PORT)) |
  823. + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(QCA8K_IPQ4019_CPU_PORT)) |
  824. + FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(QCA8K_IPQ4019_CPU_PORT)));
  825. + if (ret)
  826. + return ret;
  827. +
  828. + /* Setup connection between CPU port & user ports */
  829. + for (i = 0; i < QCA8K_IPQ4019_NUM_PORTS; i++) {
  830. + ret = qca8k_ipq4019_setup_port(ds, i);
  831. + if (ret)
  832. + return ret;
  833. + }
  834. +
  835. + /* Setup our port MTUs to match power on defaults */
  836. + ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
  837. + if (ret)
  838. + dev_warn(priv->dev, "failed setting MTU settings");
  839. +
  840. + /* Flush the FDB table */
  841. + qca8k_fdb_flush(priv);
  842. +
  843. + /* Set min a max ageing value supported */
  844. + ds->ageing_time_min = 7000;
  845. + ds->ageing_time_max = 458745000;
  846. +
  847. + /* Set max number of LAGs supported */
  848. + ds->num_lag_ids = QCA8K_NUM_LAGS;
  849. +
  850. + /* CPU port HW learning doesnt work correctly, so let DSA handle it */
  851. + ds->assisted_learning_on_cpu_port = true;
  852. +
  853. + return 0;
  854. +}
  855. +
  856. +static const struct dsa_switch_ops qca8k_ipq4019_switch_ops = {
  857. + .get_tag_protocol = qca8k_ipq4019_get_tag_protocol,
  858. + .setup = qca8k_ipq4019_setup,
  859. + .get_strings = qca8k_get_strings,
  860. + .get_ethtool_stats = qca8k_get_ethtool_stats,
  861. + .get_sset_count = qca8k_get_sset_count,
  862. + .set_ageing_time = qca8k_set_ageing_time,
  863. + .get_mac_eee = qca8k_get_mac_eee,
  864. + .set_mac_eee = qca8k_set_mac_eee,
  865. + .port_enable = qca8k_port_enable,
  866. + .port_disable = qca8k_port_disable,
  867. + .port_change_mtu = qca8k_port_change_mtu,
  868. + .port_max_mtu = qca8k_port_max_mtu,
  869. + .port_stp_state_set = qca8k_port_stp_state_set,
  870. + .port_bridge_join = qca8k_port_bridge_join,
  871. + .port_bridge_leave = qca8k_port_bridge_leave,
  872. + .port_fast_age = qca8k_port_fast_age,
  873. + .port_fdb_add = qca8k_port_fdb_add,
  874. + .port_fdb_del = qca8k_port_fdb_del,
  875. + .port_fdb_dump = qca8k_port_fdb_dump,
  876. + .port_mdb_add = qca8k_port_mdb_add,
  877. + .port_mdb_del = qca8k_port_mdb_del,
  878. + .port_mirror_add = qca8k_port_mirror_add,
  879. + .port_mirror_del = qca8k_port_mirror_del,
  880. + .port_vlan_filtering = qca8k_port_vlan_filtering,
  881. + .port_vlan_add = qca8k_port_vlan_add,
  882. + .port_vlan_del = qca8k_port_vlan_del,
  883. + .phylink_mac_select_pcs = qca8k_ipq4019_phylink_mac_select_pcs,
  884. + .phylink_get_caps = qca8k_ipq4019_phylink_get_caps,
  885. + .phylink_mac_config = qca8k_phylink_ipq4019_mac_config,
  886. + .phylink_mac_link_down = qca8k_phylink_ipq4019_mac_link_down,
  887. + .phylink_mac_link_up = qca8k_phylink_ipq4019_mac_link_up,
  888. + .port_lag_join = qca8k_port_lag_join,
  889. + .port_lag_leave = qca8k_port_lag_leave,
  890. +};
  891. +
  892. +static const struct qca8k_match_data ipq4019 = {
  893. + .id = QCA8K_ID_IPQ4019,
  894. + .mib_count = QCA8K_QCA833X_MIB_COUNT,
  895. +};
  896. +
  897. +static int
  898. +qca8k_ipq4019_probe(struct platform_device *pdev)
  899. +{
  900. + struct device *dev = &pdev->dev;
  901. + struct qca8k_priv *priv;
  902. + void __iomem *base, *psgmii;
  903. + struct device_node *np = dev->of_node, *mdio_np, *psgmii_ethphy_np;
  904. + int ret;
  905. +
  906. + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  907. + if (!priv)
  908. + return -ENOMEM;
  909. +
  910. + priv->dev = dev;
  911. + priv->info = &ipq4019;
  912. +
  913. + /* Start by setting up the register mapping */
  914. + base = devm_platform_ioremap_resource_byname(pdev, "base");
  915. + if (IS_ERR(base))
  916. + return PTR_ERR(base);
  917. +
  918. + priv->regmap = devm_regmap_init_mmio(dev, base,
  919. + &qca8k_ipq4019_regmap_config);
  920. + if (IS_ERR(priv->regmap)) {
  921. + ret = PTR_ERR(priv->regmap);
  922. + dev_err(dev, "base regmap initialization failed, %d\n", ret);
  923. + return ret;
  924. + }
  925. +
  926. + psgmii = devm_platform_ioremap_resource_byname(pdev, "psgmii_phy");
  927. + if (IS_ERR(psgmii))
  928. + return PTR_ERR(psgmii);
  929. +
  930. + priv->psgmii = devm_regmap_init_mmio(dev, psgmii,
  931. + &qca8k_ipq4019_psgmii_phy_regmap_config);
  932. + if (IS_ERR(priv->psgmii)) {
  933. + ret = PTR_ERR(priv->psgmii);
  934. + dev_err(dev, "PSGMII regmap initialization failed, %d\n", ret);
  935. + return ret;
  936. + }
  937. +
  938. + mdio_np = of_parse_phandle(np, "mdio", 0);
  939. + if (!mdio_np) {
  940. + dev_err(dev, "unable to get MDIO bus phandle\n");
  941. + of_node_put(mdio_np);
  942. + return -EINVAL;
  943. + }
  944. +
  945. + priv->bus = of_mdio_find_bus(mdio_np);
  946. + of_node_put(mdio_np);
  947. + if (!priv->bus) {
  948. + dev_err(dev, "unable to find MDIO bus\n");
  949. + return -EPROBE_DEFER;
  950. + }
  951. +
  952. + psgmii_ethphy_np = of_parse_phandle(np, "psgmii-ethphy", 0);
  953. + if (!psgmii_ethphy_np) {
  954. + dev_dbg(dev, "unable to get PSGMII eth PHY phandle\n");
  955. + of_node_put(psgmii_ethphy_np);
  956. + }
  957. +
  958. + if (psgmii_ethphy_np) {
  959. + priv->psgmii_ethphy = of_phy_find_device(psgmii_ethphy_np);
  960. + of_node_put(psgmii_ethphy_np);
  961. + if (!priv->psgmii_ethphy) {
  962. + dev_err(dev, "unable to get PSGMII eth PHY\n");
  963. + return -ENODEV;
  964. + }
  965. + }
  966. +
  967. + /* Check the detected switch id */
  968. + ret = qca8k_read_switch_id(priv);
  969. + if (ret)
  970. + return ret;
  971. +
  972. + priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL);
  973. + if (!priv->ds)
  974. + return -ENOMEM;
  975. +
  976. + priv->ds->dev = dev;
  977. + priv->ds->num_ports = QCA8K_IPQ4019_NUM_PORTS;
  978. + priv->ds->priv = priv;
  979. + priv->ds->ops = &qca8k_ipq4019_switch_ops;
  980. + mutex_init(&priv->reg_mutex);
  981. + platform_set_drvdata(pdev, priv);
  982. +
  983. + return dsa_register_switch(priv->ds);
  984. +}
  985. +
  986. +static int
  987. +qca8k_ipq4019_remove(struct platform_device *pdev)
  988. +{
  989. + struct qca8k_priv *priv = dev_get_drvdata(&pdev->dev);
  990. + int i;
  991. +
  992. + if (!priv)
  993. + return 0;
  994. +
  995. + for (i = 0; i < QCA8K_IPQ4019_NUM_PORTS; i++)
  996. + qca8k_port_set_status(priv, i, 0);
  997. +
  998. + dsa_unregister_switch(priv->ds);
  999. +
  1000. + platform_set_drvdata(pdev, NULL);
  1001. +
  1002. + return 0;
  1003. +}
  1004. +
  1005. +static const struct of_device_id qca8k_ipq4019_of_match[] = {
  1006. + { .compatible = "qca,ipq4019-qca8337n", },
  1007. + { /* sentinel */ },
  1008. +};
  1009. +
  1010. +static struct platform_driver qca8k_ipq4019_driver = {
  1011. + .probe = qca8k_ipq4019_probe,
  1012. + .remove = qca8k_ipq4019_remove,
  1013. + .driver = {
  1014. + .name = "qca8k-ipq4019",
  1015. + .of_match_table = qca8k_ipq4019_of_match,
  1016. + },
  1017. +};
  1018. +
  1019. +module_platform_driver(qca8k_ipq4019_driver);
  1020. +
  1021. +MODULE_AUTHOR("Mathieu Olivari, John Crispin <[email protected]>");
  1022. +MODULE_AUTHOR("Gabor Juhos <[email protected]>, Robert Marko <[email protected]>");
  1023. +MODULE_DESCRIPTION("Qualcomm IPQ4019 built-in switch driver");
  1024. +MODULE_LICENSE("GPL");
  1025. --- a/drivers/net/dsa/qca/qca8k.h
  1026. +++ b/drivers/net/dsa/qca/qca8k.h
  1027. @@ -19,7 +19,10 @@
  1028. #define QCA8K_ETHERNET_TIMEOUT 5
  1029. #define QCA8K_NUM_PORTS 7
  1030. +#define QCA8K_IPQ4019_NUM_PORTS 6
  1031. #define QCA8K_NUM_CPU_PORTS 2
  1032. +#define QCA8K_IPQ4019_NUM_CPU_PORTS 1
  1033. +#define QCA8K_IPQ4019_CPU_PORT 0
  1034. #define QCA8K_MAX_MTU 9000
  1035. #define QCA8K_NUM_LAGS 4
  1036. #define QCA8K_NUM_PORTS_FOR_LAG 4
  1037. @@ -28,6 +31,7 @@
  1038. #define QCA8K_ID_QCA8327 0x12
  1039. #define PHY_ID_QCA8337 0x004dd036
  1040. #define QCA8K_ID_QCA8337 0x13
  1041. +#define QCA8K_ID_IPQ4019 0x14
  1042. #define QCA8K_QCA832X_MIB_COUNT 39
  1043. #define QCA8K_QCA833X_MIB_COUNT 41
  1044. @@ -265,6 +269,7 @@
  1045. #define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3)
  1046. #define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4)
  1047. #define QCA8K_PORT_LOOKUP_LEARN BIT(20)
  1048. +#define QCA8K_PORT_LOOKUP_LOOPBACK_EN BIT(21)
  1049. #define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
  1050. #define QCA8K_REG_GOL_TRUNK_CTRL0 0x700
  1051. @@ -341,6 +346,53 @@
  1052. #define MII_ATH_MMD_ADDR 0x0d
  1053. #define MII_ATH_MMD_DATA 0x0e
  1054. +/* IPQ4019 PSGMII PHY registers */
  1055. +#define QCA8K_IPQ4019_REG_RGMII_CTRL 0x004
  1056. +#define QCA8K_IPQ4019_RGMII_CTRL_RGMII_RXC GENMASK(1, 0)
  1057. +#define QCA8K_IPQ4019_RGMII_CTRL_RGMII_TXC GENMASK(9, 8)
  1058. +/* Some kind of CLK selection
  1059. + * 0: gcc_ess_dly2ns
  1060. + * 1: gcc_ess_clk
  1061. + */
  1062. +#define QCA8K_IPQ4019_RGMII_CTRL_CLK BIT(10)
  1063. +#define QCA8K_IPQ4019_RGMII_CTRL_DELAY_RMII0 GENMASK(17, 16)
  1064. +#define QCA8K_IPQ4019_RGMII_CTRL_INVERT_RMII0_REF_CLK BIT(18)
  1065. +#define QCA8K_IPQ4019_RGMII_CTRL_DELAY_RMII1 GENMASK(20, 19)
  1066. +#define QCA8K_IPQ4019_RGMII_CTRL_INVERT_RMII1_REF_CLK BIT(21)
  1067. +#define QCA8K_IPQ4019_RGMII_CTRL_INVERT_RMII0_MASTER_EN BIT(24)
  1068. +#define QCA8K_IPQ4019_RGMII_CTRL_INVERT_RMII1_MASTER_EN BIT(25)
  1069. +
  1070. +#define PSGMIIPHY_MODE_CONTROL 0x1b4
  1071. +#define PSGMIIPHY_MODE_ATHR_CSCO_MODE_25M BIT(0)
  1072. +#define PSGMIIPHY_TX_CONTROL 0x288
  1073. +#define PSGMIIPHY_TX_CONTROL_MAGIC_VALUE 0x8380
  1074. +#define PSGMIIPHY_VCO_CALIBRATION_CONTROL_REGISTER_1 0x9c
  1075. +#define PSGMIIPHY_REG_PLL_VCO_CALIB_RESTART BIT(14)
  1076. +#define PSGMIIPHY_VCO_CALIBRATION_CONTROL_REGISTER_2 0xa0
  1077. +#define PSGMIIPHY_REG_PLL_VCO_CALIB_READY BIT(0)
  1078. +
  1079. +#define QCA8K_PSGMII_CALB_NUM 100
  1080. +#define MII_QCA8075_SSTATUS 0x11
  1081. +#define QCA8075_PHY_SPEC_STATUS_LINK BIT(10)
  1082. +#define QCA8075_MMD7_CRC_AND_PKTS_COUNT 0x8029
  1083. +#define QCA8075_MMD7_PKT_GEN_PKT_NUMB 0x8021
  1084. +#define QCA8075_MMD7_PKT_GEN_PKT_SIZE 0x8062
  1085. +#define QCA8075_MMD7_PKT_GEN_CTRL 0x8020
  1086. +#define QCA8075_MMD7_CNT_SELFCLR BIT(1)
  1087. +#define QCA8075_MMD7_CNT_FRAME_CHK_EN BIT(0)
  1088. +#define QCA8075_MMD7_PKT_GEN_START BIT(13)
  1089. +#define QCA8075_MMD7_PKT_GEN_INPROGR BIT(15)
  1090. +#define QCA8075_MMD7_IG_FRAME_RECV_CNT_HI 0x802a
  1091. +#define QCA8075_MMD7_IG_FRAME_RECV_CNT_LO 0x802b
  1092. +#define QCA8075_MMD7_IG_FRAME_ERR_CNT 0x802c
  1093. +#define QCA8075_MMD7_EG_FRAME_RECV_CNT_HI 0x802d
  1094. +#define QCA8075_MMD7_EG_FRAME_RECV_CNT_LO 0x802e
  1095. +#define QCA8075_MMD7_EG_FRAME_ERR_CNT 0x802f
  1096. +#define QCA8075_MMD7_MDIO_BRDCST_WRITE 0x8028
  1097. +#define QCA8075_MMD7_MDIO_BRDCST_WRITE_EN BIT(15)
  1098. +#define QCA8075_MDIO_BRDCST_PHY_ADDR 0x1f
  1099. +#define QCA8075_PKT_GEN_PKTS_COUNT 4096
  1100. +
  1101. enum {
  1102. QCA8K_PORT_SPEED_10M = 0,
  1103. QCA8K_PORT_SPEED_100M = 1,
  1104. @@ -466,6 +518,10 @@ struct qca8k_priv {
  1105. struct qca8k_pcs pcs_port_6;
  1106. const struct qca8k_match_data *info;
  1107. struct qca8k_led ports_led[QCA8K_LED_COUNT];
  1108. + /* IPQ4019 specific */
  1109. + struct regmap *psgmii;
  1110. + struct phy_device *psgmii_ethphy;
  1111. + bool psgmii_calibrated;
  1112. };
  1113. struct qca8k_mib_desc {