brintos

brintos / linux-shallow public Read only

0
0
Text · 8.3 KiB · fcd182d Raw
303 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2019, Linaro Limited3 4#include <linux/clk.h>5#include <linux/gpio/consumer.h>6#include <linux/interrupt.h>7#include <linux/kernel.h>8#include <linux/mfd/core.h>9#include <linux/mfd/wcd934x/registers.h>10#include <linux/mfd/wcd934x/wcd934x.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/of_irq.h>14#include <linux/platform_device.h>15#include <linux/regmap.h>16#include <linux/regulator/consumer.h>17#include <linux/slimbus.h>18 19#define WCD934X_REGMAP_IRQ_REG(_irq, _off, _mask)		\20	[_irq] = {						\21		.reg_offset = (_off),				\22		.mask = (_mask),				\23		.type = {					\24			.type_reg_offset = (_off),		\25			.types_supported = IRQ_TYPE_EDGE_BOTH,	\26			.type_reg_mask  = (_mask),		\27			.type_level_low_val = (_mask),		\28			.type_level_high_val = (_mask),		\29			.type_falling_val = 0,			\30			.type_rising_val = 0,			\31		},						\32	}33 34static const struct mfd_cell wcd934x_devices[] = {35	{36		.name = "wcd934x-codec",37	}, {38		.name = "wcd934x-gpio",39		.of_compatible = "qcom,wcd9340-gpio",40	}, {41		.name = "wcd934x-soundwire",42		.of_compatible = "qcom,soundwire-v1.3.0",43	},44};45 46static const struct regmap_irq wcd934x_irqs[] = {47	WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_SLIMBUS, 0, BIT(0)),48	WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_HPH_PA_OCPL_FAULT, 0, BIT(2)),49	WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_HPH_PA_OCPR_FAULT, 0, BIT(3)),50	WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_MBHC_SW_DET, 1, BIT(0)),51	WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, 1, BIT(1)),52	WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, 1, BIT(2)),53	WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, 1, BIT(3)),54	WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 1, BIT(4)),55	WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_SOUNDWIRE, 2, BIT(4)),56};57 58static const unsigned int wcd934x_config_regs[] = {59	WCD934X_INTR_LEVEL0,60};61 62static const struct regmap_irq_chip wcd934x_regmap_irq_chip = {63	.name = "wcd934x_irq",64	.status_base = WCD934X_INTR_PIN1_STATUS0,65	.mask_base = WCD934X_INTR_PIN1_MASK0,66	.ack_base = WCD934X_INTR_PIN1_CLEAR0,67	.num_regs = 4,68	.irqs = wcd934x_irqs,69	.num_irqs = ARRAY_SIZE(wcd934x_irqs),70	.config_base = wcd934x_config_regs,71	.num_config_bases = ARRAY_SIZE(wcd934x_config_regs),72	.num_config_regs = 4,73	.set_type_config = regmap_irq_set_type_config_simple,74};75 76static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)77{78	switch (reg) {79	case WCD934X_INTR_PIN1_STATUS0...WCD934X_INTR_PIN2_CLEAR3:80	case WCD934X_SWR_AHB_BRIDGE_RD_DATA_0:81	case WCD934X_SWR_AHB_BRIDGE_RD_DATA_1:82	case WCD934X_SWR_AHB_BRIDGE_RD_DATA_2:83	case WCD934X_SWR_AHB_BRIDGE_RD_DATA_3:84	case WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS:85	case WCD934X_ANA_MBHC_RESULT_3:86	case WCD934X_ANA_MBHC_RESULT_2:87	case WCD934X_ANA_MBHC_RESULT_1:88	case WCD934X_ANA_MBHC_MECH:89	case WCD934X_ANA_MBHC_ELECT:90	case WCD934X_ANA_MBHC_ZDET:91	case WCD934X_ANA_MICB2:92	case WCD934X_ANA_RCO:93	case WCD934X_ANA_BIAS:94		return true;95	default:96		return false;97	}98};99 100static const struct regmap_range_cfg wcd934x_ranges[] = {101	{	.name = "WCD934X",102		.range_min =  0x0,103		.range_max =  WCD934X_MAX_REGISTER,104		.selector_reg = WCD934X_SEL_REGISTER,105		.selector_mask = WCD934X_SEL_MASK,106		.selector_shift = WCD934X_SEL_SHIFT,107		.window_start = WCD934X_WINDOW_START,108		.window_len = WCD934X_WINDOW_LENGTH,109	},110};111 112static const struct regmap_config wcd934x_regmap_config = {113	.reg_bits = 16,114	.val_bits = 8,115	.cache_type = REGCACHE_MAPLE,116	.max_register = 0xffff,117	.can_multi_write = true,118	.ranges = wcd934x_ranges,119	.num_ranges = ARRAY_SIZE(wcd934x_ranges),120	.volatile_reg = wcd934x_is_volatile_register,121};122 123static int wcd934x_bring_up(struct wcd934x_ddata *ddata)124{125	struct regmap *regmap = ddata->regmap;126	u16 id_minor, id_major;127	int ret;128 129	ret = regmap_bulk_read(regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0,130			       (u8 *)&id_minor, sizeof(u16));131	if (ret)132		return ret;133 134	ret = regmap_bulk_read(regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2,135			       (u8 *)&id_major, sizeof(u16));136	if (ret)137		return ret;138 139	dev_info(ddata->dev, "WCD934x chip id major 0x%x, minor 0x%x\n",140		 id_major, id_minor);141 142	regmap_write(regmap, WCD934X_CODEC_RPM_RST_CTL, 0x01);143	regmap_write(regmap, WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x19);144	regmap_write(regmap, WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x15);145	/* Add 1msec delay for VOUT to settle */146	usleep_range(1000, 1100);147	regmap_write(regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);148	regmap_write(regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);149	regmap_write(regmap, WCD934X_CODEC_RPM_RST_CTL, 0x3);150	regmap_write(regmap, WCD934X_CODEC_RPM_RST_CTL, 0x7);151	regmap_write(regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);152 153	return 0;154}155 156static int wcd934x_slim_status_up(struct slim_device *sdev)157{158	struct device *dev = &sdev->dev;159	struct wcd934x_ddata *ddata;160	int ret;161 162	ddata = dev_get_drvdata(dev);163 164	ddata->regmap = regmap_init_slimbus(sdev, &wcd934x_regmap_config);165	if (IS_ERR(ddata->regmap)) {166		dev_err(dev, "Error allocating slim regmap\n");167		return PTR_ERR(ddata->regmap);168	}169 170	ret = wcd934x_bring_up(ddata);171	if (ret) {172		dev_err(dev, "Failed to bring up WCD934X: err = %d\n", ret);173		return ret;174	}175 176	ret = devm_regmap_add_irq_chip(dev, ddata->regmap, ddata->irq,177				       IRQF_TRIGGER_HIGH, 0,178				       &wcd934x_regmap_irq_chip,179				       &ddata->irq_data);180	if (ret) {181		dev_err(dev, "Failed to add IRQ chip: err = %d\n", ret);182		return ret;183	}184 185	ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, wcd934x_devices,186			      ARRAY_SIZE(wcd934x_devices), NULL, 0, NULL);187	if (ret) {188		dev_err(dev, "Failed to add child devices: err = %d\n",189			ret);190		return ret;191	}192 193	return ret;194}195 196static int wcd934x_slim_status(struct slim_device *sdev,197			       enum slim_device_status status)198{199	switch (status) {200	case SLIM_DEVICE_STATUS_UP:201		return wcd934x_slim_status_up(sdev);202	case SLIM_DEVICE_STATUS_DOWN:203		mfd_remove_devices(&sdev->dev);204		break;205	default:206		return -EINVAL;207	}208 209	return 0;210}211 212static int wcd934x_slim_probe(struct slim_device *sdev)213{214	struct device *dev = &sdev->dev;215	struct device_node *np = dev->of_node;216	struct wcd934x_ddata *ddata;217	struct gpio_desc *reset_gpio;218	int ret;219 220	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);221	if (!ddata)222		return	-ENOMEM;223 224	ddata->irq = of_irq_get(np, 0);225	if (ddata->irq < 0)226		return dev_err_probe(ddata->dev, ddata->irq,227				     "Failed to get IRQ\n");228 229	ddata->extclk = devm_clk_get(dev, "extclk");230	if (IS_ERR(ddata->extclk))231		return dev_err_probe(dev, PTR_ERR(ddata->extclk),232				     "Failed to get extclk");233 234	ddata->supplies[0].supply = "vdd-buck";235	ddata->supplies[1].supply = "vdd-buck-sido";236	ddata->supplies[2].supply = "vdd-tx";237	ddata->supplies[3].supply = "vdd-rx";238	ddata->supplies[4].supply = "vdd-io";239 240	ret = regulator_bulk_get(dev, WCD934X_MAX_SUPPLY, ddata->supplies);241	if (ret)242		return dev_err_probe(dev, ret, "Failed to get supplies\n");243 244	ret = regulator_bulk_enable(WCD934X_MAX_SUPPLY, ddata->supplies);245	if (ret)246		return dev_err_probe(dev, ret, "Failed to enable supplies\n");247 248	/*249	 * For WCD934X, it takes about 600us for the Vout_A and250	 * Vout_D to be ready after BUCK_SIDO is powered up.251	 * SYS_RST_N shouldn't be pulled high during this time252	 */253	usleep_range(600, 650);254	reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);255	if (IS_ERR(reset_gpio)) {256		ret = dev_err_probe(dev, PTR_ERR(reset_gpio),257				    "Failed to get reset gpio\n");258		goto err_disable_regulators;259	}260	msleep(20);261	gpiod_set_value(reset_gpio, 1);262	msleep(20);263 264	ddata->dev = dev;265	dev_set_drvdata(dev, ddata);266 267	return 0;268 269err_disable_regulators:270	regulator_bulk_disable(WCD934X_MAX_SUPPLY, ddata->supplies);271	return ret;272}273 274static void wcd934x_slim_remove(struct slim_device *sdev)275{276	struct wcd934x_ddata *ddata = dev_get_drvdata(&sdev->dev);277 278	regulator_bulk_disable(WCD934X_MAX_SUPPLY, ddata->supplies);279	mfd_remove_devices(&sdev->dev);280}281 282static const struct slim_device_id wcd934x_slim_id[] = {283	{ SLIM_MANF_ID_QCOM, SLIM_PROD_CODE_WCD9340,284	  SLIM_DEV_IDX_WCD9340, SLIM_DEV_INSTANCE_ID_WCD9340 },285	{}286};287 288static struct slim_driver wcd934x_slim_driver = {289	.driver = {290		.name = "wcd934x-slim",291	},292	.probe = wcd934x_slim_probe,293	.remove = wcd934x_slim_remove,294	.device_status = wcd934x_slim_status,295	.id_table = wcd934x_slim_id,296};297 298module_slim_driver(wcd934x_slim_driver);299MODULE_DESCRIPTION("WCD934X slim driver");300MODULE_LICENSE("GPL v2");301MODULE_ALIAS("slim:217:250:*");302MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");303