All of lore.kernel.org
 help / color / mirror / Atom feed
From: Joe Hershberger <joe.hershberger@gmail.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v2 3/8] drivers: net: keystone_net: convert driver to adopt device driver model
Date: Fri, 22 Jan 2016 14:15:04 -0600	[thread overview]
Message-ID: <CANr=Z=bvzwzFawtF_TAcbcjV2QiHSQCFdSmveg36UqC9DM-Ymg@mail.gmail.com> (raw)
In-Reply-To: <1453451002-21444-4-git-send-email-mugunthanvnm@ti.com>

On Fri, Jan 22, 2016 at 2:23 AM, Mugunthan V N <mugunthanvnm@ti.com> wrote:
> Adopt keystone_net driver to adopt device driver model
>
> Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
> Reviewed-by: Tom Rini <trini@konsulko.com>
> ---
>  drivers/net/keystone_net.c | 418 ++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 376 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/net/keystone_net.c b/drivers/net/keystone_net.c
> index 209fae9..21ebaeb 100644
> --- a/drivers/net/keystone_net.c
> +++ b/drivers/net/keystone_net.c
> @@ -10,6 +10,8 @@
>  #include <command.h>
>  #include <console.h>
>
> +#include <dm.h>
> +
>  #include <net.h>
>  #include <phy.h>
>  #include <errno.h>
> @@ -18,10 +20,15 @@
>  #include <asm/ti-common/keystone_nav.h>
>  #include <asm/ti-common/keystone_net.h>
>  #include <asm/ti-common/keystone_serdes.h>
> +#include <asm/arch/psc_defs.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
>
> +#ifndef CONFIG_DM_ETH
>  unsigned int emac_open;
>  static struct mii_dev *mdio_bus;
>  static unsigned int sys_has_mdio = 1;
> +#endif
>
>  #ifdef KEYSTONE2_EMAC_GIG_ENABLE
>  #define emac_gigabit_enable(x) keystone2_eth_gigabit_enable(x)
> @@ -36,40 +43,44 @@ static unsigned int sys_has_mdio = 1;
>
>  static u8 rx_buffs[RX_BUFF_NUMS * RX_BUFF_LEN] __aligned(16);
>
> +#ifndef CONFIG_DM_ETH
>  struct rx_buff_desc net_rx_buffs = {
>         .buff_ptr       = rx_buffs,
>         .num_buffs      = RX_BUFF_NUMS,
>         .buff_len       = RX_BUFF_LEN,
>         .rx_flow        = 22,
>  };
> -
> -#ifndef CONFIG_SOC_K2G
> -static void keystone2_net_serdes_setup(void);
>  #endif
>
> -int keystone2_eth_read_mac_addr(struct eth_device *dev)
> -{
> -       struct eth_priv_t *eth_priv;
> -       u32 maca = 0;
> -       u32 macb = 0;
> -
> -       eth_priv = (struct eth_priv_t *)dev->priv;
> +#ifdef CONFIG_DM_ETH
>
> -       /* Read the e-fuse mac address */
> -       if (eth_priv->slave_port == 1) {
> -               maca = __raw_readl(MAC_ID_BASE_ADDR);
> -               macb = __raw_readl(MAC_ID_BASE_ADDR + 4);
> -       }
> -
> -       dev->enetaddr[0] = (macb >>  8) & 0xff;
> -       dev->enetaddr[1] = (macb >>  0) & 0xff;
> -       dev->enetaddr[2] = (maca >> 24) & 0xff;
> -       dev->enetaddr[3] = (maca >> 16) & 0xff;
> -       dev->enetaddr[4] = (maca >>  8) & 0xff;
> -       dev->enetaddr[5] = (maca >>  0) & 0xff;
> +enum link_type {
> +       LINK_TYPE_MAC_TO_MAC_AUTO = 0,
> +       LINK_TYPE_MAC_TO_PHY_MODE = 1,
> +       LINK_TYPE_MAC_TO_MAC_FORCED_MODE = 2,
> +       LINK_TYPE_MAC_TO_FIBRE_MODE = 3,
> +       LINK_TYPE_MAC_TO_PHY_NO_MDIO_MODE = 4,
> +       LINK_TYPE_10G_MAC_TO_PHY_MODE = 10,
> +       LINK_TYPE_10G_MAC_TO_MAC_FORCED_MODE = 11,
> +};
>
> -       return 0;
> -}
> +struct ks2_eth_priv {
> +       struct udevice                  *dev;
> +       struct phy_device               *phydev;
> +       struct mii_dev                  *mdio_bus;
> +       int                             phy_addr;
> +       phy_interface_t                 phy_if;
> +       int                             sgmii_link_type;
> +       void                            *mdio_base;
> +       struct rx_buff_desc             net_rx_buffs;
> +       struct pktdma_cfg               *netcp_pktdma;
> +       void                            *hd;
> +       int                             slave_port;
> +       enum link_type                  link_type;
> +       bool                            emac_open;
> +       bool                            has_mdio;
> +};
> +#endif
>
>  /* MDIO */
>
> @@ -140,6 +151,7 @@ static int keystone2_mdio_write(struct mii_dev *bus,
>         return 0;
>  }
>
> +#ifndef CONFIG_DM_ETH
>  static void  __attribute__((unused))
>         keystone2_eth_gigabit_enable(struct eth_device *dev)
>  {
> @@ -163,6 +175,31 @@ static void  __attribute__((unused))
>                EMAC_MACCONTROL_GIGFORCE | EMAC_MACCONTROL_GIGABIT_ENABLE,
>                DEVICE_EMACSL_BASE(eth_priv->slave_port - 1) + CPGMACSL_REG_CTL);
>  }
> +#else
> +static void  __attribute__((unused))
> +       keystone2_eth_gigabit_enable(struct udevice *dev)
> +{
> +       struct ks2_eth_priv *priv = dev_get_priv(dev);
> +       u_int16_t data;
> +
> +       if (priv->has_mdio) {
> +               data = keystone2_mdio_read(priv->mdio_bus, priv->phy_addr,
> +                                          MDIO_DEVAD_NONE, 0);
> +               /* speed selection MSB */
> +               if (!(data & (1 << 6)))
> +                       return;
> +       }
> +
> +       /*
> +        * Check if link detected is giga-bit
> +        * If Gigabit mode detected, enable gigbit in MAC
> +        */
> +       writel(readl(DEVICE_EMACSL_BASE(priv->slave_port - 1) +
> +                    CPGMACSL_REG_CTL) |
> +              EMAC_MACCONTROL_GIGFORCE | EMAC_MACCONTROL_GIGABIT_ENABLE,
> +              DEVICE_EMACSL_BASE(priv->slave_port - 1) + CPGMACSL_REG_CTL);
> +}
> +#endif
>
>  #ifdef CONFIG_SOC_K2G
>  int keystone_rgmii_config(struct phy_device *phy_dev)
> @@ -401,6 +438,58 @@ int ethss_stop(void)
>         return 0;
>  }
>
> +struct ks2_serdes ks2_serdes_sgmii_156p25mhz = {
> +       .clk = SERDES_CLOCK_156P25M,
> +       .rate = SERDES_RATE_5G,
> +       .rate_mode = SERDES_QUARTER_RATE,
> +       .intf = SERDES_PHY_SGMII,
> +       .loopback = 0,
> +};
> +
> +#ifndef CONFIG_SOC_K2G
> +static void keystone2_net_serdes_setup(void)
> +{
> +       ks2_serdes_init(CONFIG_KSNET_SERDES_SGMII_BASE,
> +                       &ks2_serdes_sgmii_156p25mhz,
> +                       CONFIG_KSNET_SERDES_LANES_PER_SGMII);
> +
> +#if defined(CONFIG_SOC_K2E) || defined(CONFIG_SOC_K2L)
> +       ks2_serdes_init(CONFIG_KSNET_SERDES_SGMII2_BASE,
> +                       &ks2_serdes_sgmii_156p25mhz,
> +                       CONFIG_KSNET_SERDES_LANES_PER_SGMII);
> +#endif
> +
> +       /* wait till setup */
> +       udelay(5000);
> +}
> +#endif
> +
> +#ifndef CONFIG_DM_ETH
> +
> +int keystone2_eth_read_mac_addr(struct eth_device *dev)
> +{
> +       struct eth_priv_t *eth_priv;
> +       u32 maca = 0;
> +       u32 macb = 0;
> +
> +       eth_priv = (struct eth_priv_t *)dev->priv;
> +
> +       /* Read the e-fuse mac address */
> +       if (eth_priv->slave_port == 1) {
> +               maca = __raw_readl(MAC_ID_BASE_ADDR);
> +               macb = __raw_readl(MAC_ID_BASE_ADDR + 4);
> +       }
> +
> +       dev->enetaddr[0] = (macb >>  8) & 0xff;
> +       dev->enetaddr[1] = (macb >>  0) & 0xff;
> +       dev->enetaddr[2] = (maca >> 24) & 0xff;
> +       dev->enetaddr[3] = (maca >> 16) & 0xff;
> +       dev->enetaddr[4] = (maca >>  8) & 0xff;
> +       dev->enetaddr[5] = (maca >>  0) & 0xff;
> +
> +       return 0;
> +}
> +
>  int32_t cpmac_drv_send(u32 *buffer, int num_bytes, int slave_port_num)
>  {
>         if (num_bytes < EMAC_MIN_ETHERNET_PKT_SIZE)
> @@ -556,6 +645,7 @@ int keystone2_emac_initialize(struct eth_priv_t *eth_priv)
>         int res;
>         struct eth_device *dev;
>         struct phy_device *phy_dev;
> +       struct mdio_regs *adap_mdio = (struct mdio_regs *)EMAC_MDIO_BASE_ADDR;
>
>         dev = malloc(sizeof(struct eth_device));
>         if (dev == NULL)
> @@ -612,28 +702,272 @@ int keystone2_emac_initialize(struct eth_priv_t *eth_priv)
>         return 0;
>  }
>
> -struct ks2_serdes ks2_serdes_sgmii_156p25mhz = {
> -       .clk = SERDES_CLOCK_156P25M,
> -       .rate = SERDES_RATE_5G,
> -       .rate_mode = SERDES_QUARTER_RATE,
> -       .intf = SERDES_PHY_SGMII,
> -       .loopback = 0,
> -};
> +#else
>
> -#ifndef CONFIG_SOC_K2G
> -static void keystone2_net_serdes_setup(void)
> +int keystone2_eth_read_mac_addr(struct ks2_eth_priv *priv,
> +                               struct eth_pdata *pdata)
>  {
> -       ks2_serdes_init(CONFIG_KSNET_SERDES_SGMII_BASE,
> -                       &ks2_serdes_sgmii_156p25mhz,
> -                       CONFIG_KSNET_SERDES_LANES_PER_SGMII);
> +       u32 maca = 0;
> +       u32 macb = 0;
>
> -#if defined(CONFIG_SOC_K2E) || defined(CONFIG_SOC_K2L)
> -       ks2_serdes_init(CONFIG_KSNET_SERDES_SGMII2_BASE,
> -                       &ks2_serdes_sgmii_156p25mhz,
> -                       CONFIG_KSNET_SERDES_LANES_PER_SGMII);
> +       /* Read the e-fuse mac address */
> +       if (priv->slave_port == 1) {
> +               maca = __raw_readl(MAC_ID_BASE_ADDR);
> +               macb = __raw_readl(MAC_ID_BASE_ADDR + 4);
> +       }
> +
> +       pdata->enetaddr[0] = (macb >>  8) & 0xff;
> +       pdata->enetaddr[1] = (macb >>  0) & 0xff;
> +       pdata->enetaddr[2] = (maca >> 24) & 0xff;
> +       pdata->enetaddr[3] = (maca >> 16) & 0xff;
> +       pdata->enetaddr[4] = (maca >>  8) & 0xff;
> +       pdata->enetaddr[5] = (maca >>  0) & 0xff;
> +
> +       return 0;
> +}
> +
> +static int ks2_eth_start(struct udevice *dev)
> +{
> +       struct ks2_eth_priv *priv = dev_get_priv(dev);
> +
> +#ifdef CONFIG_SOC_K2G
> +       keystone_rgmii_config(priv->phydev);
> +#else
> +       keystone_sgmii_config(priv->phydev, priv->slave_port - 1,
> +                             priv->sgmii_link_type);
>  #endif
>
> -       /* wait till setup */
> -       udelay(5000);
> +       udelay(10000);
> +
> +       /* On chip switch configuration */
> +       ethss_config(target_get_switch_ctl(), SWITCH_MAX_PKT_SIZE);
> +
> +       qm_init();
> +
> +       if (ksnav_init(priv->netcp_pktdma, &priv->net_rx_buffs)) {
> +               error("ksnav_init failed\n");
> +               goto err_knav_init;
> +       }
> +
> +       /*
> +        * Streaming switch configuration. If not present this
> +        * statement is defined to void in target.h.
> +        * If present this is usually defined to a series of register writes
> +        */
> +       hw_config_streaming_switch();
> +
> +       if (priv->has_mdio) {
> +               phy_startup(priv->phydev);
> +               if (priv->phydev->link == 0) {
> +                       error("phy startup failed\n");
> +                       goto err_phy_start;
> +               }
> +       }
> +
> +       emac_gigabit_enable(dev);
> +
> +       ethss_start();
> +
> +       priv->emac_open = true;
> +
> +       return 0;
> +
> +err_phy_start:
> +       ksnav_close(priv->netcp_pktdma);
> +err_knav_init:
> +       qm_close();
> +
> +       return -EFAULT;
> +}
> +
> +static int ks2_eth_send(struct udevice *dev, void *packet, int length)
> +{
> +       struct ks2_eth_priv *priv = dev_get_priv(dev);
> +
> +       genphy_update_link(priv->phydev);
> +       if (priv->phydev->link == 0)
> +               return -1;
> +
> +       if (length < EMAC_MIN_ETHERNET_PKT_SIZE)
> +               length = EMAC_MIN_ETHERNET_PKT_SIZE;
> +
> +       return ksnav_send(priv->netcp_pktdma, (u32 *)packet,
> +                         length, (priv->slave_port) << 16);
> +}
> +
> +static int ks2_eth_recv(struct udevice *dev, int flags, uchar **packetp)
> +{
> +       struct ks2_eth_priv *priv = dev_get_priv(dev);
> +       int  pkt_size;
> +       u32 *pkt = NULL;
> +
> +       priv->hd = ksnav_recv(priv->netcp_pktdma, &pkt, &pkt_size);
> +       if (priv->hd == NULL)
> +               return -EAGAIN;
> +
> +       *packetp = (uchar *)pkt;
> +
> +       return pkt_size;
> +}
> +
> +static int ks2_eth_free_pkt(struct udevice *dev, uchar *packet,
> +                                  int length)
> +{
> +       struct ks2_eth_priv *priv = dev_get_priv(dev);
> +
> +       ksnav_release_rxhd(priv->netcp_pktdma, priv->hd);
> +
> +       return 0;
> +}
> +
> +static void ks2_eth_stop(struct udevice *dev)
> +{
> +       struct ks2_eth_priv *priv = dev_get_priv(dev);
> +
> +       if (!priv->emac_open)
> +               return;
> +       ethss_stop();
> +
> +       ksnav_close(priv->netcp_pktdma);
> +       qm_close();
> +       phy_shutdown(priv->phydev);
> +       priv->emac_open = false;
> +}
> +
> +static int ks2_eth_probe(struct udevice *dev)
> +{
> +       struct ks2_eth_priv *priv = dev_get_priv(dev);
> +       struct mii_dev *mdio_bus;
> +       int ret;
> +
> +       priv->dev = dev;
> +
> +       /* These clock enables has to be moved to common location */
> +       if (cpu_is_k2g())
> +               writel(KS2_ETHERNET_RGMII, KS2_ETHERNET_CFG);
> +
> +       /* By default, select PA PLL clock as PA clock source */
> +#ifndef CONFIG_SOC_K2G
> +       if (psc_enable_module(KS2_LPSC_PA))
> +               return -1;
> +#endif
> +       if (psc_enable_module(KS2_LPSC_CPGMAC))
> +               return -1;
> +       if (psc_enable_module(KS2_LPSC_CRYPTO))
> +               return -1;

Is there not a more appropriate error code that could be used here for these 3?

> +
> +       if (cpu_is_k2e() || cpu_is_k2l())
> +               pll_pa_clk_sel();
> +
> +
> +       priv->net_rx_buffs.buff_ptr = rx_buffs,
> +       priv->net_rx_buffs.num_buffs = RX_BUFF_NUMS,
> +       priv->net_rx_buffs.buff_len = RX_BUFF_LEN,
> +
> +       /* Register MDIO bus */
> +       mdio_bus = mdio_alloc();
> +       if (!mdio_bus) {
> +               error("MDIO alloc failed\n");
> +               return -ENOMEM;
> +       }
> +       priv->mdio_bus = mdio_bus;
> +       mdio_bus->read  = keystone2_mdio_read;
> +       mdio_bus->write = keystone2_mdio_write;
> +       mdio_bus->reset = keystone2_mdio_reset;
> +       mdio_bus->priv  = priv->mdio_base;
> +       sprintf(mdio_bus->name, "ethernet-mdio");
> +
> +       ret = mdio_register(mdio_bus);
> +       if (ret) {
> +               error("MDIO bus register failed\n");
> +               return ret;
> +       }
> +
> +#ifndef CONFIG_SOC_K2G
> +       keystone2_net_serdes_setup();
> +#endif
> +
> +       priv->netcp_pktdma = &netcp_pktdma;
> +
> +       priv->phydev = phy_connect(mdio_bus, priv->phy_addr, dev, priv->phy_if);
> +       phy_config(priv->phydev);
> +
> +       return 0;
>  }
> +
> +static const struct eth_ops ks2_eth_ops = {
> +       .start          = ks2_eth_start,
> +       .send           = ks2_eth_send,
> +       .recv           = ks2_eth_recv,
> +       .free_pkt       = ks2_eth_free_pkt,
> +       .stop           = ks2_eth_stop,

Do you not have a function to set the MAC address in the emac for
packet filtering (write_hwaddr)?

> +};
> +
> +
> +static int ks2_eth_ofdata_to_platdata(struct udevice *dev)
> +{
> +       struct ks2_eth_priv *priv = dev_get_priv(dev);
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
> +       const void *fdt = gd->fdt_blob;
> +       int interfaces;
> +       int interface_0;
> +       int netcp_gbe_0;
> +       int phy;
> +       int mdio;
> +       u32 dma_channel[6];
> +
> +       interfaces = fdt_subnode_offset(fdt, dev->of_offset,
> +                                       "netcp-interfaces");
> +       interface_0 = fdt_subnode_offset(fdt, interfaces, "interface-0");
> +
> +       netcp_gbe_0 = fdtdec_lookup_phandle(fdt, interface_0, "netcp-gbe");
> +       priv->link_type = fdtdec_get_int(fdt, netcp_gbe_0,
> +                                        "link-interface", -1);
> +       priv->slave_port = fdtdec_get_int(fdt, netcp_gbe_0, "slave-port", -1);
> +       /* U-Boot slave port number starts with 1 instead of 0 */
> +       priv->slave_port += 1;
> +
> +       phy = fdtdec_lookup_phandle(fdt, netcp_gbe_0, "phy-handle");
> +       priv->phy_addr = fdtdec_get_int(fdt, phy, "reg", -1);
> +
> +       mdio = fdt_parent_offset(fdt, phy);
> +       if (mdio < 0) {
> +               error("mdio dt not found\n");
> +               return -ENODEV;
> +       }
> +       priv->mdio_base = (void *)fdtdec_get_addr(fdt, mdio, "reg");
> +
> +       keystone2_eth_read_mac_addr(priv, pdata);

Would this not be more appropriate to just set a pointer to this
function to read_rom_hwaddr in the ops structure?

> +
> +       if (priv->link_type == LINK_TYPE_MAC_TO_PHY_MODE) {
> +               priv->phy_if = PHY_INTERFACE_MODE_SGMII;
> +               pdata->phy_interface = priv->phy_if;
> +               priv->sgmii_link_type = SGMII_LINK_MAC_PHY;
> +               priv->has_mdio = true;
> +       }
> +       pdata->iobase = dev_get_addr(dev);
> +
> +       fdtdec_get_int_array(fdt, dev->of_offset, "ti,navigator-dmas",
> +                            dma_channel, 6);
> +       priv->net_rx_buffs.rx_flow = dma_channel[1];
> +
> +       return 0;
> +}
> +
> +static const struct udevice_id ks2_eth_ids[] = {
> +       { .compatible = "ti,netcp-1.0" },
> +       { }
> +};
> +
> +U_BOOT_DRIVER(eth_ks2) = {
> +       .name   = "eth_ks2",
> +       .id     = UCLASS_ETH,
> +       .of_match = ks2_eth_ids,
> +       .ofdata_to_platdata = ks2_eth_ofdata_to_platdata,
> +       .probe  = ks2_eth_probe,
> +       .ops    = &ks2_eth_ops,
> +       .priv_auto_alloc_size = sizeof(struct ks2_eth_priv),
> +       .platdata_auto_alloc_size = sizeof(struct eth_pdata),
> +       .flags = DM_FLAG_ALLOC_PRIV_DMA,
> +};
>  #endif
> --
> 2.7.0.rc3
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot

  reply	other threads:[~2016-01-22 20:15 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-22  8:23 [U-Boot] [PATCH v2 0/8] driver model bring-up of keystone net on k2hk, k2l, k2e and k2g evm Mugunthan V N
2016-01-22  8:23 ` [U-Boot] [PATCH v2 1/8] board: ti: ks2_evm: remove board_eth_init when CONFIG_DM_ETH is defined Mugunthan V N
2016-01-22 19:53   ` Joe Hershberger
2016-01-22  8:23 ` [U-Boot] [PATCH v2 2/8] drivers: net: phy: micrel: fix build errors with CONFIG_DM_ETH Mugunthan V N
2016-01-22 19:54   ` Joe Hershberger
2016-01-22  8:23 ` [U-Boot] [PATCH v2 3/8] drivers: net: keystone_net: convert driver to adopt device driver model Mugunthan V N
2016-01-22 20:15   ` Joe Hershberger [this message]
2016-01-25  8:30     ` Mugunthan V N
2016-01-25  8:35   ` Bin Meng
2016-01-25 10:10     ` Mugunthan V N
2016-01-22  8:23 ` [U-Boot] [PATCH v2 4/8] defconfig: k2hk_evm_defconfig: enable net " Mugunthan V N
2016-01-22 20:15   ` Joe Hershberger
2016-01-22  8:23 ` [U-Boot] [PATCH v2 5/8] defconfig: k2l_evm_defconfig: " Mugunthan V N
2016-01-22 20:16   ` Joe Hershberger
2016-01-22  8:23 ` [U-Boot] [PATCH v2 6/8] defconfig: k2e_evm_defconfig: " Mugunthan V N
2016-01-22 20:16   ` Joe Hershberger
2016-01-22  8:23 ` [U-Boot] [PATCH v2 7/8] ARM: dts: k2g: Add keystone net dts files Mugunthan V N
2016-01-22 20:17   ` Joe Hershberger
2016-01-22  8:23 ` [U-Boot] [PATCH v2 8/8] defconfig: k2g_evm_defconfig: enable net driver model Mugunthan V N
2016-01-22 20:17   ` Joe Hershberger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CANr=Z=bvzwzFawtF_TAcbcjV2QiHSQCFdSmveg36UqC9DM-Ymg@mail.gmail.com' \
    --to=joe.hershberger@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.