| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include <linux/device.h>
- #include <linux/devm-helpers.h>
- #include <linux/delay.h>
- #include <linux/err.h>
- #include <linux/extcon-provider.h>
- #include <linux/i2c.h>
- #include <linux/mod_devicetable.h>
- #include <linux/interrupt.h>
- #include <linux/module.h>
- #include <linux/pm.h>
- #include <linux/regmap.h>
- /* I2C addresses of MUIC internal registers */
- #define MAX14526_DEVICE_ID 0x00
- #define MAX14526_ID 0x02
- /* CONTROL_1 register masks */
- #define MAX14526_CONTROL_1 0x01
- #define ID_2P2 BIT(6)
- #define ID_620 BIT(5)
- #define ID_200 BIT(4)
- #define VLDO BIT(3)
- #define SEMREN BIT(2)
- #define ADC_EN BIT(1)
- #define CP_EN BIT(0)
- /* CONTROL_2 register masks */
- #define MAX14526_CONTROL_2 0x02
- #define INTPOL BIT(7)
- #define INT_EN BIT(6)
- #define MIC_LP BIT(5)
- #define CP_AUD BIT(4)
- #define CHG_TYPE BIT(1)
- #define USB_DET_DIS BIT(0)
- /* SW_CONTROL register masks */
- #define MAX14526_SW_CONTROL 0x03
- #define SW_DATA 0x00
- #define SW_UART 0x01
- #define SW_AUDIO 0x02
- #define SW_OPEN 0x07
- /* INT_STATUS register masks */
- #define MAX14526_INT_STAT 0x04
- #define CHGDET BIT(7)
- #define MR_COMP BIT(6)
- #define SENDEND BIT(5)
- #define V_VBUS BIT(4)
- /* STATUS register masks */
- #define MAX14526_STATUS 0x05
- #define CPORT BIT(7)
- #define CHPORT BIT(6)
- #define C1COMP BIT(0)
- enum max14526_idno_resistance {
- MAX14526_GND,
- MAX14526_24KOHM,
- MAX14526_56KOHM,
- MAX14526_100KOHM,
- MAX14526_130KOHM,
- MAX14526_180KOHM,
- MAX14526_240KOHM,
- MAX14526_330KOHM,
- MAX14526_430KOHM,
- MAX14526_620KOHM,
- MAX14526_910KOHM,
- MAX14526_OPEN
- };
- enum max14526_field_idx {
- VENDOR_ID, CHIP_REV, /* DEVID */
- DM, DP, /* SW_CONTROL */
- MAX14526_N_REGMAP_FIELDS
- };
- static const struct reg_field max14526_reg_field[MAX14526_N_REGMAP_FIELDS] = {
- [VENDOR_ID] = REG_FIELD(MAX14526_DEVICE_ID, 4, 7),
- [CHIP_REV] = REG_FIELD(MAX14526_DEVICE_ID, 0, 3),
- [DM] = REG_FIELD(MAX14526_SW_CONTROL, 0, 2),
- [DP] = REG_FIELD(MAX14526_SW_CONTROL, 3, 5),
- };
- struct max14526_data {
- struct i2c_client *client;
- struct extcon_dev *edev;
- struct regmap *regmap;
- struct regmap_field *rfield[MAX14526_N_REGMAP_FIELDS];
- int last_state;
- int cable;
- };
- enum max14526_muic_modes {
- MAX14526_OTG = MAX14526_GND, /* no power */
- MAX14526_MHL = MAX14526_56KOHM, /* no power */
- MAX14526_OTG_Y = MAX14526_GND | V_VBUS,
- MAX14526_MHL_CHG = MAX14526_GND | V_VBUS | CHGDET,
- MAX14526_NONE = MAX14526_OPEN,
- MAX14526_USB = MAX14526_OPEN | V_VBUS,
- MAX14526_CHG = MAX14526_OPEN | V_VBUS | CHGDET,
- };
- static const unsigned int max14526_extcon_cable[] = {
- EXTCON_USB,
- EXTCON_USB_HOST,
- EXTCON_CHG_USB_FAST,
- EXTCON_DISP_MHL,
- EXTCON_NONE,
- };
- static int max14526_ap_usb_mode(struct max14526_data *priv)
- {
- struct device *dev = &priv->client->dev;
- int ret;
- /* Enable USB Path */
- ret = regmap_field_write(priv->rfield[DM], SW_DATA);
- if (ret)
- return ret;
- ret = regmap_field_write(priv->rfield[DP], SW_DATA);
- if (ret)
- return ret;
- /* Enable 200K, Charger Pump and ADC */
- ret = regmap_write(priv->regmap, MAX14526_CONTROL_1,
- ID_200 | ADC_EN | CP_EN);
- if (ret)
- return ret;
- dev_dbg(dev, "AP USB mode set\n");
- return 0;
- }
- static irqreturn_t max14526_interrupt(int irq, void *dev_id)
- {
- struct max14526_data *priv = dev_id;
- struct device *dev = &priv->client->dev;
- int state, ret;
- /*
- * Upon an MUIC IRQ (MUIC_INT_N falls), wait at least 70ms
- * before reading INT_STAT and STATUS. After the reads,
- * MUIC_INT_N returns to high (but the INT_STAT and STATUS
- * contents will be held).
- */
- msleep(100);
- ret = regmap_read(priv->regmap, MAX14526_INT_STAT, &state);
- if (ret)
- dev_err(dev, "failed to read MUIC state %d\n", ret);
- if (state == priv->last_state)
- return IRQ_HANDLED;
- /* Detach previous device */
- extcon_set_state_sync(priv->edev, priv->cable, false);
- switch (state) {
- case MAX14526_USB:
- priv->cable = EXTCON_USB;
- break;
- case MAX14526_CHG:
- priv->cable = EXTCON_CHG_USB_FAST;
- break;
- case MAX14526_OTG:
- case MAX14526_OTG_Y:
- priv->cable = EXTCON_USB_HOST;
- break;
- case MAX14526_MHL:
- case MAX14526_MHL_CHG:
- priv->cable = EXTCON_DISP_MHL;
- break;
- case MAX14526_NONE:
- default:
- priv->cable = EXTCON_NONE;
- break;
- }
- extcon_set_state_sync(priv->edev, priv->cable, true);
- priv->last_state = state;
- return IRQ_HANDLED;
- }
- static const struct regmap_config max14526_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = MAX14526_STATUS,
- };
- static int max14526_probe(struct i2c_client *client)
- {
- struct device *dev = &client->dev;
- struct max14526_data *priv;
- int ret, dev_id, rev, i;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->client = client;
- i2c_set_clientdata(client, priv);
- priv->regmap = devm_regmap_init_i2c(client, &max14526_regmap_config);
- if (IS_ERR(priv->regmap))
- return dev_err_probe(dev, PTR_ERR(priv->regmap), "cannot allocate regmap\n");
- for (i = 0; i < MAX14526_N_REGMAP_FIELDS; i++) {
- priv->rfield[i] = devm_regmap_field_alloc(dev, priv->regmap,
- max14526_reg_field[i]);
- if (IS_ERR(priv->rfield[i]))
- return dev_err_probe(dev, PTR_ERR(priv->rfield[i]),
- "cannot allocate regmap field\n");
- }
- /* Detect if MUIC version is supported */
- ret = regmap_field_read(priv->rfield[VENDOR_ID], &dev_id);
- if (ret)
- return dev_err_probe(dev, ret, "failed to read MUIC ID\n");
- regmap_field_read(priv->rfield[CHIP_REV], &rev);
- if (dev_id == MAX14526_ID)
- dev_info(dev, "detected MAX14526 MUIC with id 0x%x, rev 0x%x\n", dev_id, rev);
- else
- dev_err_probe(dev, -EINVAL, "MUIC vendor id 0x%X is not recognized\n", dev_id);
- priv->edev = devm_extcon_dev_allocate(dev, max14526_extcon_cable);
- if (IS_ERR(priv->edev))
- return dev_err_probe(dev, (IS_ERR(priv->edev)),
- "failed to allocate extcon device\n");
- ret = devm_extcon_dev_register(dev, priv->edev);
- if (ret < 0)
- return dev_err_probe(dev, ret, "failed to register extcon device\n");
- ret = max14526_ap_usb_mode(priv);
- if (ret < 0)
- return dev_err_probe(dev, ret, "failed to set AP USB mode\n");
- regmap_write_bits(priv->regmap, MAX14526_CONTROL_2, INT_EN, INT_EN);
- regmap_write_bits(priv->regmap, MAX14526_CONTROL_2, USB_DET_DIS, (u32)~USB_DET_DIS);
- ret = devm_request_threaded_irq(dev, client->irq, NULL, &max14526_interrupt,
- IRQF_ONESHOT | IRQF_SHARED, client->name, priv);
- if (ret)
- return dev_err_probe(dev, ret, "failed to register IRQ\n");
- irq_wake_thread(client->irq, priv);
- return 0;
- }
- static int max14526_resume(struct device *dev)
- {
- struct i2c_client *client = to_i2c_client(dev);
- struct max14526_data *priv = i2c_get_clientdata(client);
- irq_wake_thread(client->irq, priv);
- return 0;
- }
- static DEFINE_SIMPLE_DEV_PM_OPS(max14526_pm_ops, NULL, max14526_resume);
- static const struct of_device_id max14526_match[] = {
- { .compatible = "maxim,max14526" },
- { /* sentinel */ }
- };
- MODULE_DEVICE_TABLE(of, max14526_match);
- static const struct i2c_device_id max14526_id[] = {
- { "max14526" },
- { }
- };
- MODULE_DEVICE_TABLE(i2c, max14526_id);
- static struct i2c_driver max14526_driver = {
- .driver = {
- .name = "max14526",
- .of_match_table = max14526_match,
- .pm = &max14526_pm_ops,
- },
- .probe = max14526_probe,
- .id_table = max14526_id,
- };
- module_i2c_driver(max14526_driver);
- MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
- MODULE_DESCRIPTION("MAX14526 extcon driver to support MUIC");
- MODULE_LICENSE("GPL");
|