2022-09-05 09:21:32 -07:00
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Core driver for the Ocelot chip family .
*
* The VSC7511 , 7512 , 7513 , and 7514 can be controlled internally via an
* on - chip MIPS processor , or externally via SPI , I2C , PCIe . This core driver is
* intended to be the bus - agnostic glue between , for example , the SPI bus and
* the child devices .
*
* Copyright 2021 - 2022 Innovative Advantage Inc .
*
* Author : Colin Foster < colin . foster @ in - advantage . com >
*/
# include <linux/bits.h>
# include <linux/device.h>
# include <linux/export.h>
# include <linux/iopoll.h>
# include <linux/ioport.h>
# include <linux/kernel.h>
# include <linux/mfd/core.h>
# include <linux/mfd/ocelot.h>
# include <linux/module.h>
# include <linux/regmap.h>
# include <linux/types.h>
# include <soc/mscc/ocelot.h>
# include "ocelot.h"
# define REG_GCB_SOFT_RST 0x0008
# define BIT_SOFT_CHIP_RST BIT(0)
# define VSC7512_MIIM0_RES_START 0x7107009c
# define VSC7512_MIIM1_RES_START 0x710700c0
2023-01-27 11:35:55 -08:00
# define VSC7512_MIIM_RES_SIZE 0x00000024
2022-09-05 09:21:32 -07:00
# define VSC7512_PHY_RES_START 0x710700f0
2023-01-27 11:35:55 -08:00
# define VSC7512_PHY_RES_SIZE 0x00000004
2022-09-05 09:21:32 -07:00
# define VSC7512_GPIO_RES_START 0x71070034
2023-01-27 11:35:55 -08:00
# define VSC7512_GPIO_RES_SIZE 0x0000006c
2022-09-05 09:21:32 -07:00
# define VSC7512_SIO_CTRL_RES_START 0x710700f8
2023-01-27 11:35:55 -08:00
# define VSC7512_SIO_CTRL_RES_SIZE 0x00000100
2022-09-05 09:21:32 -07:00
2023-03-17 11:54:08 -07:00
# define VSC7512_HSIO_RES_START 0x710d0000
# define VSC7512_HSIO_RES_SIZE 0x00000128
2023-01-27 11:35:59 -08:00
# define VSC7512_ANA_RES_START 0x71880000
# define VSC7512_ANA_RES_SIZE 0x00010000
# define VSC7512_QS_RES_START 0x71080000
# define VSC7512_QS_RES_SIZE 0x00000100
# define VSC7512_QSYS_RES_START 0x71800000
# define VSC7512_QSYS_RES_SIZE 0x00200000
# define VSC7512_REW_RES_START 0x71030000
# define VSC7512_REW_RES_SIZE 0x00010000
# define VSC7512_SYS_RES_START 0x71010000
# define VSC7512_SYS_RES_SIZE 0x00010000
# define VSC7512_S0_RES_START 0x71040000
# define VSC7512_S1_RES_START 0x71050000
# define VSC7512_S2_RES_START 0x71060000
# define VCAP_RES_SIZE 0x00000400
# define VSC7512_PORT_0_RES_START 0x711e0000
# define VSC7512_PORT_1_RES_START 0x711f0000
# define VSC7512_PORT_2_RES_START 0x71200000
# define VSC7512_PORT_3_RES_START 0x71210000
# define VSC7512_PORT_4_RES_START 0x71220000
# define VSC7512_PORT_5_RES_START 0x71230000
# define VSC7512_PORT_6_RES_START 0x71240000
# define VSC7512_PORT_7_RES_START 0x71250000
# define VSC7512_PORT_8_RES_START 0x71260000
# define VSC7512_PORT_9_RES_START 0x71270000
# define VSC7512_PORT_10_RES_START 0x71280000
# define VSC7512_PORT_RES_SIZE 0x00010000
2022-09-05 09:21:32 -07:00
# define VSC7512_GCB_RST_SLEEP_US 100
# define VSC7512_GCB_RST_TIMEOUT_US 100000
static int ocelot_gcb_chip_rst_status ( struct ocelot_ddata * ddata )
{
int val , err ;
err = regmap_read ( ddata - > gcb_regmap , REG_GCB_SOFT_RST , & val ) ;
if ( err )
return err ;
return val ;
}
int ocelot_chip_reset ( struct device * dev )
{
struct ocelot_ddata * ddata = dev_get_drvdata ( dev ) ;
int ret , val ;
/*
* Reset the entire chip here to put it into a completely known state .
* Other drivers may want to reset their own subsystems . The register
* self - clears , so one write is all that is needed and wait for it to
* clear .
*/
ret = regmap_write ( ddata - > gcb_regmap , REG_GCB_SOFT_RST , BIT_SOFT_CHIP_RST ) ;
if ( ret )
return ret ;
return readx_poll_timeout ( ocelot_gcb_chip_rst_status , ddata , val , ! val ,
VSC7512_GCB_RST_SLEEP_US , VSC7512_GCB_RST_TIMEOUT_US ) ;
}
EXPORT_SYMBOL_NS ( ocelot_chip_reset , MFD_OCELOT ) ;
static const struct resource vsc7512_miim0_resources [ ] = {
DEFINE_RES_REG_NAMED ( VSC7512_MIIM0_RES_START , VSC7512_MIIM_RES_SIZE , " gcb_miim0 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PHY_RES_START , VSC7512_PHY_RES_SIZE , " gcb_phy " ) ,
} ;
static const struct resource vsc7512_miim1_resources [ ] = {
DEFINE_RES_REG_NAMED ( VSC7512_MIIM1_RES_START , VSC7512_MIIM_RES_SIZE , " gcb_miim1 " ) ,
} ;
static const struct resource vsc7512_pinctrl_resources [ ] = {
DEFINE_RES_REG_NAMED ( VSC7512_GPIO_RES_START , VSC7512_GPIO_RES_SIZE , " gcb_gpio " ) ,
} ;
static const struct resource vsc7512_sgpio_resources [ ] = {
DEFINE_RES_REG_NAMED ( VSC7512_SIO_CTRL_RES_START , VSC7512_SIO_CTRL_RES_SIZE , " gcb_sio " ) ,
} ;
2023-03-17 11:54:08 -07:00
static const struct resource vsc7512_serdes_resources [ ] = {
DEFINE_RES_REG_NAMED ( VSC7512_HSIO_RES_START , VSC7512_HSIO_RES_SIZE , " hsio " ) ,
} ;
2023-01-27 11:35:59 -08:00
static const struct resource vsc7512_switch_resources [ ] = {
DEFINE_RES_REG_NAMED ( VSC7512_ANA_RES_START , VSC7512_ANA_RES_SIZE , " ana " ) ,
2023-03-17 11:54:08 -07:00
DEFINE_RES_REG_NAMED ( VSC7512_HSIO_RES_START , VSC7512_HSIO_RES_SIZE , " hsio " ) ,
2023-01-27 11:35:59 -08:00
DEFINE_RES_REG_NAMED ( VSC7512_QS_RES_START , VSC7512_QS_RES_SIZE , " qs " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_QSYS_RES_START , VSC7512_QSYS_RES_SIZE , " qsys " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_REW_RES_START , VSC7512_REW_RES_SIZE , " rew " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_SYS_RES_START , VSC7512_SYS_RES_SIZE , " sys " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_S0_RES_START , VCAP_RES_SIZE , " s0 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_S1_RES_START , VCAP_RES_SIZE , " s1 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_S2_RES_START , VCAP_RES_SIZE , " s2 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_0_RES_START , VSC7512_PORT_RES_SIZE , " port0 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_1_RES_START , VSC7512_PORT_RES_SIZE , " port1 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_2_RES_START , VSC7512_PORT_RES_SIZE , " port2 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_3_RES_START , VSC7512_PORT_RES_SIZE , " port3 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_4_RES_START , VSC7512_PORT_RES_SIZE , " port4 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_5_RES_START , VSC7512_PORT_RES_SIZE , " port5 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_6_RES_START , VSC7512_PORT_RES_SIZE , " port6 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_7_RES_START , VSC7512_PORT_RES_SIZE , " port7 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_8_RES_START , VSC7512_PORT_RES_SIZE , " port8 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_9_RES_START , VSC7512_PORT_RES_SIZE , " port9 " ) ,
DEFINE_RES_REG_NAMED ( VSC7512_PORT_10_RES_START , VSC7512_PORT_RES_SIZE , " port10 " )
} ;
2022-09-05 09:21:32 -07:00
static const struct mfd_cell vsc7512_devs [ ] = {
{
. name = " ocelot-pinctrl " ,
. of_compatible = " mscc,ocelot-pinctrl " ,
. num_resources = ARRAY_SIZE ( vsc7512_pinctrl_resources ) ,
. resources = vsc7512_pinctrl_resources ,
} , {
. name = " ocelot-sgpio " ,
. of_compatible = " mscc,ocelot-sgpio " ,
. num_resources = ARRAY_SIZE ( vsc7512_sgpio_resources ) ,
. resources = vsc7512_sgpio_resources ,
} , {
. name = " ocelot-miim0 " ,
. of_compatible = " mscc,ocelot-miim " ,
. of_reg = VSC7512_MIIM0_RES_START ,
. use_of_reg = true ,
. num_resources = ARRAY_SIZE ( vsc7512_miim0_resources ) ,
. resources = vsc7512_miim0_resources ,
} , {
. name = " ocelot-miim1 " ,
. of_compatible = " mscc,ocelot-miim " ,
. of_reg = VSC7512_MIIM1_RES_START ,
. use_of_reg = true ,
. num_resources = ARRAY_SIZE ( vsc7512_miim1_resources ) ,
. resources = vsc7512_miim1_resources ,
2023-03-17 11:54:08 -07:00
} , {
. name = " ocelot-serdes " ,
. of_compatible = " mscc,vsc7514-serdes " ,
. num_resources = ARRAY_SIZE ( vsc7512_serdes_resources ) ,
. resources = vsc7512_serdes_resources ,
2023-01-27 11:35:59 -08:00
} , {
2023-02-24 17:52:35 +02:00
. name = " ocelot-ext-switch " ,
2023-01-27 11:35:59 -08:00
. of_compatible = " mscc,vsc7512-switch " ,
. num_resources = ARRAY_SIZE ( vsc7512_switch_resources ) ,
. resources = vsc7512_switch_resources ,
2022-09-05 09:21:32 -07:00
} ,
} ;
static void ocelot_core_try_add_regmap ( struct device * dev ,
const struct resource * res )
{
if ( dev_get_regmap ( dev , res - > name ) )
return ;
ocelot_spi_init_regmap ( dev , res ) ;
}
static void ocelot_core_try_add_regmaps ( struct device * dev ,
const struct mfd_cell * cell )
{
int i ;
for ( i = 0 ; i < cell - > num_resources ; i + + )
ocelot_core_try_add_regmap ( dev , & cell - > resources [ i ] ) ;
}
int ocelot_core_init ( struct device * dev )
{
int i , ndevs ;
ndevs = ARRAY_SIZE ( vsc7512_devs ) ;
for ( i = 0 ; i < ndevs ; i + + )
ocelot_core_try_add_regmaps ( dev , & vsc7512_devs [ i ] ) ;
return devm_mfd_add_devices ( dev , PLATFORM_DEVID_AUTO , vsc7512_devs , ndevs , NULL , 0 , NULL ) ;
}
EXPORT_SYMBOL_NS ( ocelot_core_init , MFD_OCELOT ) ;
MODULE_DESCRIPTION ( " Externally Controlled Ocelot Chip Driver " ) ;
MODULE_AUTHOR ( " Colin Foster <colin.foster@in-advantage.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_IMPORT_NS ( MFD_OCELOT_SPI ) ;