From patchwork Wed Dec 19 03:41:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tristram.Ha@microchip.com X-Patchwork-Id: 1015817 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=microchip.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43KLKZ4Bb7z9s1c for ; Wed, 19 Dec 2018 14:41:38 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727573AbeLSDlg (ORCPT ); Tue, 18 Dec 2018 22:41:36 -0500 Received: from esa6.microchip.iphmx.com ([216.71.154.253]:34620 "EHLO esa6.microchip.iphmx.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726537AbeLSDlg (ORCPT ); Tue, 18 Dec 2018 22:41:36 -0500 X-IronPort-AV: E=Sophos;i="5.56,371,1539673200"; d="scan'208";a="21678066" Received: from smtpout.microchip.com (HELO email.microchip.com) ([198.175.253.82]) by esa6.microchip.iphmx.com with ESMTP/TLS/DHE-RSA-AES256-SHA; 18 Dec 2018 20:41:36 -0700 Received: from localhost.localdomain (10.10.76.4) by chn-sv-exch04.mchp-main.com (10.10.76.105) with Microsoft SMTP Server id 14.3.352.0; Tue, 18 Dec 2018 20:41:50 -0700 From: To: Sergio Paracuellos , Andrew Lunn , Florian Fainelli , Pavel Machek , Marek Vasut , Dan Carpenter CC: Tristram Ha , , , Subject: [PATCH v1 net-next] net: dsa: microchip: add KSZ9477 I2C driver Date: Tue, 18 Dec 2018 19:41:37 -0800 Message-ID: <1545190897-22622-1-git-send-email-Tristram.Ha@microchip.com> X-Mailer: git-send-email 1.9.1 MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Tristram Ha Add KSZ9477 I2C driver support. The code ksz9477.c and ksz_common.c are used together to generate the I2C driver. Signed-off-by: Tristram Ha Tested-by: Sergio Paracuellos --- v1 - Return error code from i2c_transfer - Change GPL license - Change author drivers/net/dsa/microchip/Kconfig | 6 + drivers/net/dsa/microchip/Makefile | 1 + drivers/net/dsa/microchip/ksz9477_i2c.c | 196 ++++++++++++++++++++++++++++++++ drivers/net/dsa/microchip/ksz_i2c.h | 69 +++++++++++ 4 files changed, 272 insertions(+) create mode 100644 drivers/net/dsa/microchip/ksz9477_i2c.c create mode 100644 drivers/net/dsa/microchip/ksz_i2c.h diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig index bea29fd..fd94441 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -9,6 +9,12 @@ menuconfig NET_DSA_MICROCHIP_KSZ9477 help This driver adds support for Microchip KSZ9477 switch chips. +config NET_DSA_MICROCHIP_KSZ9477_I2C + tristate "KSZ9477 series I2C connected switch driver" + depends on NET_DSA_MICROCHIP_KSZ9477 && I2C + help + Select to enable support for registering switches configured through I2C. + config NET_DSA_MICROCHIP_KSZ9477_SPI tristate "KSZ9477 series SPI connected switch driver" depends on NET_DSA_MICROCHIP_KSZ9477 && SPI diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile index 3142c18..dbcc5db 100644 --- a/drivers/net/dsa/microchip/Makefile +++ b/drivers/net/dsa/microchip/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) += ksz_common.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477) += ksz9477.o +obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI) += ksz9477_spi.o diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c new file mode 100644 index 0000000..29511e9 --- /dev/null +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip KSZ9477 series register access through I2C + * + * Copyright (C) 2018 Microchip Technology Inc. + */ + +#include +#include +#include + +#include "ksz_priv.h" +#include "ksz_i2c.h" + +/* Enough to read all switch port registers. */ +#define I2C_TX_BUF_LEN 0x100 + +static int ksz9477_i2c_read_reg(struct i2c_client *i2c, u32 reg, u8 *val, + unsigned int len) +{ + struct i2c_msg msg[2]; + int ret; + + val[0] = (u8)(reg >> 8); + val[1] = (u8)reg; + + msg[0].addr = i2c->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = val; + + msg[1].addr = i2c->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = &val[2]; + + ret = i2c_transfer(i2c->adapter, msg, 2); + if (ret == 2) + ret = 0; + return ret; +} + +static int ksz9477_i2c_write_reg(struct i2c_client *i2c, u32 reg, u8 *val, + unsigned int len) +{ + struct i2c_msg msg; + int ret; + + val[0] = (u8)(reg >> 8); + val[1] = (u8)reg; + + msg.addr = i2c->addr; + msg.flags = 0; + msg.len = 2 + len; + msg.buf = val; + + ret = i2c_transfer(i2c->adapter, &msg, 1); + if (ret == 1) + ret = 0; + return ret; +} + +static int ksz_i2c_read(struct ksz_device *dev, u32 reg, u8 *data, + unsigned int len) +{ + struct i2c_client *i2c = dev->priv; + int ret; + + ret = ksz9477_i2c_read_reg(i2c, reg, dev->txbuf, len); + if (!ret) + memcpy(data, &dev->txbuf[2], len); + return ret; +} + +static int ksz_i2c_write(struct ksz_device *dev, u32 reg, void *data, + unsigned int len) +{ + struct i2c_client *i2c = dev->priv; + + if (len > I2C_TX_BUF_LEN) + len = I2C_TX_BUF_LEN; + memcpy(&dev->txbuf[2], data, len); + return ksz9477_i2c_write_reg(i2c, reg, dev->txbuf, len); +} + +static int ksz_i2c_read24(struct ksz_device *dev, u32 reg, u32 *val) +{ + int ret; + + *val = 0; + ret = ksz_i2c_read(dev, reg, (u8 *)val, 3); + if (!ret) { + *val = be32_to_cpu(*val); + /* convert to 24bit */ + *val >>= 8; + } + + return ret; +} + +static int ksz_i2c_write24(struct ksz_device *dev, u32 reg, u32 value) +{ + /* make it to big endian 24bit from MSB */ + value <<= 8; + value = cpu_to_be32(value); + return ksz_i2c_write(dev, reg, &value, 3); +} + +static const struct ksz_io_ops ksz9477_i2c_ops = { + .read8 = ksz_i2c_read8, + .read16 = ksz_i2c_read16, + .read24 = ksz_i2c_read24, + .read32 = ksz_i2c_read32, + .write8 = ksz_i2c_write8, + .write16 = ksz_i2c_write16, + .write24 = ksz_i2c_write24, + .write32 = ksz_i2c_write32, + .get = ksz_i2c_get, + .set = ksz_i2c_set, +}; + +static int ksz9477_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) +{ + struct ksz_device *dev; + int ret; + + dev = ksz_switch_alloc(&i2c->dev, &ksz9477_i2c_ops, i2c); + if (!dev) + return -ENOMEM; + + if (i2c->dev.platform_data) + dev->pdata = i2c->dev.platform_data; + + dev->txbuf = devm_kzalloc(dev->dev, 2 + I2C_TX_BUF_LEN, GFP_KERNEL); + + ret = ksz9477_switch_register(dev); + + /* Main DSA driver may not be started yet. */ + if (ret) + return ret; + + i2c_set_clientdata(i2c, dev); + + return 0; +} + +static int ksz9477_i2c_remove(struct i2c_client *i2c) +{ + struct ksz_device *dev = i2c_get_clientdata(i2c); + + if (dev) + ksz_switch_remove(dev); + + return 0; +} + +static void ksz9477_i2c_shutdown(struct i2c_client *i2c) +{ + struct ksz_device *dev = i2c_get_clientdata(i2c); + + if (dev && dev->dev_ops->shutdown) + dev->dev_ops->shutdown(dev); +} + +static const struct i2c_device_id ksz9477_i2c_id[] = { + { "ksz9477-switch", 0 }, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ksz9477_i2c_id); + +static const struct of_device_id ksz9477_dt_ids[] = { + { .compatible = "microchip,ksz9477" }, + { .compatible = "microchip,ksz9897" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); + +static struct i2c_driver ksz9477_i2c_driver = { + .driver = { + .name = "ksz9477-switch", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ksz9477_dt_ids), + }, + .probe = ksz9477_i2c_probe, + .remove = ksz9477_i2c_remove, + .shutdown = ksz9477_i2c_shutdown, + .id_table = ksz9477_i2c_id, +}; + +module_i2c_driver(ksz9477_i2c_driver); + +MODULE_AUTHOR("Tristram Ha "); +MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch I2C access Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/microchip/ksz_i2c.h b/drivers/net/dsa/microchip/ksz_i2c.h new file mode 100644 index 0000000..b9af0a8 --- /dev/null +++ b/drivers/net/dsa/microchip/ksz_i2c.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Microchip KSZ series I2C access common header + * + * Copyright (C) 2018 Microchip Technology Inc. + * Tristram Ha + */ + +#ifndef __KSZ_I2C_H +#define __KSZ_I2C_H + +/* Chip dependent I2C access */ +static int ksz_i2c_read(struct ksz_device *dev, u32 reg, u8 *data, + unsigned int len); +static int ksz_i2c_write(struct ksz_device *dev, u32 reg, void *data, + unsigned int len); + +static int ksz_i2c_read8(struct ksz_device *dev, u32 reg, u8 *val) +{ + return ksz_i2c_read(dev, reg, val, 1); +} + +static int ksz_i2c_read16(struct ksz_device *dev, u32 reg, u16 *val) +{ + int ret = ksz_i2c_read(dev, reg, (u8 *)val, 2); + + if (!ret) + *val = be16_to_cpu(*val); + + return ret; +} + +static int ksz_i2c_read32(struct ksz_device *dev, u32 reg, u32 *val) +{ + int ret = ksz_i2c_read(dev, reg, (u8 *)val, 4); + + if (!ret) + *val = be32_to_cpu(*val); + + return ret; +} + +static int ksz_i2c_write8(struct ksz_device *dev, u32 reg, u8 value) +{ + return ksz_i2c_write(dev, reg, &value, 1); +} + +static int ksz_i2c_write16(struct ksz_device *dev, u32 reg, u16 value) +{ + value = cpu_to_be16(value); + return ksz_i2c_write(dev, reg, &value, 2); +} + +static int ksz_i2c_write32(struct ksz_device *dev, u32 reg, u32 value) +{ + value = cpu_to_be32(value); + return ksz_i2c_write(dev, reg, &value, 4); +} + +static int ksz_i2c_get(struct ksz_device *dev, u32 reg, void *data, size_t len) +{ + return ksz_i2c_read(dev, reg, data, len); +} + +static int ksz_i2c_set(struct ksz_device *dev, u32 reg, void *data, size_t len) +{ + return ksz_i2c_write(dev, reg, data, len); +} + +#endif