18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Microchip KSZ9477 series register access through SPI 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017-2019 Microchip Technology Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/regmap.h> 148c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "ksz_common.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define SPI_ADDR_SHIFT 24 198c2ecf20Sopenharmony_ci#define SPI_ADDR_ALIGN 3 208c2ecf20Sopenharmony_ci#define SPI_TURNAROUND_SHIFT 5 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciKSZ_REGMAP_TABLE(ksz9477, 32, SPI_ADDR_SHIFT, 238c2ecf20Sopenharmony_ci SPI_TURNAROUND_SHIFT, SPI_ADDR_ALIGN); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int ksz9477_spi_probe(struct spi_device *spi) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct regmap_config rc; 288c2ecf20Sopenharmony_ci struct ksz_device *dev; 298c2ecf20Sopenharmony_ci int i, ret; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci dev = ksz_switch_alloc(&spi->dev, spi); 328c2ecf20Sopenharmony_ci if (!dev) 338c2ecf20Sopenharmony_ci return -ENOMEM; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ksz9477_regmap_config); i++) { 368c2ecf20Sopenharmony_ci rc = ksz9477_regmap_config[i]; 378c2ecf20Sopenharmony_ci rc.lock_arg = &dev->regmap_mutex; 388c2ecf20Sopenharmony_ci dev->regmap[i] = devm_regmap_init_spi(spi, &rc); 398c2ecf20Sopenharmony_ci if (IS_ERR(dev->regmap[i])) { 408c2ecf20Sopenharmony_ci ret = PTR_ERR(dev->regmap[i]); 418c2ecf20Sopenharmony_ci dev_err(&spi->dev, 428c2ecf20Sopenharmony_ci "Failed to initialize regmap%i: %d\n", 438c2ecf20Sopenharmony_ci ksz9477_regmap_config[i].val_bits, ret); 448c2ecf20Sopenharmony_ci return ret; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (spi->dev.platform_data) 498c2ecf20Sopenharmony_ci dev->pdata = spi->dev.platform_data; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ret = ksz9477_switch_register(dev); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* Main DSA driver may not be started yet. */ 548c2ecf20Sopenharmony_ci if (ret) 558c2ecf20Sopenharmony_ci return ret; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci spi_set_drvdata(spi, dev); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int ksz9477_spi_remove(struct spi_device *spi) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct ksz_device *dev = spi_get_drvdata(spi); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (dev) 678c2ecf20Sopenharmony_ci ksz_switch_remove(dev); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void ksz9477_spi_shutdown(struct spi_device *spi) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct ksz_device *dev = spi_get_drvdata(spi); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (dev && dev->dev_ops->shutdown) 778c2ecf20Sopenharmony_ci dev->dev_ops->shutdown(dev); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const struct of_device_id ksz9477_dt_ids[] = { 818c2ecf20Sopenharmony_ci { .compatible = "microchip,ksz9477" }, 828c2ecf20Sopenharmony_ci { .compatible = "microchip,ksz9897" }, 838c2ecf20Sopenharmony_ci { .compatible = "microchip,ksz9893" }, 848c2ecf20Sopenharmony_ci { .compatible = "microchip,ksz9563" }, 858c2ecf20Sopenharmony_ci { .compatible = "microchip,ksz8563" }, 868c2ecf20Sopenharmony_ci { .compatible = "microchip,ksz9567" }, 878c2ecf20Sopenharmony_ci {}, 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ksz9477_dt_ids); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic const struct spi_device_id ksz9477_spi_ids[] = { 928c2ecf20Sopenharmony_ci { "ksz9477" }, 938c2ecf20Sopenharmony_ci { "ksz9897" }, 948c2ecf20Sopenharmony_ci { "ksz9893" }, 958c2ecf20Sopenharmony_ci { "ksz9563" }, 968c2ecf20Sopenharmony_ci { "ksz8563" }, 978c2ecf20Sopenharmony_ci { "ksz9567" }, 988c2ecf20Sopenharmony_ci { }, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, ksz9477_spi_ids); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic struct spi_driver ksz9477_spi_driver = { 1038c2ecf20Sopenharmony_ci .driver = { 1048c2ecf20Sopenharmony_ci .name = "ksz9477-switch", 1058c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1068c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ksz9477_dt_ids), 1078c2ecf20Sopenharmony_ci }, 1088c2ecf20Sopenharmony_ci .id_table = ksz9477_spi_ids, 1098c2ecf20Sopenharmony_ci .probe = ksz9477_spi_probe, 1108c2ecf20Sopenharmony_ci .remove = ksz9477_spi_remove, 1118c2ecf20Sopenharmony_ci .shutdown = ksz9477_spi_shutdown, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cimodule_spi_driver(ksz9477_spi_driver); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:ksz9477"); 1178c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:ksz9897"); 1188c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:ksz9893"); 1198c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:ksz9563"); 1208c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:ksz8563"); 1218c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:ksz9567"); 1228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>"); 1238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microchip KSZ9477 Series Switch SPI access Driver"); 1248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 125