linux/drivers/usb/typec/tcpm/tcpci_mt6360.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2020 MediaTek Inc.
   4 *
   5 * Author: ChiYuan Huang <cy_huang@richtek.com>
   6 */
   7
   8#include <linux/interrupt.h>
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/of.h>
  12#include <linux/platform_device.h>
  13#include <linux/regmap.h>
  14#include <linux/usb/tcpm.h>
  15
  16#include "tcpci.h"
  17
  18#define MT6360_REG_VCONNCTRL1   0x8C
  19#define MT6360_REG_MODECTRL2    0x8F
  20#define MT6360_REG_SWRESET      0xA0
  21#define MT6360_REG_DEBCTRL1     0xA1
  22#define MT6360_REG_DRPCTRL1     0xA2
  23#define MT6360_REG_DRPCTRL2     0xA3
  24#define MT6360_REG_I2CTORST     0xBF
  25#define MT6360_REG_RXCTRL2      0xCF
  26#define MT6360_REG_CTDCTRL2     0xEC
  27
  28/* MT6360_REG_VCONNCTRL1 */
  29#define MT6360_VCONNCL_ENABLE   BIT(0)
  30/* MT6360_REG_RXCTRL2 */
  31#define MT6360_OPEN40M_ENABLE   BIT(7)
  32/* MT6360_REG_CTDCTRL2 */
  33#define MT6360_RPONESHOT_ENABLE BIT(6)
  34
  35struct mt6360_tcpc_info {
  36        struct tcpci_data tdata;
  37        struct tcpci *tcpci;
  38        struct device *dev;
  39        int irq;
  40};
  41
  42static inline int mt6360_tcpc_read16(struct regmap *regmap,
  43                                     unsigned int reg, u16 *val)
  44{
  45        return regmap_raw_read(regmap, reg, val, sizeof(u16));
  46}
  47
  48static inline int mt6360_tcpc_write16(struct regmap *regmap,
  49                                      unsigned int reg, u16 val)
  50{
  51        return regmap_raw_write(regmap, reg, &val, sizeof(u16));
  52}
  53
  54static int mt6360_tcpc_init(struct tcpci *tcpci, struct tcpci_data *tdata)
  55{
  56        struct regmap *regmap = tdata->regmap;
  57        int ret;
  58
  59        ret = regmap_write(regmap, MT6360_REG_SWRESET, 0x01);
  60        if (ret)
  61                return ret;
  62
  63        /* after reset command, wait 1~2ms to wait IC action */
  64        usleep_range(1000, 2000);
  65
  66        /* write all alert to masked */
  67        ret = mt6360_tcpc_write16(regmap, TCPC_ALERT_MASK, 0);
  68        if (ret)
  69                return ret;
  70
  71        /* config I2C timeout reset enable , and timeout to 200ms */
  72        ret = regmap_write(regmap, MT6360_REG_I2CTORST, 0x8F);
  73        if (ret)
  74                return ret;
  75
  76        /* config CC Detect Debounce : 26.7*val us */
  77        ret = regmap_write(regmap, MT6360_REG_DEBCTRL1, 0x10);
  78        if (ret)
  79                return ret;
  80
  81        /* DRP Toggle Cycle : 51.2 + 6.4*val ms */
  82        ret = regmap_write(regmap, MT6360_REG_DRPCTRL1, 4);
  83        if (ret)
  84                return ret;
  85
  86        /* DRP Duyt Ctrl : dcSRC: /1024 */
  87        ret = mt6360_tcpc_write16(regmap, MT6360_REG_DRPCTRL2, 330);
  88        if (ret)
  89                return ret;
  90
  91        /* Enable VCONN Current Limit function */
  92        ret = regmap_update_bits(regmap, MT6360_REG_VCONNCTRL1, MT6360_VCONNCL_ENABLE,
  93                                 MT6360_VCONNCL_ENABLE);
  94        if (ret)
  95                return ret;
  96
  97        /* Enable cc open 40ms when pmic send vsysuv signal */
  98        ret = regmap_update_bits(regmap, MT6360_REG_RXCTRL2, MT6360_OPEN40M_ENABLE,
  99                                 MT6360_OPEN40M_ENABLE);
 100        if (ret)
 101                return ret;
 102
 103        /* Enable Rpdet oneshot detection */
 104        ret = regmap_update_bits(regmap, MT6360_REG_CTDCTRL2, MT6360_RPONESHOT_ENABLE,
 105                                 MT6360_RPONESHOT_ENABLE);
 106        if (ret)
 107                return ret;
 108
 109        /* Set shipping mode off, AUTOIDLE on */
 110        return regmap_write(regmap, MT6360_REG_MODECTRL2, 0x7A);
 111}
 112
 113static irqreturn_t mt6360_irq(int irq, void *dev_id)
 114{
 115        struct mt6360_tcpc_info *mti = dev_id;
 116
 117        return tcpci_irq(mti->tcpci);
 118}
 119
 120static int mt6360_tcpc_probe(struct platform_device *pdev)
 121{
 122        struct mt6360_tcpc_info *mti;
 123        int ret;
 124
 125        mti = devm_kzalloc(&pdev->dev, sizeof(*mti), GFP_KERNEL);
 126        if (!mti)
 127                return -ENOMEM;
 128
 129        mti->dev = &pdev->dev;
 130
 131        mti->tdata.regmap = dev_get_regmap(pdev->dev.parent, NULL);
 132        if (!mti->tdata.regmap) {
 133                dev_err(&pdev->dev, "Failed to get parent regmap\n");
 134                return -ENODEV;
 135        }
 136
 137        mti->irq = platform_get_irq_byname(pdev, "PD_IRQB");
 138        if (mti->irq < 0)
 139                return mti->irq;
 140
 141        mti->tdata.init = mt6360_tcpc_init;
 142        mti->tcpci = tcpci_register_port(&pdev->dev, &mti->tdata);
 143        if (IS_ERR(mti->tcpci)) {
 144                dev_err(&pdev->dev, "Failed to register tcpci port\n");
 145                return PTR_ERR(mti->tcpci);
 146        }
 147
 148        ret = devm_request_threaded_irq(mti->dev, mti->irq, NULL, mt6360_irq, IRQF_ONESHOT,
 149                                        dev_name(&pdev->dev), mti);
 150        if (ret) {
 151                dev_err(mti->dev, "Failed to register irq\n");
 152                tcpci_unregister_port(mti->tcpci);
 153                return ret;
 154        }
 155
 156        device_init_wakeup(&pdev->dev, true);
 157        platform_set_drvdata(pdev, mti);
 158
 159        return 0;
 160}
 161
 162static int mt6360_tcpc_remove(struct platform_device *pdev)
 163{
 164        struct mt6360_tcpc_info *mti = platform_get_drvdata(pdev);
 165
 166        disable_irq(mti->irq);
 167        tcpci_unregister_port(mti->tcpci);
 168        return 0;
 169}
 170
 171static int __maybe_unused mt6360_tcpc_suspend(struct device *dev)
 172{
 173        struct mt6360_tcpc_info *mti = dev_get_drvdata(dev);
 174
 175        if (device_may_wakeup(dev))
 176                enable_irq_wake(mti->irq);
 177
 178        return 0;
 179}
 180
 181static int __maybe_unused mt6360_tcpc_resume(struct device *dev)
 182{
 183        struct mt6360_tcpc_info *mti = dev_get_drvdata(dev);
 184
 185        if (device_may_wakeup(dev))
 186                disable_irq_wake(mti->irq);
 187
 188        return 0;
 189}
 190
 191static SIMPLE_DEV_PM_OPS(mt6360_tcpc_pm_ops, mt6360_tcpc_suspend, mt6360_tcpc_resume);
 192
 193static const struct of_device_id __maybe_unused mt6360_tcpc_of_id[] = {
 194        { .compatible = "mediatek,mt6360-tcpc", },
 195        {},
 196};
 197MODULE_DEVICE_TABLE(of, mt6360_tcpc_of_id);
 198
 199static struct platform_driver mt6360_tcpc_driver = {
 200        .driver = {
 201                .name = "mt6360-tcpc",
 202                .pm = &mt6360_tcpc_pm_ops,
 203                .of_match_table = mt6360_tcpc_of_id,
 204        },
 205        .probe = mt6360_tcpc_probe,
 206        .remove = mt6360_tcpc_remove,
 207};
 208module_platform_driver(mt6360_tcpc_driver);
 209
 210MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
 211MODULE_DESCRIPTION("MT6360 USB Type-C Port Controller Interface Driver");
 212MODULE_LICENSE("GPL v2");
 213