162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* $Date: 2005/11/12 02:13:49 $ $RCSfile: my3126.c,v $ $Revision: 1.15 $ */ 362306a36Sopenharmony_ci#include "cphy.h" 462306a36Sopenharmony_ci#include "elmer0.h" 562306a36Sopenharmony_ci#include "suni1x10gexp_regs.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* Port Reset */ 862306a36Sopenharmony_cistatic int my3126_reset(struct cphy *cphy, int wait) 962306a36Sopenharmony_ci{ 1062306a36Sopenharmony_ci /* 1162306a36Sopenharmony_ci * This can be done through registers. It is not required since 1262306a36Sopenharmony_ci * a full chip reset is used. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci return 0; 1562306a36Sopenharmony_ci} 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic int my3126_interrupt_enable(struct cphy *cphy) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci schedule_delayed_work(&cphy->phy_update, HZ/30); 2062306a36Sopenharmony_ci t1_tpi_read(cphy->adapter, A_ELMER0_GPO, &cphy->elmer_gpo); 2162306a36Sopenharmony_ci return 0; 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int my3126_interrupt_disable(struct cphy *cphy) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci cancel_delayed_work_sync(&cphy->phy_update); 2762306a36Sopenharmony_ci return 0; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int my3126_interrupt_clear(struct cphy *cphy) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci return 0; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define OFFSET(REG_ADDR) (REG_ADDR << 2) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int my3126_interrupt_handler(struct cphy *cphy) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci u32 val; 4062306a36Sopenharmony_ci u16 val16; 4162306a36Sopenharmony_ci u16 status; 4262306a36Sopenharmony_ci u32 act_count; 4362306a36Sopenharmony_ci adapter_t *adapter; 4462306a36Sopenharmony_ci adapter = cphy->adapter; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (cphy->count == 50) { 4762306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 4862306a36Sopenharmony_ci val16 = (u16) val; 4962306a36Sopenharmony_ci status = cphy->bmsr ^ val16; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (status & MDIO_STAT1_LSTATUS) 5262306a36Sopenharmony_ci t1_link_changed(adapter, 0); 5362306a36Sopenharmony_ci cphy->bmsr = val16; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* We have only enabled link change interrupts so it 5662306a36Sopenharmony_ci must be that 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci cphy->count = 0; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci t1_tpi_write(adapter, OFFSET(SUNI1x10GEXP_REG_MSTAT_CONTROL), 6262306a36Sopenharmony_ci SUNI1x10GEXP_BITMSK_MSTAT_SNAP); 6362306a36Sopenharmony_ci t1_tpi_read(adapter, 6462306a36Sopenharmony_ci OFFSET(SUNI1x10GEXP_REG_MSTAT_COUNTER_1_LOW), &act_count); 6562306a36Sopenharmony_ci t1_tpi_read(adapter, 6662306a36Sopenharmony_ci OFFSET(SUNI1x10GEXP_REG_MSTAT_COUNTER_33_LOW), &val); 6762306a36Sopenharmony_ci act_count += val; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Populate elmer_gpo with the register value */ 7062306a36Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_GPO, &val); 7162306a36Sopenharmony_ci cphy->elmer_gpo = val; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if ( (val & (1 << 8)) || (val & (1 << 19)) || 7462306a36Sopenharmony_ci (cphy->act_count == act_count) || cphy->act_on ) { 7562306a36Sopenharmony_ci if (is_T2(adapter)) 7662306a36Sopenharmony_ci val |= (1 << 9); 7762306a36Sopenharmony_ci else if (t1_is_T1B(adapter)) 7862306a36Sopenharmony_ci val |= (1 << 20); 7962306a36Sopenharmony_ci cphy->act_on = 0; 8062306a36Sopenharmony_ci } else { 8162306a36Sopenharmony_ci if (is_T2(adapter)) 8262306a36Sopenharmony_ci val &= ~(1 << 9); 8362306a36Sopenharmony_ci else if (t1_is_T1B(adapter)) 8462306a36Sopenharmony_ci val &= ~(1 << 20); 8562306a36Sopenharmony_ci cphy->act_on = 1; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci cphy->elmer_gpo = val; 9162306a36Sopenharmony_ci cphy->act_count = act_count; 9262306a36Sopenharmony_ci cphy->count++; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return cphy_cause_link_change; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void my3126_poll(struct work_struct *work) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct cphy *cphy = container_of(work, struct cphy, phy_update.work); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci my3126_interrupt_handler(cphy); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int my3126_set_loopback(struct cphy *cphy, int on) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* To check the activity LED */ 11062306a36Sopenharmony_cistatic int my3126_get_link_status(struct cphy *cphy, 11162306a36Sopenharmony_ci int *link_ok, int *speed, int *duplex, int *fc) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci u32 val; 11462306a36Sopenharmony_ci u16 val16; 11562306a36Sopenharmony_ci adapter_t *adapter; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci adapter = cphy->adapter; 11862306a36Sopenharmony_ci cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val); 11962306a36Sopenharmony_ci val16 = (u16) val; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Populate elmer_gpo with the register value */ 12262306a36Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_GPO, &val); 12362306a36Sopenharmony_ci cphy->elmer_gpo = val; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci *link_ok = (val16 & MDIO_STAT1_LSTATUS); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (*link_ok) { 12862306a36Sopenharmony_ci /* Turn on the LED. */ 12962306a36Sopenharmony_ci if (is_T2(adapter)) 13062306a36Sopenharmony_ci val &= ~(1 << 8); 13162306a36Sopenharmony_ci else if (t1_is_T1B(adapter)) 13262306a36Sopenharmony_ci val &= ~(1 << 19); 13362306a36Sopenharmony_ci } else { 13462306a36Sopenharmony_ci /* Turn off the LED. */ 13562306a36Sopenharmony_ci if (is_T2(adapter)) 13662306a36Sopenharmony_ci val |= (1 << 8); 13762306a36Sopenharmony_ci else if (t1_is_T1B(adapter)) 13862306a36Sopenharmony_ci val |= (1 << 19); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val); 14262306a36Sopenharmony_ci cphy->elmer_gpo = val; 14362306a36Sopenharmony_ci *speed = SPEED_10000; 14462306a36Sopenharmony_ci *duplex = DUPLEX_FULL; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* need to add flow control */ 14762306a36Sopenharmony_ci if (fc) 14862306a36Sopenharmony_ci *fc = PAUSE_RX | PAUSE_TX; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void my3126_destroy(struct cphy *cphy) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci kfree(cphy); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic const struct cphy_ops my3126_ops = { 15962306a36Sopenharmony_ci .destroy = my3126_destroy, 16062306a36Sopenharmony_ci .reset = my3126_reset, 16162306a36Sopenharmony_ci .interrupt_enable = my3126_interrupt_enable, 16262306a36Sopenharmony_ci .interrupt_disable = my3126_interrupt_disable, 16362306a36Sopenharmony_ci .interrupt_clear = my3126_interrupt_clear, 16462306a36Sopenharmony_ci .interrupt_handler = my3126_interrupt_handler, 16562306a36Sopenharmony_ci .get_link_status = my3126_get_link_status, 16662306a36Sopenharmony_ci .set_loopback = my3126_set_loopback, 16762306a36Sopenharmony_ci .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | 16862306a36Sopenharmony_ci MDIO_DEVS_PHYXS), 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct cphy *my3126_phy_create(struct net_device *dev, 17262306a36Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct cphy *cphy = kzalloc(sizeof (*cphy), GFP_KERNEL); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!cphy) 17762306a36Sopenharmony_ci return NULL; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci cphy_init(cphy, dev, phy_addr, &my3126_ops, mdio_ops); 18062306a36Sopenharmony_ci INIT_DELAYED_WORK(&cphy->phy_update, my3126_poll); 18162306a36Sopenharmony_ci cphy->bmsr = 0; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return cphy; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* Chip Reset */ 18762306a36Sopenharmony_cistatic int my3126_phy_reset(adapter_t * adapter) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci u32 val; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_GPO, &val); 19262306a36Sopenharmony_ci val &= ~4; 19362306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val); 19462306a36Sopenharmony_ci msleep(100); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val | 4); 19762306a36Sopenharmony_ci msleep(1000); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Now lets enable the Laser. Delay 100us */ 20062306a36Sopenharmony_ci t1_tpi_read(adapter, A_ELMER0_GPO, &val); 20162306a36Sopenharmony_ci val |= 0x8000; 20262306a36Sopenharmony_ci t1_tpi_write(adapter, A_ELMER0_GPO, val); 20362306a36Sopenharmony_ci udelay(100); 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciconst struct gphy t1_my3126_ops = { 20862306a36Sopenharmony_ci .create = my3126_phy_create, 20962306a36Sopenharmony_ci .reset = my3126_phy_reset 21062306a36Sopenharmony_ci}; 211